]> sigrok.org Git - libsigrok.git/blob - session.c
sr: session: Add support for GPollFD or GIOChannel based sources
[libsigrok.git] / session.c
1 /*
2  * This file is part of the sigrok 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 /* demo.c. TODO: Should not be global! */
29 extern SR_PRIV GIOChannel channels[2];
30
31 struct source {
32         int timeout;
33         sr_receive_data_callback_t cb;
34         void *cb_data;
35
36         /* This is used to keep track of the object (fd, pollfd or channel) which is
37          * being polled and will be used to match the source when removing it again.
38          */
39         gintptr poll_object;
40 };
41
42 /* There can only be one session at a time. */
43 /* 'session' is not static, it's used elsewhere (via 'extern'). */
44 struct sr_session *session;
45 static int num_sources = 0;
46
47 /* Both "sources" and "pollfds" are of the same size and contain pairs of
48  * descriptor and callback function. We can not embed the GPollFD into the
49  * source struct since we want to be able to pass the array of all poll
50  * descriptors to g_poll.
51  */
52 static struct source *sources = NULL;
53 static GPollFD *pollfds;
54 static int source_timeout = -1;
55
56 /**
57  * Create a new session.
58  *
59  * TODO: Should it use the file-global "session" variable or take an argument?
60  *       The same question applies to all the other session functions.
61  *
62  * @return A pointer to the newly allocated session, or NULL upon errors.
63  */
64 SR_API struct sr_session *sr_session_new(void)
65 {
66         if (!(session = g_try_malloc0(sizeof(struct sr_session)))) {
67                 sr_err("session: %s: session malloc failed", __func__);
68                 return NULL; /* TODO: SR_ERR_MALLOC? */
69         }
70
71         return session;
72 }
73
74 /**
75  * Destroy the current session.
76  *
77  * This frees up all memory used by the session.
78  *
79  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
80  */
81 SR_API int sr_session_destroy(void)
82 {
83         if (!session) {
84                 sr_err("session: %s: session was NULL", __func__);
85                 return SR_ERR_BUG;
86         }
87
88         g_slist_free(session->devs);
89         session->devs = NULL;
90
91         /* TODO: Error checks needed? */
92
93         /* TODO: Loop over protocol decoders and free them. */
94
95         g_free(session);
96         session = NULL;
97
98         return SR_OK;
99 }
100
101 /**
102  * Remove all the devices from the current session. TODO?
103  *
104  * The session itself (i.e., the struct sr_session) is not free'd and still
105  * exists after this function returns.
106  *
107  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
108  */
109 SR_API int sr_session_dev_remove_all(void)
110 {
111         if (!session) {
112                 sr_err("session: %s: session was NULL", __func__);
113                 return SR_ERR_BUG;
114         }
115
116         g_slist_free(session->devs);
117         session->devs = NULL;
118
119         return SR_OK;
120 }
121
122 /**
123  * Add a device to the current session.
124  *
125  * @param dev The device to add to the current session. Must not be NULL.
126  *            Also, dev->driver and dev->driver->dev_open must not be NULL.
127  *
128  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
129  */
130 SR_API int sr_session_dev_add(struct sr_dev *dev)
131 {
132         int ret;
133
134         if (!dev) {
135                 sr_err("session: %s: dev was NULL", __func__);
136                 return SR_ERR_ARG;
137         }
138
139         if (!session) {
140                 sr_err("session: %s: session was NULL", __func__);
141                 return SR_ERR_BUG;
142         }
143
144         /* If dev->driver is NULL, this is a virtual device. */
145         if (!dev->driver) {
146                 sr_dbg("session: %s: dev->driver was NULL, this seems to be "
147                        "a virtual device; continuing", __func__);
148                 /* Just add the device, don't run dev_open(). */
149                 session->devs = g_slist_append(session->devs, dev);
150                 return SR_OK;
151         }
152
153         /* dev->driver is non-NULL (i.e. we have a real device). */
154         if (!dev->driver->dev_open) {
155                 sr_err("session: %s: dev->driver->dev_open was NULL",
156                        __func__);
157                 return SR_ERR_BUG;
158         }
159
160         if ((ret = dev->driver->dev_open(dev->driver_index)) != SR_OK) {
161                 sr_err("session: %s: dev_open failed (%d)", __func__, ret);
162                 return ret;
163         }
164
165         session->devs = g_slist_append(session->devs, dev);
166
167         return SR_OK;
168 }
169
170 /**
171  * Remove all datafeed callbacks in the current session.
172  *
173  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
174  */
175 SR_API int sr_session_datafeed_callback_remove_all(void)
176 {
177         if (!session) {
178                 sr_err("session: %s: session was NULL", __func__);
179                 return SR_ERR_BUG;
180         }
181
182         g_slist_free(session->datafeed_callbacks);
183         session->datafeed_callbacks = NULL;
184
185         return SR_OK;
186 }
187
188 /**
189  * Add a datafeed callback to the current session.
190  *
191  * @param cb Function to call when a chunk of data is received.
192  *           Must not be NULL.
193  *
194  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
195  */
196 SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback_t cb)
197 {
198         if (!session) {
199                 sr_err("session: %s: session was NULL", __func__);
200                 return SR_ERR_BUG;
201         }
202
203         if (!cb) {
204                 sr_err("session: %s: cb was NULL", __func__);
205                 return SR_ERR_ARG;
206         }
207
208         session->datafeed_callbacks =
209             g_slist_append(session->datafeed_callbacks, cb);
210
211         return SR_OK;
212 }
213
214 /**
215  * TODO.
216  */
217 static int sr_session_run_poll(void)
218 {
219         int ret, i;
220
221         while (session->running) {
222                 ret = g_poll(pollfds, num_sources, source_timeout);
223
224                 for (i = 0; i < num_sources; i++) {
225                         if (pollfds[i].revents > 0 || (ret == 0
226                                 && source_timeout == sources[i].timeout)) {
227                                 /*
228                                  * Invoke the source's callback on an event,
229                                  * or if the poll timeout out and this source
230                                  * asked for that timeout.
231                                  */
232                                 if (!sources[i].cb(pollfds[i].fd, pollfds[i].revents,
233                                                   sources[i].cb_data))
234                                         sr_session_source_remove(pollfds[i].fd);
235                         }
236                 }
237         }
238
239         return SR_OK;
240 }
241
242 /**
243  * Start a session.
244  *
245  * There can only be one session at a time.
246  *
247  * @return SR_OK upon success, SR_ERR upon errors.
248  */
249 SR_API int sr_session_start(void)
250 {
251         struct sr_dev *dev;
252         GSList *l;
253         int ret;
254
255         if (!session) {
256                 sr_err("session: %s: session was NULL; a session must be "
257                        "created first, before starting it.", __func__);
258                 return SR_ERR_BUG;
259         }
260
261         if (!session->devs) {
262                 /* TODO: Actually the case? */
263                 sr_err("session: %s: session->devs was NULL; a session "
264                        "cannot be started without devices.", __func__);
265                 return SR_ERR_BUG;
266         }
267
268         /* TODO: Check driver_index validity? */
269
270         sr_info("session: starting");
271
272         for (l = session->devs; l; l = l->next) {
273                 dev = l->data;
274                 /* TODO: Check for dev != NULL. */
275                 if ((ret = dev->driver->dev_acquisition_start(
276                                 dev->driver_index, dev)) != SR_OK) {
277                         sr_err("session: %s: could not start an acquisition "
278                                "(%d)", __func__, ret);
279                         break;
280                 }
281         }
282
283         /* TODO: What if there are multiple devices? Which return code? */
284
285         return ret;
286 }
287
288 /**
289  * Run the session.
290  *
291  * TODO: Various error checks etc.
292  *
293  * @return SR_OK upon success, SR_ERR_BUG upon errors.
294  */
295 SR_API int sr_session_run(void)
296 {
297         if (!session) {
298                 sr_err("session: %s: session was NULL; a session must be "
299                        "created first, before running it.", __func__);
300                 return SR_ERR_BUG;
301         }
302
303         if (!session->devs) {
304                 /* TODO: Actually the case? */
305                 sr_err("session: %s: session->devs was NULL; a session "
306                        "cannot be run without devices.", __func__);
307                 return SR_ERR_BUG;
308         }
309
310         sr_info("session: running");
311         session->running = TRUE;
312
313         /* Do we have real sources? */
314         if (num_sources == 1 && pollfds[0].fd == -1) {
315                 /* Dummy source, freewheel over it. */
316                 while (session->running)
317                         sources[0].cb(-1, 0, sources[0].cb_data);
318         } else {
319                 /* Real sources, use g_poll() main loop. */
320                 sr_session_run_poll();
321         }
322
323         return SR_OK;
324 }
325
326 /**
327  * Halt the current session.
328  *
329  * This requests the current session be stopped as soon as possible, for
330  * example on receiving an SR_DF_END packet.
331  *
332  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
333  */
334 SR_API int sr_session_halt(void)
335 {
336         if (!session) {
337                 sr_err("session: %s: session was NULL", __func__);
338                 return SR_ERR_BUG;
339         }
340
341         sr_info("session: halting");
342         session->running = FALSE;
343
344         return SR_OK;
345 }
346
347 /**
348  * Stop the current session.
349  *
350  * The current session is stopped immediately, with all acquisition sessions
351  * being stopped and hardware drivers cleaned up.
352  *
353  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
354  */
355 SR_API int sr_session_stop(void)
356 {
357         struct sr_dev *dev;
358         GSList *l;
359
360         if (!session) {
361                 sr_err("session: %s: session was NULL", __func__);
362                 return SR_ERR_BUG;
363         }
364
365         sr_info("session: stopping");
366         session->running = FALSE;
367
368         for (l = session->devs; l; l = l->next) {
369                 dev = l->data;
370                 /* Check for dev != NULL. */
371                 if (dev->driver) {
372                         if (dev->driver->dev_acquisition_stop)
373                                 dev->driver->dev_acquisition_stop(dev->driver_index, dev);
374                         if (dev->driver->cleanup)
375                                 dev->driver->cleanup();
376                 }
377         }
378
379         return SR_OK;
380 }
381
382 /**
383  * Debug helper.
384  *
385  * @param packet The packet to show debugging information for.
386  */
387 static void datafeed_dump(struct sr_datafeed_packet *packet)
388 {
389         struct sr_datafeed_logic *logic;
390         struct sr_datafeed_analog *analog;
391
392         switch (packet->type) {
393         case SR_DF_HEADER:
394                 sr_dbg("bus: received SR_DF_HEADER");
395                 break;
396         case SR_DF_TRIGGER:
397                 sr_dbg("bus: received SR_DF_TRIGGER");
398                 break;
399         case SR_DF_META_LOGIC:
400                 sr_dbg("bus: received SR_DF_META_LOGIC");
401                 break;
402         case SR_DF_LOGIC:
403                 logic = packet->payload;
404                 /* TODO: Check for logic != NULL. */
405                 sr_dbg("bus: received SR_DF_LOGIC %" PRIu64 " bytes", logic->length);
406                 break;
407         case SR_DF_META_ANALOG:
408                 sr_dbg("bus: received SR_DF_META_LOGIC");
409                 break;
410         case SR_DF_ANALOG:
411                 analog = packet->payload;
412                 /* TODO: Check for analog != NULL. */
413                 sr_dbg("bus: received SR_DF_ANALOG %d samples", analog->num_samples);
414                 break;
415         case SR_DF_END:
416                 sr_dbg("bus: received SR_DF_END");
417                 break;
418         case SR_DF_FRAME_BEGIN:
419                 sr_dbg("bus: received SR_DF_FRAME_BEGIN");
420                 break;
421         case SR_DF_FRAME_END:
422                 sr_dbg("bus: received SR_DF_FRAME_END");
423                 break;
424         default:
425                 sr_dbg("bus: received unknown packet type %d", packet->type);
426                 break;
427         }
428 }
429
430 /**
431  * Send a packet to whatever is listening on the datafeed bus.
432  *
433  * Hardware drivers use this to send a data packet to the frontend.
434  *
435  * @param dev TODO.
436  * @param packet The datafeed packet to send to the session bus.
437  *
438  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
439  */
440 SR_PRIV int sr_session_send(struct sr_dev *dev,
441                             struct sr_datafeed_packet *packet)
442 {
443         GSList *l;
444         sr_datafeed_callback_t cb;
445
446         if (!dev) {
447                 sr_err("session: %s: dev was NULL", __func__);
448                 return SR_ERR_ARG;
449         }
450
451         if (!packet) {
452                 sr_err("session: %s: packet was NULL", __func__);
453                 return SR_ERR_ARG;
454         }
455
456         for (l = session->datafeed_callbacks; l; l = l->next) {
457                 if (sr_log_loglevel_get() >= SR_LOG_DBG)
458                         datafeed_dump(packet);
459                 cb = l->data;
460                 /* TODO: Check for cb != NULL. */
461                 cb(dev, packet);
462         }
463
464         return SR_OK;
465 }
466
467 static int _sr_session_source_add(GPollFD *pollfd, int timeout,
468                 sr_receive_data_callback_t cb, void *cb_data, gintptr poll_object)
469 {
470         struct source *new_sources, *s;
471         GPollFD *new_pollfds;
472
473         if (!cb) {
474                 sr_err("session: %s: cb was NULL", __func__);
475                 return SR_ERR_ARG;
476         }
477
478         /* Note: cb_data can be NULL, that's not a bug. */
479
480         new_pollfds = g_try_realloc(pollfds, sizeof(GPollFD) * (num_sources + 1));
481         if (!new_pollfds) {
482                 sr_err("session: %s: new_sources malloc failed", __func__);
483                 return SR_ERR_MALLOC;
484         }
485
486         new_sources = g_try_realloc(sources, sizeof(struct source) * (num_sources + 1));
487         if (!new_sources) {
488                 sr_err("session: %s: new_sources malloc failed", __func__);
489                 return SR_ERR_MALLOC;
490         }
491
492         new_pollfds[num_sources] = *pollfd;
493         s = &new_sources[num_sources++];
494         s->timeout = timeout;
495         s->cb = cb;
496         s->cb_data = cb_data;
497         s->poll_object = poll_object;
498         pollfds = new_pollfds;
499         sources = new_sources;
500
501         if (timeout != source_timeout && timeout > 0
502             && (source_timeout == -1 || timeout < source_timeout))
503                 source_timeout = timeout;
504
505         return SR_OK;
506 }
507
508 /**
509  * Add a event source for a file descriptor.
510  *
511  * @param fd The file descriptor.
512  * @param events Events to check for.
513  * @param timeout Max time to wait before the callback is called, ignored if 0.
514  * @param cb Callback function to add. Must not be NULL.
515  * @param cb_data Data for the callback function. Can be NULL.
516  *
517  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
518  *         SR_ERR_MALLOC upon memory allocation errors.
519  */
520 SR_API int sr_session_source_add(int fd, int events, int timeout,
521                 sr_receive_data_callback_t cb, void *cb_data)
522 {
523         GPollFD p;
524
525 #ifdef _WIN32
526         g_io_channel_win32_make_pollfd(&channels[0],
527                         events, &p);
528 #else
529         p.fd = fd;
530         p.events = events;
531 #endif
532
533         return _sr_session_source_add(&p, timeout, cb, cb_data, (gintptr)fd);
534 }
535
536 /**
537  * Add a event source for a GPollFD
538  *
539  * TODO: More error checks etc.
540  *
541  * @param pollfd The GPollFD.
542  * @param timeout Max time to wait before the callback is called, ignored if 0.
543  * @param cb Callback function to add. Must not be NULL.
544  * @param cb_data Data for the callback function. Can be NULL.
545  *
546  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
547  *         SR_ERR_MALLOC upon memory allocation errors.
548  */
549 SR_API int sr_session_source_add_pollfd(GPollFD *pollfd, int timeout,
550                 sr_receive_data_callback_t cb, void *cb_data)
551 {
552         return _sr_session_source_add(pollfd, timeout, cb, cb_data, (gintptr)pollfd);
553 }
554
555 /**
556  * Add a event source for a GIOChannel
557  *
558  * TODO: More error checks etc.
559  *
560  * @param channel The GIOChannel.
561  * @param events Events to poll on.
562  * @param timeout Max time to wait before the callback is called, ignored if 0.
563  * @param cb Callback function to add. Must not be NULL.
564  * @param cb_data Data for the callback function. Can be NULL.
565  *
566  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
567  *         SR_ERR_MALLOC upon memory allocation errors.
568  */
569 SR_API int sr_session_source_add_channel(GIOChannel *channel, int events, int timeout,
570                 sr_receive_data_callback_t cb, void *cb_data)
571 {
572         GPollFD p;
573
574 #ifdef _WIN32
575         g_io_channel_win32_make_pollfd(channel,
576                         events, &p);
577 #else
578         p.fd = g_io_channel_unix_get_fd(channel);
579         p.events = events;
580 #endif
581
582         return _sr_session_source_add(&p, timeout, cb, cb_data, (gintptr)channel);
583 }
584
585
586 static int _sr_session_source_remove(gintptr poll_object)
587 {
588         struct source *new_sources;
589         GPollFD *new_pollfds;
590         int old;
591
592         if (!sources) {
593                 sr_err("session: %s: sources was NULL", __func__);
594                 return SR_ERR_BUG;
595         }
596
597         for (old = 0; old < num_sources; old++) {
598                 if (sources[old].poll_object == poll_object)
599                         break;
600         }
601
602         /* fd not found, nothing to do */
603         if (old == num_sources)
604                 return SR_OK;
605
606         num_sources -= 1;
607
608         if (old != num_sources) {
609                 memmove(&pollfds[old], &pollfds[old+1],
610                         (num_sources - old) * sizeof(GPollFD));
611                 memmove(&sources[old], &sources[old+1],
612                         (num_sources - old) * sizeof(struct source));
613         }
614
615         new_pollfds = g_try_realloc(sources, sizeof(GPollFD) * num_sources);
616         if (!new_pollfds && num_sources > 0) {
617                 sr_err("session: %s: new_sources malloc failed", __func__);
618                 return SR_ERR_MALLOC;
619         }
620
621         new_sources = g_try_realloc(sources, sizeof(struct source) * num_sources);
622         if (!new_sources && num_sources > 0) {
623                 sr_err("session: %s: new_sources malloc failed", __func__);
624                 return SR_ERR_MALLOC;
625         }
626
627         pollfds = new_pollfds;
628         sources = new_sources;
629
630         return SR_OK;
631 }
632
633 /*
634  * Remove the source belonging to the specified file descriptor.
635  *
636  * TODO: More error checks.
637  *
638  * @param fd: The file descriptor for which the source should be removed.
639  *
640  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
641  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
642  *         internal errors.
643  */
644 SR_API int sr_session_source_remove(int fd)
645 {
646         return _sr_session_source_remove((gintptr)fd);
647 }
648
649 /**
650  * Remove the source belonging to the specified poll descriptor.
651  *
652  * TODO: More error checks.
653  *
654  * @param pollfd The poll descriptor for which the source should be removed.
655  *
656  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
657  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
658  *         internal errors.
659  */
660 SR_API int sr_session_source_remove_pollfd(GPollFD *pollfd)
661 {
662         return _sr_session_source_remove((gintptr)pollfd);
663 }
664
665 /*
666  * Remove the source belonging to the specified channel.
667  *
668  * TODO: More error checks.
669  *
670  * @parama channel: The channel for which the source should be removed.
671  *
672  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
673  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
674  *         internal errors.
675  */
676 SR_API int sr_session_source_remove_channel(GIOChannel *channel)
677 {
678         return _sr_session_source_remove((gintptr)channel);
679 }