]> sigrok.org Git - libsigrok.git/blame - src/session.c
Add a small set of unit tests for session handling.
[libsigrok.git] / src / session.c
CommitLineData
a1bb33af 1/*
50985c20 2 * This file is part of the libsigrok project.
a1bb33af 3 *
c73d2ea4 4 * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
a1bb33af
UH
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>
544a4582 24#include <glib.h>
45c59c8b
BV
25#include "libsigrok.h"
26#include "libsigrok-internal.h"
aa4b1107 27
2ad1deb8 28/** @cond PRIVATE */
3544f848 29#define LOG_PREFIX "session"
2ad1deb8 30/** @endcond */
a421dc1d 31
393fb9cb
UH
32/**
33 * @file
34 *
35 * Creating, using, or destroying libsigrok sessions.
36 */
37
7b870c38
UH
38/**
39 * @defgroup grp_session Session handling
40 *
41 * Creating, using, or destroying libsigrok sessions.
42 *
43 * @{
44 */
45
544a4582 46struct source {
544a4582 47 int timeout;
144f6660 48 sr_receive_data_callback cb;
1f9813eb 49 void *cb_data;
aac0ea25
LPC
50
51 /* This is used to keep track of the object (fd, pollfd or channel) which is
52 * being polled and will be used to match the source when removing it again.
53 */
54 gintptr poll_object;
544a4582
BV
55};
56
2726474a 57struct datafeed_callback {
144f6660 58 sr_datafeed_callback cb;
2726474a
ML
59 void *cb_data;
60};
61
9f45fb3a
UH
62/**
63 * Create a new session.
7efe889e
UH
64 *
65 * @param new_session This will contain a pointer to the newly created
66 * session if the return value is SR_OK, otherwise the value
67 * is undefined and should not be used. Must not be NULL.
9f45fb3a 68 *
0812c40e
ML
69 * @retval SR_OK Success.
70 * @retval SR_ERR_BUG A session exists already.
47117241 71 *
0812c40e 72 * @since 0.4.0
9f45fb3a 73 */
3337e9a1 74SR_API int sr_session_new(struct sr_session **new_session)
a1bb33af 75{
3337e9a1 76 struct sr_session *session;
a1bb33af 77
3337e9a1 78 session = g_malloc0(sizeof(struct sr_session));
b7e94111 79
3337e9a1
BV
80 session->source_timeout = -1;
81 session->running = FALSE;
82 session->abort_session = FALSE;
83 g_mutex_init(&session->stop_mutex);
0812c40e 84
3337e9a1 85 *new_session = session;
0812c40e
ML
86
87 return SR_OK;
a1bb33af
UH
88}
89
9f45fb3a 90/**
0812c40e 91 * Destroy a session.
9f45fb3a
UH
92 * This frees up all memory used by the session.
93 *
7efe889e
UH
94 * @param session The session to destroy. Must not be NULL.
95 *
04cb9157 96 * @retval SR_OK Success.
0812c40e 97 * @retval SR_ERR_ARG Invalid session passed.
47117241 98 *
0812c40e 99 * @since 0.4.0
9f45fb3a 100 */
0812c40e 101SR_API int sr_session_destroy(struct sr_session *session)
a1bb33af 102{
9f45fb3a 103 if (!session) {
a421dc1d 104 sr_err("%s: session was NULL", __func__);
0812c40e 105 return SR_ERR_ARG;
9f45fb3a
UH
106 }
107
0812c40e 108 sr_session_dev_remove_all(session);
33c6e4c5 109 g_mutex_clear(&session->stop_mutex);
3d68b612
BV
110 if (session->trigger)
111 sr_trigger_free(session->trigger);
33c6e4c5 112
a1bb33af 113 g_free(session);
0812c40e 114
e0508e67 115 return SR_OK;
a1bb33af
UH
116}
117
9f45fb3a 118/**
0812c40e 119 * Remove all the devices from a session.
9f45fb3a
UH
120 *
121 * The session itself (i.e., the struct sr_session) is not free'd and still
122 * exists after this function returns.
123 *
7efe889e
UH
124 * @param session The session to use. Must not be NULL.
125 *
04cb9157 126 * @retval SR_OK Success.
0812c40e 127 * @retval SR_ERR_BUG Invalid session passed.
47117241 128 *
0812c40e 129 * @since 0.4.0
9f45fb3a 130 */
0812c40e 131SR_API int sr_session_dev_remove_all(struct sr_session *session)
a1bb33af 132{
0812c40e
ML
133 struct sr_dev_inst *sdi;
134 GSList *l;
135
9f45fb3a 136 if (!session) {
a421dc1d 137 sr_err("%s: session was NULL", __func__);
0812c40e
ML
138 return SR_ERR_ARG;
139 }
140
141 for (l = session->devs; l; l = l->next) {
142 sdi = (struct sr_dev_inst *) l->data;
143 sdi->session = NULL;
9f45fb3a
UH
144 }
145
681803df 146 g_slist_free(session->devs);
bb7ef793 147 session->devs = NULL;
e0508e67
UH
148
149 return SR_OK;
a1bb33af
UH
150}
151
9f45fb3a 152/**
0812c40e 153 * Add a device instance to a session.
9f45fb3a 154 *
7efe889e 155 * @param session The session to add to. Must not be NULL.
0812c40e 156 * @param sdi The device instance to add to a session. Must not
de4d3f99
BV
157 * be NULL. Also, sdi->driver and sdi->driver->dev_open must
158 * not be NULL.
9f45fb3a 159 *
04cb9157
MH
160 * @retval SR_OK Success.
161 * @retval SR_ERR_ARG Invalid argument.
47117241 162 *
0812c40e 163 * @since 0.4.0
9f45fb3a 164 */
0812c40e
ML
165SR_API int sr_session_dev_add(struct sr_session *session,
166 struct sr_dev_inst *sdi)
a1bb33af 167{
5451816f 168 int ret;
a1bb33af 169
de4d3f99 170 if (!sdi) {
a421dc1d 171 sr_err("%s: sdi was NULL", __func__);
9f45fb3a
UH
172 return SR_ERR_ARG;
173 }
174
d6eb0c33 175 if (!session) {
a421dc1d 176 sr_err("%s: session was NULL", __func__);
0812c40e
ML
177 return SR_ERR_ARG;
178 }
179
180 /* If sdi->session is not NULL, the device is already in this or
181 * another session. */
182 if (sdi->session) {
183 sr_err("%s: already assigned to session", __func__);
184 return SR_ERR_ARG;
d6eb0c33
UH
185 }
186
de4d3f99
BV
187 /* If sdi->driver is NULL, this is a virtual device. */
188 if (!sdi->driver) {
a421dc1d 189 sr_dbg("%s: sdi->driver was NULL, this seems to be "
d6eb0c33
UH
190 "a virtual device; continuing", __func__);
191 /* Just add the device, don't run dev_open(). */
de4d3f99 192 session->devs = g_slist_append(session->devs, (gpointer)sdi);
0812c40e 193 sdi->session = session;
d6eb0c33 194 return SR_OK;
9f45fb3a
UH
195 }
196
de4d3f99
BV
197 /* sdi->driver is non-NULL (i.e. we have a real device). */
198 if (!sdi->driver->dev_open) {
a421dc1d 199 sr_err("%s: sdi->driver->dev_open was NULL", __func__);
8ec95d22 200 return SR_ERR_BUG;
9f45fb3a
UH
201 }
202
de4d3f99 203 session->devs = g_slist_append(session->devs, (gpointer)sdi);
0812c40e 204 sdi->session = session;
aa4b1107 205
5451816f 206 if (session->running) {
32b7cd4f
DE
207 /* Adding a device to a running session. Commit settings
208 * and start acquisition on that device now. */
209 if ((ret = sr_config_commit(sdi)) != SR_OK) {
210 sr_err("Failed to commit device settings before "
211 "starting acquisition in running session (%s)",
212 sr_strerror(ret));
213 return ret;
214 }
5451816f 215 if ((ret = sdi->driver->dev_acquisition_start(sdi,
32b7cd4f 216 (void *)sdi)) != SR_OK) {
5451816f 217 sr_err("Failed to start acquisition of device in "
32b7cd4f
DE
218 "running session (%s)", sr_strerror(ret));
219 return ret;
220 }
5451816f
BV
221 }
222
e46b8fb1 223 return SR_OK;
a1bb33af
UH
224}
225
2bb311b4 226/**
0812c40e 227 * List all device instances attached to a session.
2bb311b4 228 *
7efe889e 229 * @param session The session to use. Must not be NULL.
2bb311b4
BV
230 * @param devlist A pointer where the device instance list will be
231 * stored on return. If no devices are in the session,
232 * this will be NULL. Each element in the list points
233 * to a struct sr_dev_inst *.
234 * The list must be freed by the caller, but not the
235 * elements pointed to.
236 *
04cb9157 237 * @retval SR_OK Success.
0812c40e 238 * @retval SR_ERR_ARG Invalid argument.
47117241 239 *
0812c40e 240 * @since 0.4.0
2bb311b4 241 */
0812c40e 242SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist)
2bb311b4 243{
2bb311b4 244 if (!session)
0812c40e
ML
245 return SR_ERR_ARG;
246
247 if (!devlist)
248 return SR_ERR_ARG;
2bb311b4
BV
249
250 *devlist = g_slist_copy(session->devs);
251
252 return SR_OK;
253}
254
9f45fb3a 255/**
0812c40e 256 * Remove all datafeed callbacks in a session.
9f45fb3a 257 *
7efe889e
UH
258 * @param session The session to use. Must not be NULL.
259 *
04cb9157 260 * @retval SR_OK Success.
0812c40e 261 * @retval SR_ERR_ARG Invalid session passed.
47117241 262 *
0812c40e 263 * @since 0.4.0
9f45fb3a 264 */
0812c40e 265SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session)
a1bb33af 266{
9f45fb3a 267 if (!session) {
a421dc1d 268 sr_err("%s: session was NULL", __func__);
0812c40e 269 return SR_ERR_ARG;
9f45fb3a
UH
270 }
271
2726474a 272 g_slist_free_full(session->datafeed_callbacks, g_free);
a1bb33af 273 session->datafeed_callbacks = NULL;
e0508e67
UH
274
275 return SR_OK;
a1bb33af
UH
276}
277
9f45fb3a 278/**
0812c40e 279 * Add a datafeed callback to a session.
9f45fb3a 280 *
7efe889e 281 * @param session The session to use. Must not be NULL.
d08490aa 282 * @param cb Function to call when a chunk of data is received.
0abee507 283 * Must not be NULL.
85222791 284 * @param cb_data Opaque pointer passed in by the caller.
a1645fcd 285 *
04cb9157
MH
286 * @retval SR_OK Success.
287 * @retval SR_ERR_BUG No session exists.
47117241
UH
288 *
289 * @since 0.3.0
9f45fb3a 290 */
0812c40e
ML
291SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
292 sr_datafeed_callback cb, void *cb_data)
a1bb33af 293{
2726474a
ML
294 struct datafeed_callback *cb_struct;
295
9f45fb3a 296 if (!session) {
a421dc1d 297 sr_err("%s: session was NULL", __func__);
e0508e67 298 return SR_ERR_BUG;
9f45fb3a
UH
299 }
300
0abee507 301 if (!cb) {
a421dc1d 302 sr_err("%s: cb was NULL", __func__);
0abee507
UH
303 return SR_ERR_ARG;
304 }
9f45fb3a 305
2726474a
ML
306 if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
307 return SR_ERR_MALLOC;
308
309 cb_struct->cb = cb;
310 cb_struct->cb_data = cb_data;
311
62c82025 312 session->datafeed_callbacks =
2726474a 313 g_slist_append(session->datafeed_callbacks, cb_struct);
e0508e67
UH
314
315 return SR_OK;
a1bb33af
UH
316}
317
0812c40e 318SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session)
7b5e6d29
BV
319{
320 return session->trigger;
321}
322
0812c40e 323SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig)
7b5e6d29
BV
324{
325 session->trigger = trig;
326
327 return SR_OK;
328}
329
b483be74 330/**
0812c40e 331 * Call every device in the current session's callback.
b483be74
BV
332 *
333 * For sessions not driven by select loops such as sr_session_run(),
334 * but driven by another scheduler, this can be used to poll the devices
335 * from within that scheduler.
336 *
7efe889e 337 * @param session The session to use. Must not be NULL.
f6eb2cb5
BV
338 * @param block If TRUE, this call will wait for any of the session's
339 * sources to fire an event on the file descriptors, or
340 * any of their timeouts to activate. In other words, this
341 * can be used as a select loop.
342 * If FALSE, all sources have their callback run, regardless
343 * of file descriptor or timeout status.
344 *
04cb9157
MH
345 * @retval SR_OK Success.
346 * @retval SR_ERR Error occured.
b483be74 347 */
3337e9a1 348static int sr_session_iteration(struct sr_session *session, gboolean block)
544a4582 349{
b7e94111
LPC
350 unsigned int i;
351 int ret;
544a4582 352
b483be74
BV
353 ret = g_poll(session->pollfds, session->num_sources,
354 block ? session->source_timeout : 0);
355 for (i = 0; i < session->num_sources; i++) {
356 if (session->pollfds[i].revents > 0 || (ret == 0
357 && session->source_timeout == session->sources[i].timeout)) {
33c6e4c5 358 /*
b483be74
BV
359 * Invoke the source's callback on an event,
360 * or if the poll timed out and this source
361 * asked for that timeout.
33c6e4c5 362 */
b483be74
BV
363 if (!session->sources[i].cb(session->pollfds[i].fd,
364 session->pollfds[i].revents,
365 session->sources[i].cb_data))
0812c40e
ML
366 sr_session_source_remove(session,
367 session->sources[i].poll_object);
b483be74
BV
368 }
369 /*
370 * We want to take as little time as possible to stop
371 * the session if we have been told to do so. Therefore,
372 * we check the flag after processing every source, not
373 * just once per main event loop.
374 */
375 g_mutex_lock(&session->stop_mutex);
376 if (session->abort_session) {
0812c40e 377 sr_session_stop_sync(session);
b483be74
BV
378 /* But once is enough. */
379 session->abort_session = FALSE;
544a4582 380 }
b483be74 381 g_mutex_unlock(&session->stop_mutex);
544a4582 382 }
e0508e67
UH
383
384 return SR_OK;
544a4582
BV
385}
386
7b5e6d29
BV
387
388static int verify_trigger(struct sr_trigger *trigger)
389{
390 struct sr_trigger_stage *stage;
391 struct sr_trigger_match *match;
392 GSList *l, *m;
393
394 if (!trigger->stages) {
395 sr_err("No trigger stages defined.");
396 return SR_ERR;
397 }
398
399 sr_spew("Checking trigger:");
400 for (l = trigger->stages; l; l = l->next) {
401 stage = l->data;
402 if (!stage->matches) {
403 sr_err("Stage %d has no matches defined.", stage->stage);
404 return SR_ERR;
405 }
406 for (m = stage->matches; m; m = m->next) {
407 match = m->data;
408 if (!match->channel) {
409 sr_err("Stage %d match has no channel.", stage->stage);
410 return SR_ERR;
411 }
412 if (!match->match) {
413 sr_err("Stage %d match is not defined.", stage->stage);
414 return SR_ERR;
415 }
416 sr_spew("Stage %d match on channel %s, match %d", stage->stage,
417 match->channel->name, match->match);
418 }
419 }
420
421 return SR_OK;
422}
9f45fb3a
UH
423/**
424 * Start a session.
425 *
7efe889e
UH
426 * @param session The session to use. Must not be NULL.
427 *
04cb9157 428 * @retval SR_OK Success.
0812c40e 429 * @retval SR_ERR_ARG Invalid session passed.
47117241 430 *
0812c40e 431 * @since 0.4.0
9f45fb3a 432 */
0812c40e 433SR_API int sr_session_start(struct sr_session *session)
7d658874 434{
de4d3f99 435 struct sr_dev_inst *sdi;
7d658874
BV
436 GSList *l;
437 int ret;
438
9f45fb3a 439 if (!session) {
0812c40e
ML
440 sr_err("%s: session was NULL", __func__);
441 return SR_ERR_ARG;
9f45fb3a
UH
442 }
443
bb7ef793 444 if (!session->devs) {
a421dc1d 445 sr_err("%s: session->devs was NULL; a session "
9f45fb3a 446 "cannot be started without devices.", __func__);
0812c40e 447 return SR_ERR_ARG;
9f45fb3a
UH
448 }
449
7b5e6d29
BV
450 if (session->trigger && verify_trigger(session->trigger) != SR_OK)
451 return SR_ERR;
452
c7142604 453 sr_info("Starting.");
9f45fb3a 454
b7c3e849 455 ret = SR_OK;
bb7ef793 456 for (l = session->devs; l; l = l->next) {
de4d3f99 457 sdi = l->data;
32b7cd4f
DE
458 if ((ret = sr_config_commit(sdi)) != SR_OK) {
459 sr_err("Failed to commit device settings before "
460 "starting acquisition (%s)", sr_strerror(ret));
461 break;
462 }
de4d3f99 463 if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
a421dc1d 464 sr_err("%s: could not start an acquisition "
568dcacc 465 "(%s)", __func__, sr_strerror(ret));
7d658874 466 break;
9f45fb3a 467 }
7d658874
BV
468 }
469
9f45fb3a
UH
470 /* TODO: What if there are multiple devices? Which return code? */
471
7d658874
BV
472 return ret;
473}
474
9f45fb3a 475/**
0812c40e 476 * Run a session.
9f45fb3a 477 *
7efe889e
UH
478 * @param session The session to use. Must not be NULL.
479 *
04cb9157 480 * @retval SR_OK Success.
0812c40e 481 * @retval SR_ERR_ARG Invalid session passed.
47117241 482 *
0812c40e 483 * @since 0.4.0
9f45fb3a 484 */
0812c40e 485SR_API int sr_session_run(struct sr_session *session)
7d658874 486{
9f45fb3a 487 if (!session) {
0812c40e
ML
488 sr_err("%s: session was NULL", __func__);
489 return SR_ERR_ARG;
9f45fb3a
UH
490 }
491
bb7ef793 492 if (!session->devs) {
9f45fb3a 493 /* TODO: Actually the case? */
a421dc1d 494 sr_err("%s: session->devs was NULL; a session "
9f45fb3a 495 "cannot be run without devices.", __func__);
0812c40e 496 return SR_ERR_ARG;
9f45fb3a 497 }
5451816f 498 session->running = TRUE;
9f45fb3a 499
a421dc1d 500 sr_info("Running.");
7d658874 501
9f45fb3a 502 /* Do we have real sources? */
b7e94111 503 if (session->num_sources == 1 && session->pollfds[0].fd == -1) {
9f45fb3a 504 /* Dummy source, freewheel over it. */
2cbeb2b7 505 while (session->num_sources)
b7e94111 506 session->sources[0].cb(-1, 0, session->sources[0].cb_data);
9f45fb3a
UH
507 } else {
508 /* Real sources, use g_poll() main loop. */
b483be74 509 while (session->num_sources)
3337e9a1 510 sr_session_iteration(session, TRUE);
9f45fb3a
UH
511 }
512
e0508e67 513 return SR_OK;
7d658874
BV
514}
515
9f45fb3a 516/**
0812c40e 517 * Stop a session.
9f45fb3a 518 *
0812c40e
ML
519 * The session is stopped immediately, with all acquisition sessions stopped
520 * and hardware drivers cleaned up.
9f45fb3a 521 *
33c6e4c5
AG
522 * This must be called from within the session thread, to prevent freeing
523 * resources that the session thread will try to use.
524 *
7efe889e
UH
525 * @param session The session to use. Must not be NULL.
526 *
04cb9157 527 * @retval SR_OK Success.
0812c40e 528 * @retval SR_ERR_ARG Invalid session passed.
72a08bcc
BV
529 *
530 * @private
9f45fb3a 531 */
0812c40e 532SR_PRIV int sr_session_stop_sync(struct sr_session *session)
a1bb33af 533{
de4d3f99 534 struct sr_dev_inst *sdi;
a1bb33af
UH
535 GSList *l;
536
9f45fb3a 537 if (!session) {
a421dc1d 538 sr_err("%s: session was NULL", __func__);
0812c40e 539 return SR_ERR_ARG;
9f45fb3a
UH
540 }
541
a421dc1d 542 sr_info("Stopping.");
e0508e67 543
bb7ef793 544 for (l = session->devs; l; l = l->next) {
de4d3f99
BV
545 sdi = l->data;
546 if (sdi->driver) {
547 if (sdi->driver->dev_acquisition_stop)
548 sdi->driver->dev_acquisition_stop(sdi, sdi);
8c76be53 549 }
a1bb33af 550 }
5451816f 551 session->running = FALSE;
9f45fb3a 552
e0508e67 553 return SR_OK;
a1bb33af
UH
554}
555
33c6e4c5 556/**
0812c40e 557 * Stop a session.
33c6e4c5 558 *
0812c40e
ML
559 * The session is stopped immediately, with all acquisition sessions being
560 * stopped and hardware drivers cleaned up.
33c6e4c5
AG
561 *
562 * If the session is run in a separate thread, this function will not block
563 * until the session is finished executing. It is the caller's responsibility
564 * to wait for the session thread to return before assuming that the session is
565 * completely decommissioned.
566 *
7efe889e
UH
567 * @param session The session to use. Must not be NULL.
568 *
04cb9157 569 * @retval SR_OK Success.
0812c40e 570 * @retval SR_ERR_ARG Invalid session passed.
47117241 571 *
0812c40e 572 * @since 0.4.0
33c6e4c5 573 */
0812c40e 574SR_API int sr_session_stop(struct sr_session *session)
33c6e4c5
AG
575{
576 if (!session) {
577 sr_err("%s: session was NULL", __func__);
578 return SR_ERR_BUG;
579 }
580
581 g_mutex_lock(&session->stop_mutex);
582 session->abort_session = TRUE;
583 g_mutex_unlock(&session->stop_mutex);
584
585 return SR_OK;
586}
587
9f45fb3a 588/**
a1645fcd 589 * Debug helper.
9f45fb3a 590 *
996b0c72 591 * @param packet The packet to show debugging information for.
9f45fb3a 592 */
bf53457d 593static void datafeed_dump(const struct sr_datafeed_packet *packet)
7d2afd6c 594{
bf53457d
JH
595 const struct sr_datafeed_logic *logic;
596 const struct sr_datafeed_analog *analog;
7d2afd6c
BV
597
598 switch (packet->type) {
599 case SR_DF_HEADER:
a421dc1d 600 sr_dbg("bus: Received SR_DF_HEADER packet.");
7d2afd6c
BV
601 break;
602 case SR_DF_TRIGGER:
a421dc1d 603 sr_dbg("bus: Received SR_DF_TRIGGER packet.");
7d2afd6c 604 break;
c71bac3b 605 case SR_DF_META:
a421dc1d 606 sr_dbg("bus: Received SR_DF_META packet.");
ee7489d2 607 break;
7d2afd6c
BV
608 case SR_DF_LOGIC:
609 logic = packet->payload;
7ea45862
UH
610 sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
611 "unitsize = %d).", logic->length, logic->unitsize);
7d2afd6c 612 break;
ee7489d2
BV
613 case SR_DF_ANALOG:
614 analog = packet->payload;
a421dc1d
UH
615 sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
616 analog->num_samples);
ee7489d2 617 break;
7d2afd6c 618 case SR_DF_END:
a421dc1d 619 sr_dbg("bus: Received SR_DF_END packet.");
7d2afd6c 620 break;
6ea7669c 621 case SR_DF_FRAME_BEGIN:
a421dc1d 622 sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
6ea7669c
BV
623 break;
624 case SR_DF_FRAME_END:
a421dc1d 625 sr_dbg("bus: Received SR_DF_FRAME_END packet.");
6ea7669c 626 break;
7d2afd6c 627 default:
a421dc1d 628 sr_dbg("bus: Received unknown packet type: %d.", packet->type);
9f45fb3a 629 break;
7d2afd6c 630 }
7d2afd6c
BV
631}
632
9f45fb3a 633/**
a1645fcd
BV
634 * Send a packet to whatever is listening on the datafeed bus.
635 *
636 * Hardware drivers use this to send a data packet to the frontend.
9f45fb3a 637 *
6b2d8d3e 638 * @param sdi TODO.
31ccebc4 639 * @param packet The datafeed packet to send to the session bus.
44dae539 640 *
04cb9157
MH
641 * @retval SR_OK Success.
642 * @retval SR_ERR_ARG Invalid argument.
b4bd7088
UH
643 *
644 * @private
9f45fb3a 645 */
de4d3f99 646SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
bf53457d 647 const struct sr_datafeed_packet *packet)
a1bb33af
UH
648{
649 GSList *l;
2726474a 650 struct datafeed_callback *cb_struct;
a1bb33af 651
de4d3f99 652 if (!sdi) {
a421dc1d 653 sr_err("%s: sdi was NULL", __func__);
e0508e67 654 return SR_ERR_ARG;
9f45fb3a
UH
655 }
656
e0508e67 657 if (!packet) {
a421dc1d 658 sr_err("%s: packet was NULL", __func__);
e0508e67 659 return SR_ERR_ARG;
9f45fb3a
UH
660 }
661
3337e9a1 662 for (l = sdi->session->datafeed_callbacks; l; l = l->next) {
18beaeff
BV
663 if (sr_log_loglevel_get() >= SR_LOG_DBG)
664 datafeed_dump(packet);
2726474a
ML
665 cb_struct = l->data;
666 cb_struct->cb(sdi, packet, cb_struct->cb_data);
a1bb33af 667 }
9f45fb3a 668
e0508e67 669 return SR_OK;
a1bb33af
UH
670}
671
6b2d8d3e
UH
672/**
673 * Add an event source for a file descriptor.
674 *
7efe889e 675 * @param session The session to use. Must not be NULL.
6b2d8d3e 676 * @param pollfd The GPollFD.
04cb9157
MH
677 * @param[in] timeout Max time to wait before the callback is called,
678 * ignored if 0.
6b2d8d3e
UH
679 * @param cb Callback function to add. Must not be NULL.
680 * @param cb_data Data for the callback function. Can be NULL.
681 * @param poll_object TODO.
682 *
04cb9157
MH
683 * @retval SR_OK Success.
684 * @retval SR_ERR_ARG Invalid argument.
685 * @retval SR_ERR_MALLOC Memory allocation error.
6b2d8d3e 686 */
102f1239
BV
687static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
688 int timeout, sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
544a4582
BV
689{
690 struct source *new_sources, *s;
aac0ea25 691 GPollFD *new_pollfds;
544a4582 692
d08490aa 693 if (!cb) {
a421dc1d 694 sr_err("%s: cb was NULL", __func__);
e0508e67 695 return SR_ERR_ARG;
9f45fb3a
UH
696 }
697
1f9813eb 698 /* Note: cb_data can be NULL, that's not a bug. */
9f45fb3a 699
2ac2e629
BV
700 new_pollfds = g_try_realloc(session->pollfds,
701 sizeof(GPollFD) * (session->num_sources + 1));
0687dfcd 702 if (!new_pollfds) {
a421dc1d 703 sr_err("%s: new_pollfds malloc failed", __func__);
0687dfcd
LPC
704 return SR_ERR_MALLOC;
705 }
706
b7e94111 707 new_sources = g_try_realloc(session->sources, sizeof(struct source) *
2ac2e629 708 (session->num_sources + 1));
9f45fb3a 709 if (!new_sources) {
a421dc1d 710 sr_err("%s: new_sources malloc failed", __func__);
e0508e67 711 return SR_ERR_MALLOC;
9f45fb3a 712 }
544a4582 713
b7e94111
LPC
714 new_pollfds[session->num_sources] = *pollfd;
715 s = &new_sources[session->num_sources++];
544a4582 716 s->timeout = timeout;
d08490aa 717 s->cb = cb;
1f9813eb 718 s->cb_data = cb_data;
aac0ea25 719 s->poll_object = poll_object;
b7e94111
LPC
720 session->pollfds = new_pollfds;
721 session->sources = new_sources;
544a4582 722
b7e94111
LPC
723 if (timeout != session->source_timeout && timeout > 0
724 && (session->source_timeout == -1 || timeout < session->source_timeout))
725 session->source_timeout = timeout;
9f45fb3a 726
e0508e67 727 return SR_OK;
544a4582
BV
728}
729
9f45fb3a 730/**
6b2d8d3e 731 * Add an event source for a file descriptor.
9f45fb3a 732 *
7efe889e 733 * @param session The session to use. Must not be NULL.
aac0ea25
LPC
734 * @param fd The file descriptor.
735 * @param events Events to check for.
736 * @param timeout Max time to wait before the callback is called, ignored if 0.
737 * @param cb Callback function to add. Must not be NULL.
738 * @param cb_data Data for the callback function. Can be NULL.
9f45fb3a 739 *
04cb9157
MH
740 * @retval SR_OK Success.
741 * @retval SR_ERR_ARG Invalid argument.
742 * @retval SR_ERR_MALLOC Memory allocation error.
47117241
UH
743 *
744 * @since 0.3.0
aac0ea25 745 */
0812c40e
ML
746SR_API int sr_session_source_add(struct sr_session *session, int fd,
747 int events, int timeout, sr_receive_data_callback cb, void *cb_data)
aac0ea25
LPC
748{
749 GPollFD p;
750
aac0ea25
LPC
751 p.fd = fd;
752 p.events = events;
aac0ea25 753
102f1239 754 return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)fd);
aac0ea25
LPC
755}
756
757/**
1a895c61 758 * Add an event source for a GPollFD.
aac0ea25 759 *
7efe889e 760 * @param session The session to use. Must not be NULL.
aac0ea25
LPC
761 * @param pollfd The GPollFD.
762 * @param timeout Max time to wait before the callback is called, ignored if 0.
763 * @param cb Callback function to add. Must not be NULL.
764 * @param cb_data Data for the callback function. Can be NULL.
44dae539 765 *
04cb9157
MH
766 * @retval SR_OK Success.
767 * @retval SR_ERR_ARG Invalid argument.
768 * @retval SR_ERR_MALLOC Memory allocation error.
47117241
UH
769 *
770 * @since 0.3.0
9f45fb3a 771 */
0812c40e
ML
772SR_API int sr_session_source_add_pollfd(struct sr_session *session,
773 GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
774 void *cb_data)
aac0ea25 775{
102f1239 776 return _sr_session_source_add(session, pollfd, timeout, cb,
1a895c61 777 cb_data, (gintptr)pollfd);
aac0ea25
LPC
778}
779
780/**
1a895c61 781 * Add an event source for a GIOChannel.
aac0ea25 782 *
7efe889e 783 * @param session The session to use. Must not be NULL.
aac0ea25
LPC
784 * @param channel The GIOChannel.
785 * @param events Events to poll on.
786 * @param timeout Max time to wait before the callback is called, ignored if 0.
787 * @param cb Callback function to add. Must not be NULL.
788 * @param cb_data Data for the callback function. Can be NULL.
789 *
04cb9157
MH
790 * @retval SR_OK Success.
791 * @retval SR_ERR_ARG Invalid argument.
792 * @retval SR_ERR_MALLOC Memory allocation error.
47117241
UH
793 *
794 * @since 0.3.0
aac0ea25 795 */
0812c40e
ML
796SR_API int sr_session_source_add_channel(struct sr_session *session,
797 GIOChannel *channel, int events, int timeout,
798 sr_receive_data_callback cb, void *cb_data)
aac0ea25
LPC
799{
800 GPollFD p;
801
802#ifdef _WIN32
6b2d8d3e 803 g_io_channel_win32_make_pollfd(channel, events, &p);
aac0ea25
LPC
804#else
805 p.fd = g_io_channel_unix_get_fd(channel);
806 p.events = events;
807#endif
808
102f1239 809 return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)channel);
aac0ea25
LPC
810}
811
6b2d8d3e
UH
812/**
813 * Remove the source belonging to the specified channel.
814 *
815 * @todo Add more error checks and logging.
816 *
7efe889e 817 * @param session The session to use. Must not be NULL.
04cb9157 818 * @param poll_object The channel for which the source should be removed.
6b2d8d3e 819 *
04cb9157
MH
820 * @retval SR_OK Success
821 * @retval SR_ERR_ARG Invalid arguments
822 * @retval SR_ERR_MALLOC Memory allocation error
823 * @retval SR_ERR_BUG Internal error
6b2d8d3e 824 */
102f1239 825static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object)
544a4582
BV
826{
827 struct source *new_sources;
0687dfcd 828 GPollFD *new_pollfds;
b7e94111 829 unsigned int old;
544a4582 830
b7e94111 831 if (!session->sources || !session->num_sources) {
a421dc1d 832 sr_err("%s: sources was NULL", __func__);
0abee507 833 return SR_ERR_BUG;
e0508e67
UH
834 }
835
b7e94111
LPC
836 for (old = 0; old < session->num_sources; old++) {
837 if (session->sources[old].poll_object == poll_object)
2bccd322 838 break;
9f45fb3a
UH
839 }
840
2bccd322 841 /* fd not found, nothing to do */
b7e94111 842 if (old == session->num_sources)
2bccd322
LPC
843 return SR_OK;
844
b7e94111 845 session->num_sources -= 1;
2bccd322 846
b7e94111
LPC
847 if (old != session->num_sources) {
848 memmove(&session->pollfds[old], &session->pollfds[old+1],
849 (session->num_sources - old) * sizeof(GPollFD));
850 memmove(&session->sources[old], &session->sources[old+1],
851 (session->num_sources - old) * sizeof(struct source));
9f45fb3a 852 }
544a4582 853
b7e94111
LPC
854 new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
855 if (!new_pollfds && session->num_sources > 0) {
a421dc1d 856 sr_err("%s: new_pollfds malloc failed", __func__);
0687dfcd
LPC
857 return SR_ERR_MALLOC;
858 }
859
b7e94111
LPC
860 new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
861 if (!new_sources && session->num_sources > 0) {
a421dc1d 862 sr_err("%s: new_sources malloc failed", __func__);
2bccd322 863 return SR_ERR_MALLOC;
544a4582 864 }
e0508e67 865
b7e94111
LPC
866 session->pollfds = new_pollfds;
867 session->sources = new_sources;
2bccd322 868
e0508e67 869 return SR_OK;
544a4582 870}
aac0ea25 871
6b2d8d3e 872/**
aac0ea25
LPC
873 * Remove the source belonging to the specified file descriptor.
874 *
7efe889e 875 * @param session The session to use. Must not be NULL.
1a895c61 876 * @param fd The file descriptor for which the source should be removed.
aac0ea25 877 *
04cb9157
MH
878 * @retval SR_OK Success
879 * @retval SR_ERR_ARG Invalid argument
880 * @retval SR_ERR_MALLOC Memory allocation error.
881 * @retval SR_ERR_BUG Internal error.
47117241
UH
882 *
883 * @since 0.3.0
aac0ea25 884 */
0812c40e 885SR_API int sr_session_source_remove(struct sr_session *session, int fd)
aac0ea25 886{
102f1239 887 return _sr_session_source_remove(session, (gintptr)fd);
aac0ea25
LPC
888}
889
890/**
891 * Remove the source belonging to the specified poll descriptor.
892 *
7efe889e 893 * @param session The session to use. Must not be NULL.
aac0ea25
LPC
894 * @param pollfd The poll descriptor for which the source should be removed.
895 *
896 * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
897 * SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
898 * internal errors.
47117241
UH
899 *
900 * @since 0.2.0
aac0ea25 901 */
0812c40e
ML
902SR_API int sr_session_source_remove_pollfd(struct sr_session *session,
903 GPollFD *pollfd)
aac0ea25 904{
102f1239 905 return _sr_session_source_remove(session, (gintptr)pollfd);
aac0ea25
LPC
906}
907
6b2d8d3e 908/**
aac0ea25
LPC
909 * Remove the source belonging to the specified channel.
910 *
7efe889e 911 * @param session The session to use. Must not be NULL.
1a895c61 912 * @param channel The channel for which the source should be removed.
aac0ea25 913 *
04cb9157
MH
914 * @retval SR_OK Success.
915 * @retval SR_ERR_ARG Invalid argument.
916 * @retval SR_ERR_MALLOC Memory allocation error.
917 * @return SR_ERR_BUG Internal error.
47117241
UH
918 *
919 * @since 0.2.0
aac0ea25 920 */
0812c40e
ML
921SR_API int sr_session_source_remove_channel(struct sr_session *session,
922 GIOChannel *channel)
aac0ea25 923{
102f1239 924 return _sr_session_source_remove(session, (gintptr)channel);
aac0ea25 925}
7b870c38
UH
926
927/** @} */