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