]> sigrok.org Git - libsigrok.git/blob - session.c
15a72989e5bf4c1993433a24e371d5d174a14340
[libsigrok.git] / session.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 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 "sigrok.h"
26 #include "sigrok-internal.h"
27
28 /* demo.c. TODO: Should not be global! */
29 extern SR_PRIV GIOChannel channels[2];
30
31 struct source {
32         int fd;
33         int events;
34         int timeout;
35         sr_receive_data_callback cb;
36         void *user_data;
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 static int num_sources = 0;
43
44 static struct source *sources = NULL;
45 static int source_timeout = -1;
46
47 /**
48  * Create a new session.
49  *
50  * TODO: Should it use the file-global "session" variable or take an argument?
51  *       The same question applies to all the other session functions.
52  *
53  * @return A pointer to the newly allocated session, or NULL upon errors.
54  */
55 SR_API struct sr_session *sr_session_new(void)
56 {
57         if (!(session = g_try_malloc0(sizeof(struct sr_session)))) {
58                 sr_err("session: %s: session malloc failed", __func__);
59                 return NULL; /* TODO: SR_ERR_MALLOC? */
60         }
61
62         return session;
63 }
64
65 /**
66  * Destroy the current session.
67  *
68  * This frees up all memory used by the session.
69  *
70  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
71  */
72 SR_API int sr_session_destroy(void)
73 {
74         if (!session) {
75                 sr_err("session: %s: session was NULL", __func__);
76                 return SR_ERR_BUG;
77         }
78
79         g_slist_free(session->devices);
80         session->devices = NULL;
81
82         /* TODO: Error checks needed? */
83
84         /* TODO: Loop over protocol decoders and free them. */
85
86         g_free(session);
87         session = NULL;
88
89         return SR_OK;
90 }
91
92 /**
93  * Remove all the devices from the current session. TODO?
94  *
95  * The session itself (i.e., the struct sr_session) is not free'd and still
96  * exists after this function returns.
97  *
98  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
99  */
100 SR_API int sr_session_device_clear(void)
101 {
102         if (!session) {
103                 sr_err("session: %s: session was NULL", __func__);
104                 return SR_ERR_BUG;
105         }
106
107         g_slist_free(session->devices);
108         session->devices = NULL;
109
110         return SR_OK;
111 }
112
113 /**
114  * Add a device to the current session.
115  *
116  * @param device The device to add to the current session. Must not be NULL.
117  *               Also, device->plugin and device->plugin->opendev must not
118  *               be NULL.
119  *
120  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
121  */
122 SR_API int sr_session_device_add(struct sr_device *device)
123 {
124         int ret;
125
126         if (!device) {
127                 sr_err("session: %s: device was NULL", __func__);
128                 return SR_ERR_ARG;
129         }
130
131         if (!device->plugin) {
132                 sr_err("session: %s: device->plugin was NULL", __func__);
133                 return SR_ERR_ARG;
134         }
135
136         if (!device->plugin->opendev) {
137                 sr_err("session: %s: device->plugin->opendev was NULL",
138                        __func__);
139                 return SR_ERR_ARG;
140         }
141
142         if (!session) {
143                 sr_err("session: %s: session was NULL", __func__);
144                 return SR_ERR; /* TODO: SR_ERR_BUG? */
145         }
146
147         if ((ret = device->plugin->opendev(device->plugin_index)) != SR_OK) {
148                 sr_err("session: %s: opendev failed (%d)", __func__, ret);
149                 return ret;
150         }
151
152         session->devices = g_slist_append(session->devices, device);
153
154         return SR_OK;
155 }
156
157 /**
158  * Clear all datafeed callbacks in the current session.
159  *
160  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
161  */
162 SR_API int sr_session_datafeed_callback_clear(void)
163 {
164         if (!session) {
165                 sr_err("session: %s: session was NULL", __func__);
166                 return SR_ERR_BUG;
167         }
168
169         g_slist_free(session->datafeed_callbacks);
170         session->datafeed_callbacks = NULL;
171
172         return SR_OK;
173 }
174
175 /**
176  * Add a datafeed callback to the current session.
177  *
178  * @param callback Function to call when a chunk of data is received.
179  *
180  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
181  */
182 SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback callback)
183 {
184         if (!session) {
185                 sr_err("session: %s: session was NULL", __func__);
186                 return SR_ERR_BUG;
187         }
188
189         /* TODO: Is 'callback' allowed to be NULL? */
190
191         session->datafeed_callbacks =
192             g_slist_append(session->datafeed_callbacks, callback);
193
194         return SR_OK;
195 }
196
197 /**
198  * TODO.
199  */
200 static int sr_session_run_poll(void)
201 {
202         GPollFD *fds, my_gpollfd;
203         int ret, i;
204
205         fds = NULL;
206         while (session->running) {
207                 /* TODO: Add comment. */
208                 g_free(fds);
209
210                 /* Construct g_poll()'s array. */
211                 if (!(fds = g_try_malloc(sizeof(GPollFD) * num_sources))) {
212                         sr_err("session: %s: fds malloc failed", __func__);
213                         return SR_ERR_MALLOC;
214                 }
215                 for (i = 0; i < num_sources; i++) {
216 #ifdef _WIN32
217                         g_io_channel_win32_make_pollfd(&channels[0],
218                                         sources[i].events, &my_gpollfd);
219 #else
220                         my_gpollfd.fd = sources[i].fd;
221                         my_gpollfd.events = sources[i].events;
222                         fds[i] = my_gpollfd;
223 #endif
224                 }
225
226                 ret = g_poll(fds, num_sources, source_timeout);
227
228                 for (i = 0; i < num_sources; i++) {
229                         if (fds[i].revents > 0 || (ret == 0
230                                 && source_timeout == sources[i].timeout)) {
231                                 /*
232                                  * Invoke the source's callback on an event,
233                                  * or if the poll timeout out and this source
234                                  * asked for that timeout.
235                                  */
236                                 if (!sources[i].cb(fds[i].fd, fds[i].revents,
237                                                   sources[i].user_data))
238                                         sr_session_source_remove(sources[i].fd);
239                         }
240                 }
241         }
242         g_free(fds);
243
244         return SR_OK;
245 }
246
247 /**
248  * Start a session.
249  *
250  * There can only be one session at a time.
251  *
252  * @return SR_OK upon success, SR_ERR upon errors.
253  */
254 SR_API int sr_session_start(void)
255 {
256         struct sr_device *device;
257         GSList *l;
258         int ret;
259
260         if (!session) {
261                 sr_err("session: %s: session was NULL; a session must be "
262                        "created first, before starting it.", __func__);
263                 return SR_ERR; /* TODO: SR_ERR_BUG? */
264         }
265
266         if (!session->devices) {
267                 /* TODO: Actually the case? */
268                 sr_err("session: %s: session->devices was NULL; a session "
269                        "cannot be started without devices.", __func__);
270                 return SR_ERR; /* TODO: SR_ERR_BUG? */
271         }
272
273         /* TODO: Check plugin_index validity? */
274
275         sr_info("session: starting");
276
277         for (l = session->devices; l; l = l->next) {
278                 device = l->data;
279                 /* TODO: Check for device != NULL. */
280                 if ((ret = device->plugin->start_acquisition(
281                                 device->plugin_index, device)) != SR_OK) {
282                         sr_err("session: %s: could not start an acquisition "
283                                "(%d)", __func__, ret);
284                         break;
285                 }
286         }
287
288         /* TODO: What if there are multiple devices? Which return code? */
289
290         return ret;
291 }
292
293 /**
294  * Run the session.
295  *
296  * TODO: Various error checks etc.
297  *
298  * @return SR_OK upon success, SR_ERR_BUG upon errors.
299  */
300 SR_API int sr_session_run(void)
301 {
302         if (!session) {
303                 sr_err("session: %s: session was NULL; a session must be "
304                        "created first, before running it.", __func__);
305                 return SR_ERR_BUG;
306         }
307
308         if (!session->devices) {
309                 /* TODO: Actually the case? */
310                 sr_err("session: %s: session->devices was NULL; a session "
311                        "cannot be run without devices.", __func__);
312                 return SR_ERR_BUG;
313         }
314
315         sr_info("session: running");
316         session->running = TRUE;
317
318         /* Do we have real sources? */
319         if (num_sources == 1 && sources[0].fd == -1) {
320                 /* Dummy source, freewheel over it. */
321                 while (session->running)
322                         sources[0].cb(-1, 0, sources[0].user_data);
323         } else {
324                 /* Real sources, use g_poll() main loop. */
325                 sr_session_run_poll();
326         }
327
328         return SR_OK;
329 }
330
331 /**
332  * Halt the current session.
333  *
334  * This requests the current session be stopped as soon as possible, for example
335  * on receiving an SR_DF_END packet.
336  *
337  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
338  */
339 SR_API int sr_session_halt(void)
340 {
341         if (!session) {
342                 sr_err("session: %s: session was NULL", __func__);
343                 return SR_ERR_BUG;
344         }
345
346         sr_info("session: halting");
347         session->running = FALSE;
348
349         return SR_OK;
350 }
351
352 /**
353  * Stop the current session.
354  *
355  * The current session is stopped immediately, with all acquisition sessions
356  * being stopped and hardware plugins cleaned up.
357  *
358  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
359  */
360 SR_API int sr_session_stop(void)
361 {
362         struct sr_device *device;
363         GSList *l;
364
365         if (!session) {
366                 sr_err("session: %s: session was NULL", __func__);
367                 return SR_ERR_BUG;
368         }
369
370         sr_info("session: stopping");
371         session->running = FALSE;
372
373         for (l = session->devices; l; l = l->next) {
374                 device = l->data;
375                 /* Check for device != NULL. */
376                 if (device->plugin) {
377                         if (device->plugin->stop_acquisition)
378                                 device->plugin->stop_acquisition(device->plugin_index, device);
379                         if (device->plugin->cleanup)
380                                 device->plugin->cleanup();
381                 }
382         }
383
384         return SR_OK;
385 }
386
387 /**
388  * Debug helper.
389  *
390  * @param packet The packet to show debugging information for.
391  *
392  */
393 static void datafeed_dump(struct sr_datafeed_packet *packet)
394 {
395         struct sr_datafeed_logic *logic;
396
397         switch (packet->type) {
398         case SR_DF_HEADER:
399                 sr_dbg("bus: received SR_DF_HEADER");
400                 break;
401         case SR_DF_TRIGGER:
402                 sr_dbg("bus: received SR_DF_TRIGGER");
403                 break;
404         case SR_DF_LOGIC:
405                 logic = packet->payload;
406                 /* TODO: Check for logic != NULL. */
407                 sr_dbg("bus: received SR_DF_LOGIC %" PRIu64 " bytes", logic->length);
408                 break;
409         case SR_DF_END:
410                 sr_dbg("bus: received SR_DF_END");
411                 break;
412         default:
413                 sr_dbg("bus: received unknown packet type %d", packet->type);
414                 break;
415         }
416
417 }
418
419 /**
420  * Send a packet to whatever is listening on the datafeed bus.
421  *
422  * Hardware drivers use this to send a data packet to the frontend.
423  *
424  * @param device TODO.
425  * @param packet TODO.
426  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
427  */
428 SR_PRIV int sr_session_bus(struct sr_device *device,
429                           struct sr_datafeed_packet *packet)
430 {
431         GSList *l;
432         sr_datafeed_callback cb;
433
434         if (!device) {
435                 sr_err("session: %s: device was NULL", __func__);
436                 return SR_ERR_ARG;
437         }
438
439         if (!device->plugin) {
440                 sr_err("session: %s: device->plugin was NULL", __func__);
441                 return SR_ERR_ARG;
442         }
443
444         if (!packet) {
445                 sr_err("session: %s: packet was NULL", __func__);
446                 return SR_ERR_ARG;
447         }
448
449         for (l = session->datafeed_callbacks; l; l = l->next) {
450                 if (sr_log_loglevel_get() >= SR_LOG_DBG)
451                         datafeed_dump(packet);
452                 cb = l->data;
453                 /* TODO: Check for cb != NULL. */
454                 cb(device, packet);
455         }
456
457         return SR_OK;
458 }
459
460 /**
461  * TODO.
462  *
463  * TODO: More error checks etc.
464  *
465  * @param fd TODO.
466  * @param events TODO.
467  * @param timeout TODO.
468  * @param callback TODO.
469  * @param user_data TODO.
470  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
471  *         SR_ERR_MALLOC upon memory allocation errors.
472  */
473 SR_API int sr_session_source_add(int fd, int events, int timeout,
474                 sr_receive_data_callback callback, void *user_data)
475 {
476         struct source *new_sources, *s;
477
478         if (!callback) {
479                 sr_err("session: %s: callback was NULL", __func__);
480                 return SR_ERR_ARG;
481         }
482
483         /* Note: user_data can be NULL, that's not a bug. */
484
485         new_sources = g_try_malloc0(sizeof(struct source) * (num_sources + 1));
486         if (!new_sources) {
487                 sr_err("session: %s: new_sources malloc failed", __func__);
488                 return SR_ERR_MALLOC;
489         }
490
491         if (sources) {
492                 memcpy(new_sources, sources,
493                        sizeof(struct source) * num_sources);
494                 g_free(sources);
495         }
496
497         s = &new_sources[num_sources++];
498         s->fd = fd;
499         s->events = events;
500         s->timeout = timeout;
501         s->cb = callback;
502         s->user_data = user_data;
503         sources = new_sources;
504
505         if (timeout != source_timeout && timeout > 0
506             && (source_timeout == -1 || timeout < source_timeout))
507                 source_timeout = timeout;
508
509         return SR_OK;
510 }
511
512 /**
513  * Remove the source belonging to the specified file descriptor.
514  *
515  * TODO: More error checks.
516  *
517  * @param fd TODO.
518  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
519  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
520  *         internal errors.
521  */
522 SR_API int sr_session_source_remove(int fd)
523 {
524         struct source *new_sources;
525         int old, new;
526
527         if (!sources) {
528                 sr_err("session: %s: sources was NULL", __func__);
529                 return SR_ERR_BUG; /* TODO: Other? */
530         }
531
532         /* TODO: Check if 'fd' valid. */
533
534         new_sources = g_try_malloc0(sizeof(struct source) * num_sources);
535         if (!new_sources) {
536                 sr_err("session: %s: new_sources malloc failed", __func__);
537                 return SR_ERR_MALLOC;
538         }
539
540         for (old = 0, new = 0; old < num_sources; old++) {
541                 if (sources[old].fd != fd)
542                         memcpy(&new_sources[new++], &sources[old],
543                                sizeof(struct source));
544         }
545
546         if (old != new) {
547                 g_free(sources);
548                 sources = new_sources;
549                 num_sources--;
550         } else {
551                 /* Target fd was not found. */
552                 g_free(new_sources);
553         }
554
555         return SR_OK;
556 }