]> sigrok.org Git - libsigrok.git/blame - session.c
More .gitignore files cleanup.
[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
9f45fb3a
UH
157/**
158 * Clear all datafeed callbacks in the current session.
159 *
160 * TODO: Should return int?
161 *
162 * TODO
163 */
8a2efef2 164void sr_session_datafeed_callback_clear(void)
a1bb33af 165{
9f45fb3a
UH
166 if (!session) {
167 sr_err("session: %s: session was NULL", __func__);
168 return; /* TODO: SR_ERR_BUG? */
169 }
170
a1bb33af
UH
171 g_slist_free(session->datafeed_callbacks);
172 session->datafeed_callbacks = NULL;
a1bb33af
UH
173}
174
9f45fb3a
UH
175/**
176 * Add a datafeed callback to the current session.
177 *
178 * TODO: Should return int?
179 *
180 * @param callback TODO
181 */
13b05733 182void sr_session_datafeed_callback_add(sr_datafeed_callback callback)
a1bb33af 183{
9f45fb3a
UH
184 if (!session) {
185 sr_err("session: %s: session was NULL", __func__);
186 return; /* TODO: SR_ERR_BUG? */
187 }
188
189 /* TODO: Is 'callback' allowed to be NULL? */
190
62c82025
UH
191 session->datafeed_callbacks =
192 g_slist_append(session->datafeed_callbacks, callback);
a1bb33af
UH
193}
194
9f45fb3a
UH
195/**
196 * TODO.
197 */
198static void sr_session_run_poll(void)
544a4582
BV
199{
200 GPollFD *fds, my_gpollfd;
201 int ret, i;
202
544a4582
BV
203 fds = NULL;
204 while (session->running) {
205 if (fds)
206 free(fds);
207
208 /* Construct g_poll()'s array. */
9f45fb3a 209 /* TODO: Check malloc return value. */
544a4582
BV
210 fds = malloc(sizeof(GPollFD) * num_sources);
211 for (i = 0; i < num_sources; i++) {
212#ifdef _WIN32
213 g_io_channel_win32_make_pollfd(&channels[0],
214 sources[i].events, &my_gpollfd);
215#else
216 my_gpollfd.fd = sources[i].fd;
217 my_gpollfd.events = sources[i].events;
218 fds[i] = my_gpollfd;
219#endif
220 }
221
222 ret = g_poll(fds, num_sources, source_timeout);
223
224 for (i = 0; i < num_sources; i++) {
225 if (fds[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 */
5c582d9f
GM
232 if (!sources[i].cb(fds[i].fd, fds[i].revents,
233 sources[i].user_data))
234 sr_session_source_remove(sources[i].fd);
544a4582
BV
235 }
236 }
237 }
238 free(fds);
544a4582
BV
239}
240
9f45fb3a
UH
241/**
242 * Start a session.
243 *
244 * There can only be one session at a time. TODO
245 *
246 * @return SR_OK upon success, SR_ERR upon errors.
247 */
8a2efef2 248int sr_session_start(void)
7d658874
BV
249{
250 struct sr_device *device;
251 GSList *l;
252 int ret;
253
9f45fb3a
UH
254 if (!session) {
255 sr_err("session: %s: session was NULL; a session must be "
256 "created first, before starting it.", __func__);
257 return SR_ERR; /* TODO: SR_ERR_BUG? */
258 }
259
260 if (!session->devices) {
261 /* TODO: Actually the case? */
262 sr_err("session: %s: session->devices was NULL; a session "
263 "cannot be started without devices.", __func__);
264 return SR_ERR; /* TODO: SR_ERR_BUG? */
265 }
266
267 /* TODO: Check plugin_index validity? */
268
b08024a8 269 sr_info("session: starting");
9f45fb3a 270
7d658874
BV
271 for (l = session->devices; l; l = l->next) {
272 device = l->data;
9f45fb3a 273 /* TODO: Check for device != NULL. */
7d658874 274 if ((ret = device->plugin->start_acquisition(
9f45fb3a 275 device->plugin_index, device)) != SR_OK) {
446a0372 276 sr_err("session: %s: could not start an acquisition "
9f45fb3a 277 "(%d)", __func__, ret);
7d658874 278 break;
9f45fb3a 279 }
7d658874
BV
280 }
281
9f45fb3a
UH
282 /* TODO: What if there are multiple devices? Which return code? */
283
7d658874
BV
284 return ret;
285}
286
9f45fb3a
UH
287/**
288 * Run the session.
289 *
290 * TODO: Should return int.
291 * TODO: Various error checks etc.
292 *
293 * TODO.
294 */
8a2efef2 295void sr_session_run(void)
7d658874 296{
9f45fb3a
UH
297 if (!session) {
298 sr_err("session: %s: session was NULL; a session must be "
299 "created first, before running it.", __func__);
300 return; /* TODO: SR_ERR or SR_ERR_BUG? */
301 }
302
303 if (!session->devices) {
304 /* TODO: Actually the case? */
305 sr_err("session: %s: session->devices was NULL; a session "
306 "cannot be run without devices.", __func__);
307 return; /* TODO: SR_ERR or SR_ERR_BUG? */
308 }
309
b08024a8 310 sr_info("session: running");
7d658874
BV
311 session->running = TRUE;
312
9f45fb3a
UH
313 /* Do we have real sources? */
314 if (num_sources == 1 && sources[0].fd == -1) {
315 /* Dummy source, freewheel over it. */
7d658874
BV
316 while (session->running)
317 sources[0].cb(-1, 0, sources[0].user_data);
9f45fb3a
UH
318 } else {
319 /* Real sources, use g_poll() main loop. */
8a2efef2 320 sr_session_run_poll();
9f45fb3a
UH
321 }
322
323 /* TODO: return SR_OK; */
7d658874
BV
324}
325
9f45fb3a
UH
326/**
327 * Halt the current session.
328 *
329 * TODO: Return int.
330 *
331 * TODO.
332 */
8a2efef2 333void sr_session_halt(void)
544a4582 334{
9f45fb3a
UH
335 if (!session) {
336 sr_err("session: %s: session was NULL", __func__);
337 return; /* TODO: SR_ERR; or SR_ERR_BUG? */
338 }
339
b08024a8 340 sr_info("session: halting");
544a4582 341 session->running = FALSE;
9f45fb3a
UH
342
343 /* TODO: return SR_OK; */
544a4582
BV
344}
345
9f45fb3a
UH
346/**
347 * Stop the current session.
348 *
349 * TODO: Difference to halt?
350 *
351 * TODO.
352 */
8a2efef2 353void sr_session_stop(void)
a1bb33af 354{
5c2d46d1 355 struct sr_device *device;
a1bb33af
UH
356 GSList *l;
357
9f45fb3a
UH
358 if (!session) {
359 sr_err("session: %s: session was NULL", __func__);
360 return; /* TODO: SR_ERR or SR_ERR_BUG? */
361 }
362
b08024a8 363 sr_info("session: stopping");
544a4582 364 session->running = FALSE;
62c82025 365 for (l = session->devices; l; l = l->next) {
a1bb33af 366 device = l->data;
b8c2f85f 367 if (device->plugin && device->plugin->stop_acquisition)
aa4b1107 368 device->plugin->stop_acquisition(device->plugin_index, device);
a1bb33af 369 }
9f45fb3a
UH
370
371 /* TODO: return SR_OK; */
a1bb33af
UH
372}
373
9f45fb3a
UH
374/**
375 * TODO.
376 *
377 * TODO: Should return int?
378 * TODO: Various error checks.
379 *
380 * @param packet TODO.
381 */
7d2afd6c
BV
382static void datafeed_dump(struct sr_datafeed_packet *packet)
383{
384 struct sr_datafeed_logic *logic;
385
386 switch (packet->type) {
387 case SR_DF_HEADER:
388 sr_dbg("bus: received SR_DF_HEADER");
389 break;
390 case SR_DF_TRIGGER:
391 sr_dbg("bus: received SR_DF_TRIGGER at %lu ms",
392 packet->timeoffset / 1000000);
393 break;
394 case SR_DF_LOGIC:
395 logic = packet->payload;
9f45fb3a
UH
396 sr_dbg("bus: received SR_DF_LOGIC at %f ms duration %f ms, "
397 "%" PRIu64 " bytes", packet->timeoffset / 1000000.0,
398 packet->duration / 1000000.0, logic->length);
7d2afd6c
BV
399 break;
400 case SR_DF_END:
401 sr_dbg("bus: received SR_DF_END");
402 break;
403 default:
9f45fb3a 404 /* TODO: sr_err() and abort? */
7d2afd6c 405 sr_dbg("bus: received unknown packet type %d", packet->type);
9f45fb3a 406 break;
7d2afd6c 407 }
7d2afd6c
BV
408}
409
9f45fb3a
UH
410/**
411 * TODO.
412 *
413 * TODO: Should return int?
414 *
415 * @param device TODO.
416 * @param packet TODO.
417 */
8a2efef2 418void sr_session_bus(struct sr_device *device, struct sr_datafeed_packet *packet)
a1bb33af
UH
419{
420 GSList *l;
13b05733 421 sr_datafeed_callback cb;
a1bb33af 422
9f45fb3a
UH
423 if (!device) {
424 sr_err("session: %s: device was NULL", __func__);
425 return; /* TODO: SR_ERR_ARG */
426 }
427
428 if (!device->plugin) {
429 sr_err("session: %s: device->plugin was NULL", __func__);
430 return; /* TODO: SR_ERR_ARG */
431 }
432
62c82025 433 /*
aa4b1107 434 * TODO: Send packet through PD pipe, and send the output of that to
62c82025 435 * the callbacks as well.
a1bb33af 436 */
62c82025 437 for (l = session->datafeed_callbacks; l; l = l->next) {
a1bb33af 438 cb = l->data;
9f45fb3a 439 /* TODO: Check for cb != NULL. */
7d2afd6c 440 datafeed_dump(packet);
a1bb33af
UH
441 cb(device, packet);
442 }
9f45fb3a
UH
443
444 /* TODO: return SR_OK; */
a1bb33af
UH
445}
446
9f45fb3a
UH
447/**
448 * TODO.
449 *
450 * TODO: Should return int?
451 * TODO: Switch to g_try_malloc0() / g_free().
452 * TODO: More error checks etc.
453 *
454 * @param fd TODO.
455 * @param events TODO.
456 * @param timeout TODO.
457 * @param callback TODO.
458 * @param user_data TODO.
459 */
8a2efef2 460void sr_session_source_add(int fd, int events, int timeout,
a887e3da 461 sr_receive_data_callback callback, void *user_data)
544a4582
BV
462{
463 struct source *new_sources, *s;
464
9f45fb3a
UH
465 if (!callback) {
466 sr_err("session: %s: callback was NULL", __func__);
467 return; /* TODO: SR_ERR_ARG */
468 }
469
470 /* Note: user_data can be NULL, that's not a bug. */
471
544a4582 472 new_sources = calloc(1, sizeof(struct source) * (num_sources + 1));
9f45fb3a
UH
473 if (!new_sources) {
474 sr_err("session: %s: new_sources malloc failed", __func__);
475 return; /* TODO: SR_ERR_MALLOC */
476 }
544a4582
BV
477
478 if (sources) {
479 memcpy(new_sources, sources,
480 sizeof(struct source) * num_sources);
481 free(sources);
482 }
483
484 s = &new_sources[num_sources++];
485 s->fd = fd;
486 s->events = events;
487 s->timeout = timeout;
488 s->cb = callback;
489 s->user_data = user_data;
490 sources = new_sources;
491
492 if (timeout != source_timeout && timeout > 0
493 && (source_timeout == -1 || timeout < source_timeout))
494 source_timeout = timeout;
9f45fb3a
UH
495
496 /* TODO: return SR_OK; */
544a4582
BV
497}
498
9f45fb3a
UH
499/**
500 * Remove the source belonging to the specified file descriptor.
501 *
502 * TODO: Should return int.
503 * TODO: More error checks.
504 * TODO: Switch to g_try_malloc0() / g_free().
505 *
506 * @param fd TODO.
507 */
8a2efef2 508void sr_session_source_remove(int fd)
544a4582
BV
509{
510 struct source *new_sources;
511 int old, new;
512
9f45fb3a 513 /* TODO */
544a4582
BV
514 if (!sources)
515 return;
516
517 new_sources = calloc(1, sizeof(struct source) * num_sources);
9f45fb3a
UH
518 if (!new_sources) {
519 sr_err("session: %s: new_sources malloc failed", __func__);
520 return; /* TODO: SR_ERR_MALLOC */
521 }
522
523 for (old = 0, new = 0; old < num_sources; old++) {
544a4582
BV
524 if (sources[old].fd != fd)
525 memcpy(&new_sources[new++], &sources[old],
526 sizeof(struct source));
9f45fb3a 527 }
544a4582
BV
528
529 if (old != new) {
530 free(sources);
531 sources = new_sources;
532 num_sources--;
533 } else {
534 /* Target fd was not found. */
535 free(new_sources);
536 }
537}