2 * This file is part of the sigrok project.
4 * Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
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.
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.
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/>.
27 #include "sigrok-internal.h"
30 extern GIOChannel channels[2];
36 sr_receive_data_callback cb;
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;
45 static struct source *sources = NULL;
46 static int source_timeout = -1;
49 * Create a new session.
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.
57 * @return A pointer to the newly allocated session, or NULL upon errors.
59 struct sr_session *sr_session_new(void)
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? */
70 * Destroy the current session.
72 * This frees up all memory used by the session.
74 * TODO: Should return int?
76 void sr_session_destroy(void)
79 sr_warn("session: %s: session was NULL", __func__);
80 return; /* TODO: SR_ERR_BUG? */
83 g_slist_free(session->devices);
85 /* TODO: Error checks needed? */
87 /* TODO: Loop over protocol decoders and free them. */
95 * Remove all the devices from the current session. TODO?
97 * The session itself (i.e., the struct sr_session) is not free'd and still
98 * exists after this function returns.
102 void sr_session_device_clear(void)
105 sr_warn("session: %s: session was NULL", __func__);
106 return; /* TODO: SR_ERR_BUG? */
109 g_slist_free(session->devices);
110 session->devices = NULL;
114 * Add a device to the current session.
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
120 * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
122 int sr_session_device_add(struct sr_device *device)
127 sr_err("session: %s: device was NULL", __func__);
131 if (!device->plugin) {
132 sr_err("session: %s: device->plugin was NULL", __func__);
136 if (!device->plugin->opendev) {
137 sr_err("session: %s: device->plugin->opendev was NULL",
143 sr_err("session: %s: session was NULL", __func__);
144 return SR_ERR; /* TODO: SR_ERR_BUG? */
147 if ((ret = device->plugin->opendev(device->plugin_index)) != SR_OK) {
148 sr_err("session: %s: opendev failed (%d)", __func__, ret);
152 session->devices = g_slist_append(session->devices, device);
158 * Clear all datafeed callbacks in the current session.
160 * TODO: Should return int?
164 void sr_session_datafeed_callback_clear(void)
167 sr_err("session: %s: session was NULL", __func__);
168 return; /* TODO: SR_ERR_BUG? */
171 g_slist_free(session->datafeed_callbacks);
172 session->datafeed_callbacks = NULL;
176 * Add a datafeed callback to the current session.
178 * TODO: Should return int?
180 * @param callback TODO
182 void sr_session_datafeed_callback_add(sr_datafeed_callback callback)
185 sr_err("session: %s: session was NULL", __func__);
186 return; /* TODO: SR_ERR_BUG? */
189 /* TODO: Is 'callback' allowed to be NULL? */
191 session->datafeed_callbacks =
192 g_slist_append(session->datafeed_callbacks, callback);
198 static void sr_session_run_poll(void)
200 GPollFD *fds, my_gpollfd;
204 while (session->running) {
208 /* Construct g_poll()'s array. */
209 /* TODO: Check malloc return value. */
210 fds = malloc(sizeof(GPollFD) * num_sources);
211 for (i = 0; i < num_sources; i++) {
213 g_io_channel_win32_make_pollfd(&channels[0],
214 sources[i].events, &my_gpollfd);
216 my_gpollfd.fd = sources[i].fd;
217 my_gpollfd.events = sources[i].events;
222 ret = g_poll(fds, num_sources, source_timeout);
224 for (i = 0; i < num_sources; i++) {
225 if (fds[i].revents > 0 || (ret == 0
226 && source_timeout == sources[i].timeout)) {
228 * Invoke the source's callback on an event,
229 * or if the poll timeout out and this source
230 * asked for that timeout.
232 if (!sources[i].cb(fds[i].fd, fds[i].revents,
233 sources[i].user_data))
234 sr_session_source_remove(sources[i].fd);
244 * There can only be one session at a time. TODO
246 * @return SR_OK upon success, SR_ERR upon errors.
248 int sr_session_start(void)
250 struct sr_device *device;
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? */
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? */
267 /* TODO: Check plugin_index validity? */
269 sr_info("session: starting");
271 for (l = session->devices; l; l = l->next) {
273 /* TODO: Check for device != NULL. */
274 if ((ret = device->plugin->start_acquisition(
275 device->plugin_index, device)) != SR_OK) {
276 sr_err("session: %s: could not start an acquisition "
277 "(%d)", __func__, ret);
282 /* TODO: What if there are multiple devices? Which return code? */
290 * TODO: Should return int.
291 * TODO: Various error checks etc.
295 void sr_session_run(void)
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? */
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? */
310 sr_info("session: running");
311 session->running = TRUE;
313 /* Do we have real sources? */
314 if (num_sources == 1 && sources[0].fd == -1) {
315 /* Dummy source, freewheel over it. */
316 while (session->running)
317 sources[0].cb(-1, 0, sources[0].user_data);
319 /* Real sources, use g_poll() main loop. */
320 sr_session_run_poll();
323 /* TODO: return SR_OK; */
327 * Halt the current session.
333 void sr_session_halt(void)
336 sr_err("session: %s: session was NULL", __func__);
337 return; /* TODO: SR_ERR; or SR_ERR_BUG? */
340 sr_info("session: halting");
341 session->running = FALSE;
343 /* TODO: return SR_OK; */
347 * Stop the current session.
349 * TODO: Difference to halt?
353 void sr_session_stop(void)
355 struct sr_device *device;
359 sr_err("session: %s: session was NULL", __func__);
360 return; /* TODO: SR_ERR or SR_ERR_BUG? */
363 sr_info("session: stopping");
364 session->running = FALSE;
365 for (l = session->devices; l; l = l->next) {
367 if (device->plugin && device->plugin->stop_acquisition)
368 device->plugin->stop_acquisition(device->plugin_index, device);
371 /* TODO: return SR_OK; */
377 * TODO: Should return int?
378 * TODO: Various error checks.
380 * @param packet TODO.
382 static void datafeed_dump(struct sr_datafeed_packet *packet)
384 struct sr_datafeed_logic *logic;
386 switch (packet->type) {
388 sr_dbg("bus: received SR_DF_HEADER");
391 sr_dbg("bus: received SR_DF_TRIGGER at %lu ms",
392 packet->timeoffset / 1000000);
395 logic = packet->payload;
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);
401 sr_dbg("bus: received SR_DF_END");
404 /* TODO: sr_err() and abort? */
405 sr_dbg("bus: received unknown packet type %d", packet->type);
413 * TODO: Should return int?
415 * @param device TODO.
416 * @param packet TODO.
418 void sr_session_bus(struct sr_device *device, struct sr_datafeed_packet *packet)
421 sr_datafeed_callback cb;
424 sr_err("session: %s: device was NULL", __func__);
425 return; /* TODO: SR_ERR_ARG */
428 if (!device->plugin) {
429 sr_err("session: %s: device->plugin was NULL", __func__);
430 return; /* TODO: SR_ERR_ARG */
434 * TODO: Send packet through PD pipe, and send the output of that to
435 * the callbacks as well.
437 for (l = session->datafeed_callbacks; l; l = l->next) {
439 /* TODO: Check for cb != NULL. */
440 datafeed_dump(packet);
444 /* TODO: return SR_OK; */
450 * TODO: Should return int?
451 * TODO: Switch to g_try_malloc0() / g_free().
452 * TODO: More error checks etc.
455 * @param events TODO.
456 * @param timeout TODO.
457 * @param callback TODO.
458 * @param user_data TODO.
460 void sr_session_source_add(int fd, int events, int timeout,
461 sr_receive_data_callback callback, void *user_data)
463 struct source *new_sources, *s;
466 sr_err("session: %s: callback was NULL", __func__);
467 return; /* TODO: SR_ERR_ARG */
470 /* Note: user_data can be NULL, that's not a bug. */
472 new_sources = calloc(1, sizeof(struct source) * (num_sources + 1));
474 sr_err("session: %s: new_sources malloc failed", __func__);
475 return; /* TODO: SR_ERR_MALLOC */
479 memcpy(new_sources, sources,
480 sizeof(struct source) * num_sources);
484 s = &new_sources[num_sources++];
487 s->timeout = timeout;
489 s->user_data = user_data;
490 sources = new_sources;
492 if (timeout != source_timeout && timeout > 0
493 && (source_timeout == -1 || timeout < source_timeout))
494 source_timeout = timeout;
496 /* TODO: return SR_OK; */
500 * Remove the source belonging to the specified file descriptor.
502 * TODO: Should return int.
503 * TODO: More error checks.
504 * TODO: Switch to g_try_malloc0() / g_free().
508 void sr_session_source_remove(int fd)
510 struct source *new_sources;
517 new_sources = calloc(1, sizeof(struct source) * num_sources);
519 sr_err("session: %s: new_sources malloc failed", __func__);
520 return; /* TODO: SR_ERR_MALLOC */
523 for (old = 0, new = 0; old < num_sources; old++) {
524 if (sources[old].fd != fd)
525 memcpy(&new_sources[new++], &sources[old],
526 sizeof(struct source));
531 sources = new_sources;
534 /* Target fd was not found. */