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