]> sigrok.org Git - libsigrok.git/blame - session.c
sr: session: Add docs and some error checks.
[libsigrok.git] / session.c
CommitLineData
a1bb33af
UH
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
22b02383 20#include "config.h"
a1bb33af
UH
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <string.h>
544a4582 25#include <glib.h>
b7f09cf8
UH
26#include "sigrok.h"
27#include "sigrok-internal.h"
aa4b1107 28
9f4bc44e
UH
29/* demo.c */
30extern GIOChannel channels[2];
a1bb33af 31
544a4582
BV
32struct source {
33 int fd;
34 int events;
35 int timeout;
a887e3da 36 sr_receive_data_callback cb;
544a4582
BV
37 void *user_data;
38};
39
7d658874 40/* There can only be one session at a time. */
a0ecd83b 41/* 'session' is not static, it's used elsewhere (via 'extern'). */
2872d21e 42struct sr_session *session;
a0ecd83b 43static int num_sources = 0;
544a4582 44
a0ecd83b
UH
45static struct source *sources = NULL;
46static int source_timeout = -1;
544a4582 47
9f45fb3a
UH
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 */
8a2efef2 59struct sr_session *sr_session_new(void)
a1bb33af 60{
9f45fb3a
UH
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 }
a1bb33af
UH
65
66 return session;
67}
68
9f45fb3a
UH
69/**
70 * Destroy the current session.
71 *
72 * This frees up all memory used by the session.
73 *
74 * TODO: Should return int?
75 */
8a2efef2 76void sr_session_destroy(void)
a1bb33af 77{
9f45fb3a
UH
78 if (!session) {
79 sr_warn("session: %s: session was NULL", __func__);
80 return; /* TODO: SR_ERR_BUG? */
81 }
82
a1bb33af
UH
83 g_slist_free(session->devices);
84
9f45fb3a
UH
85 /* TODO: Error checks needed? */
86
aa4b1107 87 /* TODO: Loop over protocol decoders and free them. */
a1bb33af
UH
88
89 g_free(session);
9f45fb3a
UH
90
91 session = NULL;
a1bb33af
UH
92}
93
9f45fb3a
UH
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 */
8a2efef2 102void sr_session_device_clear(void)
a1bb33af 103{
9f45fb3a
UH
104 if (!session) {
105 sr_warn("session: %s: session was NULL", __func__);
106 return; /* TODO: SR_ERR_BUG? */
107 }
108
a1bb33af
UH
109 g_slist_free(session->devices);
110 session->devices = NULL;
a1bb33af
UH
111}
112
9f45fb3a
UH
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 */
8a2efef2 122int sr_session_device_add(struct sr_device *device)
a1bb33af
UH
123{
124 int ret;
125
9f45fb3a
UH
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;
aa4b1107 150 }
a1bb33af 151
aa4b1107
BV
152 session->devices = g_slist_append(session->devices, device);
153
e46b8fb1 154 return SR_OK;
a1bb33af
UH
155}
156
8bb416be 157#if 0
8a2efef2 158void sr_session_pa_clear(void)
a1bb33af 159{
62c82025
UH
160 /*
161 * The protocols are pointers to the global set of PA plugins,
162 * so don't free them.
163 */
a1bb33af
UH
164 g_slist_free(session->analyzers);
165 session->analyzers = NULL;
a1bb33af
UH
166}
167
809c5f20 168void sr_session_pa_add(struct sr_analyzer *an)
a1bb33af 169{
a1bb33af 170 session->analyzers = g_slist_append(session->analyzers, an);
a1bb33af 171}
8bb416be 172#endif
a1bb33af 173
9f45fb3a
UH
174/**
175 * Clear all datafeed callbacks in the current session.
176 *
177 * TODO: Should return int?
178 *
179 * TODO
180 */
8a2efef2 181void sr_session_datafeed_callback_clear(void)
a1bb33af 182{
9f45fb3a
UH
183 if (!session) {
184 sr_err("session: %s: session was NULL", __func__);
185 return; /* TODO: SR_ERR_BUG? */
186 }
187
a1bb33af
UH
188 g_slist_free(session->datafeed_callbacks);
189 session->datafeed_callbacks = NULL;
a1bb33af
UH
190}
191
9f45fb3a
UH
192/**
193 * Add a datafeed callback to the current session.
194 *
195 * TODO: Should return int?
196 *
197 * @param callback TODO
198 */
13b05733 199void sr_session_datafeed_callback_add(sr_datafeed_callback callback)
a1bb33af 200{
9f45fb3a
UH
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
62c82025
UH
208 session->datafeed_callbacks =
209 g_slist_append(session->datafeed_callbacks, callback);
a1bb33af
UH
210}
211
9f45fb3a
UH
212/**
213 * TODO.
214 */
215static void sr_session_run_poll(void)
544a4582
BV
216{
217 GPollFD *fds, my_gpollfd;
218 int ret, i;
219
544a4582
BV
220 fds = NULL;
221 while (session->running) {
222 if (fds)
223 free(fds);
224
225 /* Construct g_poll()'s array. */
9f45fb3a 226 /* TODO: Check malloc return value. */
544a4582
BV
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 */
5c582d9f
GM
249 if (!sources[i].cb(fds[i].fd, fds[i].revents,
250 sources[i].user_data))
251 sr_session_source_remove(sources[i].fd);
544a4582
BV
252 }
253 }
254 }
255 free(fds);
544a4582
BV
256}
257
9f45fb3a
UH
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 */
8a2efef2 265int sr_session_start(void)
7d658874
BV
266{
267 struct sr_device *device;
268 GSList *l;
269 int ret;
270
9f45fb3a
UH
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
b08024a8 286 sr_info("session: starting");
9f45fb3a 287
7d658874
BV
288 for (l = session->devices; l; l = l->next) {
289 device = l->data;
9f45fb3a 290 /* TODO: Check for device != NULL. */
7d658874 291 if ((ret = device->plugin->start_acquisition(
9f45fb3a
UH
292 device->plugin_index, device)) != SR_OK) {
293 sr_err("session: %s: could not start an acquisition ",
294 "(%d)", __func__, ret);
7d658874 295 break;
9f45fb3a 296 }
7d658874
BV
297 }
298
9f45fb3a
UH
299 /* TODO: What if there are multiple devices? Which return code? */
300
7d658874
BV
301 return ret;
302}
303
9f45fb3a
UH
304/**
305 * Run the session.
306 *
307 * TODO: Should return int.
308 * TODO: Various error checks etc.
309 *
310 * TODO.
311 */
8a2efef2 312void sr_session_run(void)
7d658874 313{
9f45fb3a
UH
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
b08024a8 327 sr_info("session: running");
7d658874
BV
328 session->running = TRUE;
329
9f45fb3a
UH
330 /* Do we have real sources? */
331 if (num_sources == 1 && sources[0].fd == -1) {
332 /* Dummy source, freewheel over it. */
7d658874
BV
333 while (session->running)
334 sources[0].cb(-1, 0, sources[0].user_data);
9f45fb3a
UH
335 } else {
336 /* Real sources, use g_poll() main loop. */
8a2efef2 337 sr_session_run_poll();
9f45fb3a
UH
338 }
339
340 /* TODO: return SR_OK; */
7d658874
BV
341}
342
9f45fb3a
UH
343/**
344 * Halt the current session.
345 *
346 * TODO: Return int.
347 *
348 * TODO.
349 */
8a2efef2 350void sr_session_halt(void)
544a4582 351{
9f45fb3a
UH
352 if (!session) {
353 sr_err("session: %s: session was NULL", __func__);
354 return; /* TODO: SR_ERR; or SR_ERR_BUG? */
355 }
356
b08024a8 357 sr_info("session: halting");
544a4582 358 session->running = FALSE;
9f45fb3a
UH
359
360 /* TODO: return SR_OK; */
544a4582
BV
361}
362
9f45fb3a
UH
363/**
364 * Stop the current session.
365 *
366 * TODO: Difference to halt?
367 *
368 * TODO.
369 */
8a2efef2 370void sr_session_stop(void)
a1bb33af 371{
5c2d46d1 372 struct sr_device *device;
a1bb33af
UH
373 GSList *l;
374
9f45fb3a
UH
375 if (!session) {
376 sr_err("session: %s: session was NULL", __func__);
377 return; /* TODO: SR_ERR or SR_ERR_BUG? */
378 }
379
b08024a8 380 sr_info("session: stopping");
544a4582 381 session->running = FALSE;
62c82025 382 for (l = session->devices; l; l = l->next) {
a1bb33af 383 device = l->data;
b8c2f85f 384 if (device->plugin && device->plugin->stop_acquisition)
aa4b1107 385 device->plugin->stop_acquisition(device->plugin_index, device);
a1bb33af 386 }
9f45fb3a
UH
387
388 /* TODO: return SR_OK; */
a1bb33af
UH
389}
390
9f45fb3a
UH
391/**
392 * TODO.
393 *
394 * TODO: Should return int?
395 * TODO: Various error checks.
396 *
397 * @param packet TODO.
398 */
7d2afd6c
BV
399static 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;
9f45fb3a
UH
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);
7d2afd6c
BV
416 break;
417 case SR_DF_END:
418 sr_dbg("bus: received SR_DF_END");
419 break;
420 default:
9f45fb3a 421 /* TODO: sr_err() and abort? */
7d2afd6c 422 sr_dbg("bus: received unknown packet type %d", packet->type);
9f45fb3a 423 break;
7d2afd6c 424 }
7d2afd6c
BV
425}
426
9f45fb3a
UH
427/**
428 * TODO.
429 *
430 * TODO: Should return int?
431 *
432 * @param device TODO.
433 * @param packet TODO.
434 */
8a2efef2 435void sr_session_bus(struct sr_device *device, struct sr_datafeed_packet *packet)
a1bb33af
UH
436{
437 GSList *l;
13b05733 438 sr_datafeed_callback cb;
a1bb33af 439
9f45fb3a
UH
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
62c82025 450 /*
aa4b1107 451 * TODO: Send packet through PD pipe, and send the output of that to
62c82025 452 * the callbacks as well.
a1bb33af 453 */
62c82025 454 for (l = session->datafeed_callbacks; l; l = l->next) {
a1bb33af 455 cb = l->data;
9f45fb3a 456 /* TODO: Check for cb != NULL. */
7d2afd6c 457 datafeed_dump(packet);
a1bb33af
UH
458 cb(device, packet);
459 }
9f45fb3a
UH
460
461 /* TODO: return SR_OK; */
a1bb33af
UH
462}
463
9f45fb3a
UH
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 */
8a2efef2 477void sr_session_source_add(int fd, int events, int timeout,
a887e3da 478 sr_receive_data_callback callback, void *user_data)
544a4582
BV
479{
480 struct source *new_sources, *s;
481
9f45fb3a
UH
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
544a4582 489 new_sources = calloc(1, sizeof(struct source) * (num_sources + 1));
9f45fb3a
UH
490 if (!new_sources) {
491 sr_err("session: %s: new_sources malloc failed", __func__);
492 return; /* TODO: SR_ERR_MALLOC */
493 }
544a4582
BV
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;
9f45fb3a
UH
512
513 /* TODO: return SR_OK; */
544a4582
BV
514}
515
9f45fb3a
UH
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 */
8a2efef2 525void sr_session_source_remove(int fd)
544a4582
BV
526{
527 struct source *new_sources;
528 int old, new;
529
9f45fb3a 530 /* TODO */
544a4582
BV
531 if (!sources)
532 return;
533
534 new_sources = calloc(1, sizeof(struct source) * num_sources);
9f45fb3a
UH
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++) {
544a4582
BV
541 if (sources[old].fd != fd)
542 memcpy(&new_sources[new++], &sources[old],
543 sizeof(struct source));
9f45fb3a 544 }
544a4582
BV
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}