]> sigrok.org Git - libsigrok.git/blob - session.c
Add struct sr_session parameter to all session source backends.
[libsigrok.git] / session.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <glib.h>
25 #include "libsigrok.h"
26 #include "libsigrok-internal.h"
27
28 /** @cond PRIVATE */
29 #define LOG_PREFIX "session"
30 /** @endcond */
31
32 /**
33  * @file
34  *
35  * Creating, using, or destroying libsigrok sessions.
36  */
37
38 /**
39  * @defgroup grp_session Session handling
40  *
41  * Creating, using, or destroying libsigrok sessions.
42  *
43  * @{
44  */
45
46 struct source {
47         int timeout;
48         sr_receive_data_callback cb;
49         void *cb_data;
50
51         /* This is used to keep track of the object (fd, pollfd or channel) which is
52          * being polled and will be used to match the source when removing it again.
53          */
54         gintptr poll_object;
55 };
56
57 struct datafeed_callback {
58         sr_datafeed_callback cb;
59         void *cb_data;
60 };
61
62 /* There can currently only be one session at a time. */
63 /* 'sr_current_session' is not static, it's used elsewhere (via 'extern'). */
64 struct sr_session *sr_current_session;
65
66 /**
67  * Create a new session.
68  * Currently, there can be only one session at a time within the same process.
69  *
70  * @retval SR_OK Success.
71  * @retval SR_ERR_BUG A session exists already.
72  *
73  * @since 0.4.0
74  */
75 SR_API int sr_session_new(struct sr_session **session)
76 {
77         if (sr_current_session) {
78                 sr_err("%s: session already exists", __func__);
79                 return SR_ERR_BUG;
80         }
81
82         sr_current_session = g_malloc0(sizeof(struct sr_session));
83
84         sr_current_session->source_timeout = -1;
85         sr_current_session->running = FALSE;
86         sr_current_session->abort_session = FALSE;
87         g_mutex_init(&sr_current_session->stop_mutex);
88
89         *session = sr_current_session;
90
91         return SR_OK;
92 }
93
94 /**
95  * Destroy a session.
96  * This frees up all memory used by the session.
97  *
98  * @retval SR_OK Success.
99  * @retval SR_ERR_ARG Invalid session passed.
100  *
101  * @since 0.4.0
102  */
103 SR_API int sr_session_destroy(struct sr_session *session)
104 {
105         if (!session) {
106                 sr_err("%s: session was NULL", __func__);
107                 return SR_ERR_ARG;
108         }
109
110         sr_session_dev_remove_all(session);
111         g_mutex_clear(&session->stop_mutex);
112         if (session->trigger)
113                 sr_trigger_free(session->trigger);
114
115         g_free(session);
116
117         if (session == sr_current_session)
118                 sr_current_session = NULL;
119
120         return SR_OK;
121 }
122
123 /**
124  * Remove all the devices from a session.
125  *
126  * The session itself (i.e., the struct sr_session) is not free'd and still
127  * exists after this function returns.
128  *
129  * @retval SR_OK Success.
130  * @retval SR_ERR_BUG Invalid session passed.
131  *
132  * @since 0.4.0
133  */
134 SR_API int sr_session_dev_remove_all(struct sr_session *session)
135 {
136         struct sr_dev_inst *sdi;
137         GSList *l;
138
139         if (!session) {
140                 sr_err("%s: session was NULL", __func__);
141                 return SR_ERR_ARG;
142         }
143
144         for (l = session->devs; l; l = l->next) {
145                 sdi = (struct sr_dev_inst *) l->data;
146                 sdi->session = NULL;
147         }
148
149         g_slist_free(session->devs);
150         session->devs = NULL;
151
152         return SR_OK;
153 }
154
155 /**
156  * Add a device instance to a session.
157  *
158  * @param sdi The device instance to add to a session. Must not
159  *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
160  *            not be NULL.
161  *
162  * @retval SR_OK Success.
163  * @retval SR_ERR_ARG Invalid argument.
164  *
165  * @since 0.4.0
166  */
167 SR_API int sr_session_dev_add(struct sr_session *session,
168                 struct sr_dev_inst *sdi)
169 {
170         int ret;
171
172         if (!sdi) {
173                 sr_err("%s: sdi was NULL", __func__);
174                 return SR_ERR_ARG;
175         }
176
177         if (!session) {
178                 sr_err("%s: session was NULL", __func__);
179                 return SR_ERR_ARG;
180         }
181
182         /* If sdi->session is not NULL, the device is already in this or
183          * another session. */
184         if (sdi->session) {
185                 sr_err("%s: already assigned to session", __func__);
186                 return SR_ERR_ARG;
187         }
188
189         /* If sdi->driver is NULL, this is a virtual device. */
190         if (!sdi->driver) {
191                 sr_dbg("%s: sdi->driver was NULL, this seems to be "
192                        "a virtual device; continuing", __func__);
193                 /* Just add the device, don't run dev_open(). */
194                 session->devs = g_slist_append(session->devs, (gpointer)sdi);
195                 sdi->session = session;
196                 return SR_OK;
197         }
198
199         /* sdi->driver is non-NULL (i.e. we have a real device). */
200         if (!sdi->driver->dev_open) {
201                 sr_err("%s: sdi->driver->dev_open was NULL", __func__);
202                 return SR_ERR_BUG;
203         }
204
205         session->devs = g_slist_append(session->devs, (gpointer)sdi);
206         sdi->session = session;
207
208         if (session->running) {
209                 /* Adding a device to a running session. Commit settings
210                  * and start acquisition on that device now. */
211                 if ((ret = sr_config_commit(sdi)) != SR_OK) {
212                         sr_err("Failed to commit device settings before "
213                                "starting acquisition in running session (%s)",
214                                sr_strerror(ret));
215                         return ret;
216                 }
217                 if ((ret = sdi->driver->dev_acquisition_start(sdi,
218                                                 (void *)sdi)) != SR_OK) {
219                         sr_err("Failed to start acquisition of device in "
220                                "running session (%s)", sr_strerror(ret));
221                         return ret;
222                 }
223         }
224
225         return SR_OK;
226 }
227
228 /**
229  * List all device instances attached to a session.
230  *
231  * @param devlist A pointer where the device instance list will be
232  *                stored on return. If no devices are in the session,
233  *                this will be NULL. Each element in the list points
234  *                to a struct sr_dev_inst *.
235  *                The list must be freed by the caller, but not the
236  *                elements pointed to.
237  *
238  * @retval SR_OK Success.
239  * @retval SR_ERR_ARG Invalid argument.
240  *
241  * @since 0.4.0
242  */
243 SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist)
244 {
245         if (!session)
246                 return SR_ERR_ARG;
247
248         if (!devlist)
249                 return SR_ERR_ARG;
250
251         *devlist = g_slist_copy(session->devs);
252
253         return SR_OK;
254 }
255
256 /**
257  * Remove all datafeed callbacks in a session.
258  *
259  * @retval SR_OK Success.
260  * @retval SR_ERR_ARG Invalid session passed.
261  *
262  * @since 0.4.0
263  */
264 SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session)
265 {
266         if (!session) {
267                 sr_err("%s: session was NULL", __func__);
268                 return SR_ERR_ARG;
269         }
270
271         g_slist_free_full(session->datafeed_callbacks, g_free);
272         session->datafeed_callbacks = NULL;
273
274         return SR_OK;
275 }
276
277 /**
278  * Add a datafeed callback to a session.
279  *
280  * @param cb Function to call when a chunk of data is received.
281  *           Must not be NULL.
282  * @param cb_data Opaque pointer passed in by the caller.
283  *
284  * @retval SR_OK Success.
285  * @retval SR_ERR_BUG No session exists.
286  *
287  * @since 0.3.0
288  */
289 SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
290                 sr_datafeed_callback cb, void *cb_data)
291 {
292         struct datafeed_callback *cb_struct;
293
294         if (!session) {
295                 sr_err("%s: session was NULL", __func__);
296                 return SR_ERR_BUG;
297         }
298
299         if (!cb) {
300                 sr_err("%s: cb was NULL", __func__);
301                 return SR_ERR_ARG;
302         }
303
304         if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
305                 return SR_ERR_MALLOC;
306
307         cb_struct->cb = cb;
308         cb_struct->cb_data = cb_data;
309
310         session->datafeed_callbacks =
311             g_slist_append(session->datafeed_callbacks, cb_struct);
312
313         return SR_OK;
314 }
315
316 SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session)
317 {
318         return session->trigger;
319 }
320
321 SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig)
322 {
323         session->trigger = trig;
324
325         return SR_OK;
326 }
327
328 /**
329  * Call every device in the current session's callback.
330  *
331  * For sessions not driven by select loops such as sr_session_run(),
332  * but driven by another scheduler, this can be used to poll the devices
333  * from within that scheduler.
334  *
335  * @param block If TRUE, this call will wait for any of the session's
336  *              sources to fire an event on the file descriptors, or
337  *              any of their timeouts to activate. In other words, this
338  *              can be used as a select loop.
339  *              If FALSE, all sources have their callback run, regardless
340  *              of file descriptor or timeout status.
341  *
342  * @retval SR_OK Success.
343  * @retval SR_ERR Error occured.
344  */
345 static int sr_session_iteration(gboolean block)
346 {
347         struct sr_session *session = sr_current_session;
348         unsigned int i;
349         int ret;
350
351         ret = g_poll(session->pollfds, session->num_sources,
352                         block ? session->source_timeout : 0);
353         for (i = 0; i < session->num_sources; i++) {
354                 if (session->pollfds[i].revents > 0 || (ret == 0
355                         && session->source_timeout == session->sources[i].timeout)) {
356                         /*
357                          * Invoke the source's callback on an event,
358                          * or if the poll timed out and this source
359                          * asked for that timeout.
360                          */
361                         if (!session->sources[i].cb(session->pollfds[i].fd,
362                                         session->pollfds[i].revents,
363                                         session->sources[i].cb_data))
364                                 sr_session_source_remove(session,
365                                                 session->sources[i].poll_object);
366                 }
367                 /*
368                  * We want to take as little time as possible to stop
369                  * the session if we have been told to do so. Therefore,
370                  * we check the flag after processing every source, not
371                  * just once per main event loop.
372                  */
373                 g_mutex_lock(&session->stop_mutex);
374                 if (session->abort_session) {
375                         sr_session_stop_sync(session);
376                         /* But once is enough. */
377                         session->abort_session = FALSE;
378                 }
379                 g_mutex_unlock(&session->stop_mutex);
380         }
381
382         return SR_OK;
383 }
384
385
386 static int verify_trigger(struct sr_trigger *trigger)
387 {
388         struct sr_trigger_stage *stage;
389         struct sr_trigger_match *match;
390         GSList *l, *m;
391
392         if (!trigger->stages) {
393                 sr_err("No trigger stages defined.");
394                 return SR_ERR;
395         }
396
397         sr_spew("Checking trigger:");
398         for (l = trigger->stages; l; l = l->next) {
399                 stage = l->data;
400                 if (!stage->matches) {
401                         sr_err("Stage %d has no matches defined.", stage->stage);
402                         return SR_ERR;
403                 }
404                 for (m = stage->matches; m; m = m->next) {
405                         match = m->data;
406                         if (!match->channel) {
407                                 sr_err("Stage %d match has no channel.", stage->stage);
408                                 return SR_ERR;
409                         }
410                         if (!match->match) {
411                                 sr_err("Stage %d match is not defined.", stage->stage);
412                                 return SR_ERR;
413                         }
414                         sr_spew("Stage %d match on channel %s, match %d", stage->stage,
415                                         match->channel->name, match->match);
416                 }
417         }
418
419         return SR_OK;
420 }
421 /**
422  * Start a session.
423  *
424  * @retval SR_OK Success.
425  * @retval SR_ERR_ARG Invalid session passed.
426  *
427  * @since 0.4.0
428  */
429 SR_API int sr_session_start(struct sr_session *session)
430 {
431         struct sr_dev_inst *sdi;
432         GSList *l;
433         int ret;
434
435         if (!session) {
436                 sr_err("%s: session was NULL", __func__);
437                 return SR_ERR_ARG;
438         }
439
440         if (!session->devs) {
441                 sr_err("%s: session->devs was NULL; a session "
442                        "cannot be started without devices.", __func__);
443                 return SR_ERR_ARG;
444         }
445
446         if (session->trigger && verify_trigger(session->trigger) != SR_OK)
447                 return SR_ERR;
448
449         sr_info("Starting.");
450
451         ret = SR_OK;
452         for (l = session->devs; l; l = l->next) {
453                 sdi = l->data;
454                 if ((ret = sr_config_commit(sdi)) != SR_OK) {
455                         sr_err("Failed to commit device settings before "
456                                "starting acquisition (%s)", sr_strerror(ret));
457                         break;
458                 }
459                 if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
460                         sr_err("%s: could not start an acquisition "
461                                "(%s)", __func__, sr_strerror(ret));
462                         break;
463                 }
464         }
465
466         /* TODO: What if there are multiple devices? Which return code? */
467
468         return ret;
469 }
470
471 /**
472  * Run a session.
473  *
474  * @retval SR_OK Success.
475  * @retval SR_ERR_ARG Invalid session passed.
476  *
477  * @since 0.4.0
478  */
479 SR_API int sr_session_run(struct sr_session *session)
480 {
481         if (!session) {
482                 sr_err("%s: session was NULL", __func__);
483                 return SR_ERR_ARG;
484         }
485
486         if (!session->devs) {
487                 /* TODO: Actually the case? */
488                 sr_err("%s: session->devs was NULL; a session "
489                        "cannot be run without devices.", __func__);
490                 return SR_ERR_ARG;
491         }
492         session->running = TRUE;
493
494         sr_info("Running.");
495
496         /* Do we have real sources? */
497         if (session->num_sources == 1 && session->pollfds[0].fd == -1) {
498                 /* Dummy source, freewheel over it. */
499                 while (session->num_sources)
500                         session->sources[0].cb(-1, 0, session->sources[0].cb_data);
501         } else {
502                 /* Real sources, use g_poll() main loop. */
503                 while (session->num_sources)
504                         sr_session_iteration(TRUE);
505         }
506
507         return SR_OK;
508 }
509
510 /**
511  * Stop a session.
512  *
513  * The session is stopped immediately, with all acquisition sessions stopped
514  * and hardware drivers cleaned up.
515  *
516  * This must be called from within the session thread, to prevent freeing
517  * resources that the session thread will try to use.
518  *
519  * @retval SR_OK Success.
520  * @retval SR_ERR_ARG Invalid session passed.
521  *
522  * @private
523  */
524 SR_PRIV int sr_session_stop_sync(struct sr_session *session)
525 {
526         struct sr_dev_inst *sdi;
527         GSList *l;
528
529         if (!session) {
530                 sr_err("%s: session was NULL", __func__);
531                 return SR_ERR_ARG;
532         }
533
534         sr_info("Stopping.");
535
536         for (l = session->devs; l; l = l->next) {
537                 sdi = l->data;
538                 if (sdi->driver) {
539                         if (sdi->driver->dev_acquisition_stop)
540                                 sdi->driver->dev_acquisition_stop(sdi, sdi);
541                 }
542         }
543         session->running = FALSE;
544
545         return SR_OK;
546 }
547
548 /**
549  * Stop a session.
550  *
551  * The session is stopped immediately, with all acquisition sessions being
552  * stopped and hardware drivers cleaned up.
553  *
554  * If the session is run in a separate thread, this function will not block
555  * until the session is finished executing. It is the caller's responsibility
556  * to wait for the session thread to return before assuming that the session is
557  * completely decommissioned.
558  *
559  * @retval SR_OK Success.
560  * @retval SR_ERR_ARG Invalid session passed.
561  *
562  * @since 0.4.0
563  */
564 SR_API int sr_session_stop(struct sr_session *session)
565 {
566         if (!session) {
567                 sr_err("%s: session was NULL", __func__);
568                 return SR_ERR_BUG;
569         }
570
571         g_mutex_lock(&session->stop_mutex);
572         session->abort_session = TRUE;
573         g_mutex_unlock(&session->stop_mutex);
574
575         return SR_OK;
576 }
577
578 /**
579  * Debug helper.
580  *
581  * @param packet The packet to show debugging information for.
582  */
583 static void datafeed_dump(const struct sr_datafeed_packet *packet)
584 {
585         const struct sr_datafeed_logic *logic;
586         const struct sr_datafeed_analog *analog;
587
588         switch (packet->type) {
589         case SR_DF_HEADER:
590                 sr_dbg("bus: Received SR_DF_HEADER packet.");
591                 break;
592         case SR_DF_TRIGGER:
593                 sr_dbg("bus: Received SR_DF_TRIGGER packet.");
594                 break;
595         case SR_DF_META:
596                 sr_dbg("bus: Received SR_DF_META packet.");
597                 break;
598         case SR_DF_LOGIC:
599                 logic = packet->payload;
600                 sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
601                        "unitsize = %d).", logic->length, logic->unitsize);
602                 break;
603         case SR_DF_ANALOG:
604                 analog = packet->payload;
605                 sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
606                        analog->num_samples);
607                 break;
608         case SR_DF_END:
609                 sr_dbg("bus: Received SR_DF_END packet.");
610                 break;
611         case SR_DF_FRAME_BEGIN:
612                 sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
613                 break;
614         case SR_DF_FRAME_END:
615                 sr_dbg("bus: Received SR_DF_FRAME_END packet.");
616                 break;
617         default:
618                 sr_dbg("bus: Received unknown packet type: %d.", packet->type);
619                 break;
620         }
621 }
622
623 /**
624  * Send a packet to whatever is listening on the datafeed bus.
625  *
626  * Hardware drivers use this to send a data packet to the frontend.
627  *
628  * @param sdi TODO.
629  * @param packet The datafeed packet to send to the session bus.
630  *
631  * @retval SR_OK Success.
632  * @retval SR_ERR_ARG Invalid argument.
633  *
634  * @private
635  */
636 SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
637                             const struct sr_datafeed_packet *packet)
638 {
639         GSList *l;
640         struct datafeed_callback *cb_struct;
641
642         if (!sdi) {
643                 sr_err("%s: sdi was NULL", __func__);
644                 return SR_ERR_ARG;
645         }
646
647         if (!packet) {
648                 sr_err("%s: packet was NULL", __func__);
649                 return SR_ERR_ARG;
650         }
651
652         for (l = sr_current_session->datafeed_callbacks; l; l = l->next) {
653                 if (sr_log_loglevel_get() >= SR_LOG_DBG)
654                         datafeed_dump(packet);
655                 cb_struct = l->data;
656                 cb_struct->cb(sdi, packet, cb_struct->cb_data);
657         }
658
659         return SR_OK;
660 }
661
662 /**
663  * Add an event source for a file descriptor.
664  *
665  * @param pollfd The GPollFD.
666  * @param[in] timeout Max time to wait before the callback is called,
667  *              ignored if 0.
668  * @param cb Callback function to add. Must not be NULL.
669  * @param cb_data Data for the callback function. Can be NULL.
670  * @param poll_object TODO.
671  *
672  * @retval SR_OK Success.
673  * @retval SR_ERR_ARG Invalid argument.
674  * @retval SR_ERR_MALLOC Memory allocation error.
675  */
676 static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
677                 int timeout, sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
678 {
679         struct source *new_sources, *s;
680         GPollFD *new_pollfds;
681
682         if (!cb) {
683                 sr_err("%s: cb was NULL", __func__);
684                 return SR_ERR_ARG;
685         }
686
687         /* Note: cb_data can be NULL, that's not a bug. */
688
689         new_pollfds = g_try_realloc(session->pollfds,
690                         sizeof(GPollFD) * (session->num_sources + 1));
691         if (!new_pollfds) {
692                 sr_err("%s: new_pollfds malloc failed", __func__);
693                 return SR_ERR_MALLOC;
694         }
695
696         new_sources = g_try_realloc(session->sources, sizeof(struct source) *
697                         (session->num_sources + 1));
698         if (!new_sources) {
699                 sr_err("%s: new_sources malloc failed", __func__);
700                 return SR_ERR_MALLOC;
701         }
702
703         new_pollfds[session->num_sources] = *pollfd;
704         s = &new_sources[session->num_sources++];
705         s->timeout = timeout;
706         s->cb = cb;
707         s->cb_data = cb_data;
708         s->poll_object = poll_object;
709         session->pollfds = new_pollfds;
710         session->sources = new_sources;
711
712         if (timeout != session->source_timeout && timeout > 0
713             && (session->source_timeout == -1 || timeout < session->source_timeout))
714                 session->source_timeout = timeout;
715
716         return SR_OK;
717 }
718
719 /**
720  * Add an event source for a file descriptor.
721  *
722  * @param fd The file descriptor.
723  * @param events Events to check for.
724  * @param timeout Max time to wait before the callback is called, ignored if 0.
725  * @param cb Callback function to add. Must not be NULL.
726  * @param cb_data Data for the callback function. Can be NULL.
727  *
728  * @retval SR_OK Success.
729  * @retval SR_ERR_ARG Invalid argument.
730  * @retval SR_ERR_MALLOC Memory allocation error.
731  *
732  * @since 0.3.0
733  */
734 SR_API int sr_session_source_add(struct sr_session *session, int fd,
735                 int events, int timeout, sr_receive_data_callback cb, void *cb_data)
736 {
737         GPollFD p;
738
739         (void) session;
740
741         p.fd = fd;
742         p.events = events;
743
744         return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)fd);
745 }
746
747 /**
748  * Add an event source for a GPollFD.
749  *
750  * @param pollfd The GPollFD.
751  * @param timeout Max time to wait before the callback is called, ignored if 0.
752  * @param cb Callback function to add. Must not be NULL.
753  * @param cb_data Data for the callback function. Can be NULL.
754  *
755  * @retval SR_OK Success.
756  * @retval SR_ERR_ARG Invalid argument.
757  * @retval SR_ERR_MALLOC Memory allocation error.
758  *
759  * @since 0.3.0
760  */
761 SR_API int sr_session_source_add_pollfd(struct sr_session *session,
762                 GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
763                 void *cb_data)
764 {
765         (void) session;
766
767         return _sr_session_source_add(session, pollfd, timeout, cb,
768                                       cb_data, (gintptr)pollfd);
769 }
770
771 /**
772  * Add an event source for a GIOChannel.
773  *
774  * @param channel The GIOChannel.
775  * @param events Events to poll on.
776  * @param timeout Max time to wait before the callback is called, ignored if 0.
777  * @param cb Callback function to add. Must not be NULL.
778  * @param cb_data Data for the callback function. Can be NULL.
779  *
780  * @retval SR_OK Success.
781  * @retval SR_ERR_ARG Invalid argument.
782  * @retval SR_ERR_MALLOC Memory allocation error.
783  *
784  * @since 0.3.0
785  */
786 SR_API int sr_session_source_add_channel(struct sr_session *session,
787                 GIOChannel *channel, int events, int timeout,
788                 sr_receive_data_callback cb, void *cb_data)
789 {
790         GPollFD p;
791
792         (void) session;
793
794 #ifdef _WIN32
795         g_io_channel_win32_make_pollfd(channel, events, &p);
796 #else
797         p.fd = g_io_channel_unix_get_fd(channel);
798         p.events = events;
799 #endif
800
801         return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)channel);
802 }
803
804 /**
805  * Remove the source belonging to the specified channel.
806  *
807  * @todo Add more error checks and logging.
808  *
809  * @param poll_object The channel for which the source should be removed.
810  *
811  * @retval SR_OK Success
812  * @retval SR_ERR_ARG Invalid arguments
813  * @retval SR_ERR_MALLOC Memory allocation error
814  * @retval SR_ERR_BUG Internal error
815  */
816 static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object)
817 {
818         struct source *new_sources;
819         GPollFD *new_pollfds;
820         unsigned int old;
821
822         if (!session->sources || !session->num_sources) {
823                 sr_err("%s: sources was NULL", __func__);
824                 return SR_ERR_BUG;
825         }
826
827         for (old = 0; old < session->num_sources; old++) {
828                 if (session->sources[old].poll_object == poll_object)
829                         break;
830         }
831
832         /* fd not found, nothing to do */
833         if (old == session->num_sources)
834                 return SR_OK;
835
836         session->num_sources -= 1;
837
838         if (old != session->num_sources) {
839                 memmove(&session->pollfds[old], &session->pollfds[old+1],
840                         (session->num_sources - old) * sizeof(GPollFD));
841                 memmove(&session->sources[old], &session->sources[old+1],
842                         (session->num_sources - old) * sizeof(struct source));
843         }
844
845         new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
846         if (!new_pollfds && session->num_sources > 0) {
847                 sr_err("%s: new_pollfds malloc failed", __func__);
848                 return SR_ERR_MALLOC;
849         }
850
851         new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
852         if (!new_sources && session->num_sources > 0) {
853                 sr_err("%s: new_sources malloc failed", __func__);
854                 return SR_ERR_MALLOC;
855         }
856
857         session->pollfds = new_pollfds;
858         session->sources = new_sources;
859
860         return SR_OK;
861 }
862
863 /**
864  * Remove the source belonging to the specified file descriptor.
865  *
866  * @param fd The file descriptor for which the source should be removed.
867  *
868  * @retval SR_OK Success
869  * @retval SR_ERR_ARG Invalid argument
870  * @retval SR_ERR_MALLOC Memory allocation error.
871  * @retval SR_ERR_BUG Internal error.
872  *
873  * @since 0.3.0
874  */
875 SR_API int sr_session_source_remove(struct sr_session *session, int fd)
876 {
877         (void) session;
878
879         return _sr_session_source_remove(session, (gintptr)fd);
880 }
881
882 /**
883  * Remove the source belonging to the specified poll descriptor.
884  *
885  * @param pollfd The poll descriptor for which the source should be removed.
886  *
887  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
888  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
889  *         internal errors.
890  *
891  * @since 0.2.0
892  */
893 SR_API int sr_session_source_remove_pollfd(struct sr_session *session,
894                 GPollFD *pollfd)
895 {
896         (void) session;
897
898         return _sr_session_source_remove(session, (gintptr)pollfd);
899 }
900
901 /**
902  * Remove the source belonging to the specified channel.
903  *
904  * @param channel The channel for which the source should be removed.
905  *
906  * @retval SR_OK Success.
907  * @retval SR_ERR_ARG Invalid argument.
908  * @retval SR_ERR_MALLOC Memory allocation error.
909  * @return SR_ERR_BUG Internal error.
910  *
911  * @since 0.2.0
912  */
913 SR_API int sr_session_source_remove_channel(struct sr_session *session,
914                 GIOChannel *channel)
915 {
916         (void) session;
917
918         return _sr_session_source_remove(session, (gintptr)channel);
919 }
920
921 /** @} */