libsigrok
session.c
Go to the documentation of this file.
00001 /*
00002  * This file is part of the sigrok project.
00003  *
00004  * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
00005  *
00006  * This program is free software: you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation, either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <unistd.h>
00023 #include <string.h>
00024 #include <glib.h>
00025 #include "sigrok.h"
00026 #include "sigrok-internal.h"
00027 
00028 /* demo.c. TODO: Should not be global! */
00029 extern SR_PRIV GIOChannel channels[2];
00030 
00031 struct source {
00032         int fd;
00033         int events;
00034         int timeout;
00035         sr_receive_data_callback_t cb;
00036         void *cb_data;
00037 };
00038 
00039 /* There can only be one session at a time. */
00040 /* 'session' is not static, it's used elsewhere (via 'extern'). */
00041 struct sr_session *session;
00042 static int num_sources = 0;
00043 
00044 static struct source *sources = NULL;
00045 static int source_timeout = -1;
00046 
00047 /**
00048  * Create a new session.
00049  *
00050  * TODO: Should it use the file-global "session" variable or take an argument?
00051  *       The same question applies to all the other session functions.
00052  *
00053  * @return A pointer to the newly allocated session, or NULL upon errors.
00054  */
00055 SR_API struct sr_session *sr_session_new(void)
00056 {
00057         if (!(session = g_try_malloc0(sizeof(struct sr_session)))) {
00058                 sr_err("session: %s: session malloc failed", __func__);
00059                 return NULL; /* TODO: SR_ERR_MALLOC? */
00060         }
00061 
00062         return session;
00063 }
00064 
00065 /**
00066  * Destroy the current session.
00067  *
00068  * This frees up all memory used by the session.
00069  *
00070  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
00071  */
00072 SR_API int sr_session_destroy(void)
00073 {
00074         if (!session) {
00075                 sr_err("session: %s: session was NULL", __func__);
00076                 return SR_ERR_BUG;
00077         }
00078 
00079         g_slist_free(session->devs);
00080         session->devs = NULL;
00081 
00082         /* TODO: Error checks needed? */
00083 
00084         /* TODO: Loop over protocol decoders and free them. */
00085 
00086         g_free(session);
00087         session = NULL;
00088 
00089         return SR_OK;
00090 }
00091 
00092 /**
00093  * Remove all the devices from the current session. TODO?
00094  *
00095  * The session itself (i.e., the struct sr_session) is not free'd and still
00096  * exists after this function returns.
00097  *
00098  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
00099  */
00100 SR_API int sr_session_dev_remove_all(void)
00101 {
00102         if (!session) {
00103                 sr_err("session: %s: session was NULL", __func__);
00104                 return SR_ERR_BUG;
00105         }
00106 
00107         g_slist_free(session->devs);
00108         session->devs = NULL;
00109 
00110         return SR_OK;
00111 }
00112 
00113 /**
00114  * Add a device to the current session.
00115  *
00116  * @param dev The device to add to the current session. Must not be NULL.
00117  *            Also, dev->driver and dev->driver->dev_open must not be NULL.
00118  *
00119  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
00120  */
00121 SR_API int sr_session_dev_add(struct sr_dev *dev)
00122 {
00123         int ret;
00124 
00125         if (!dev) {
00126                 sr_err("session: %s: dev was NULL", __func__);
00127                 return SR_ERR_ARG;
00128         }
00129 
00130         if (!session) {
00131                 sr_err("session: %s: session was NULL", __func__);
00132                 return SR_ERR_BUG;
00133         }
00134 
00135         /* If dev->driver is NULL, this is a virtual device. */
00136         if (!dev->driver) {
00137                 sr_dbg("session: %s: dev->driver was NULL, this seems to be "
00138                        "a virtual device; continuing", __func__);
00139                 /* Just add the device, don't run dev_open(). */
00140                 session->devs = g_slist_append(session->devs, dev);
00141                 return SR_OK;
00142         }
00143 
00144         /* dev->driver is non-NULL (i.e. we have a real device). */
00145         if (!dev->driver->dev_open) {
00146                 sr_err("session: %s: dev->driver->dev_open was NULL",
00147                        __func__);
00148                 return SR_ERR_BUG;
00149         }
00150 
00151         if ((ret = dev->driver->dev_open(dev->driver_index)) != SR_OK) {
00152                 sr_err("session: %s: dev_open failed (%d)", __func__, ret);
00153                 return ret;
00154         }
00155 
00156         session->devs = g_slist_append(session->devs, dev);
00157 
00158         return SR_OK;
00159 }
00160 
00161 /**
00162  * Remove all datafeed callbacks in the current session.
00163  *
00164  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
00165  */
00166 SR_API int sr_session_datafeed_callback_remove_all(void)
00167 {
00168         if (!session) {
00169                 sr_err("session: %s: session was NULL", __func__);
00170                 return SR_ERR_BUG;
00171         }
00172 
00173         g_slist_free(session->datafeed_callbacks);
00174         session->datafeed_callbacks = NULL;
00175 
00176         return SR_OK;
00177 }
00178 
00179 /**
00180  * Add a datafeed callback to the current session.
00181  *
00182  * @param cb Function to call when a chunk of data is received.
00183  *           Must not be NULL.
00184  *
00185  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
00186  */
00187 SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback_t cb)
00188 {
00189         if (!session) {
00190                 sr_err("session: %s: session was NULL", __func__);
00191                 return SR_ERR_BUG;
00192         }
00193 
00194         if (!cb) {
00195                 sr_err("session: %s: cb was NULL", __func__);
00196                 return SR_ERR_ARG;
00197         }
00198 
00199         session->datafeed_callbacks =
00200             g_slist_append(session->datafeed_callbacks, cb);
00201 
00202         return SR_OK;
00203 }
00204 
00205 /**
00206  * TODO.
00207  */
00208 static int sr_session_run_poll(void)
00209 {
00210         GPollFD *fds, my_gpollfd;
00211         int ret, i;
00212 
00213         fds = NULL;
00214         while (session->running) {
00215                 /* TODO: Add comment. */
00216                 g_free(fds);
00217 
00218                 /* Construct g_poll()'s array. */
00219                 if (!(fds = g_try_malloc(sizeof(GPollFD) * num_sources))) {
00220                         sr_err("session: %s: fds malloc failed", __func__);
00221                         return SR_ERR_MALLOC;
00222                 }
00223                 for (i = 0; i < num_sources; i++) {
00224 #ifdef _WIN32
00225                         g_io_channel_win32_make_pollfd(&channels[0],
00226                                         sources[i].events, &my_gpollfd);
00227 #else
00228                         my_gpollfd.fd = sources[i].fd;
00229                         my_gpollfd.events = sources[i].events;
00230                         fds[i] = my_gpollfd;
00231 #endif
00232                 }
00233 
00234                 ret = g_poll(fds, num_sources, source_timeout);
00235 
00236                 for (i = 0; i < num_sources; i++) {
00237                         if (fds[i].revents > 0 || (ret == 0
00238                                 && source_timeout == sources[i].timeout)) {
00239                                 /*
00240                                  * Invoke the source's callback on an event,
00241                                  * or if the poll timeout out and this source
00242                                  * asked for that timeout.
00243                                  */
00244                                 if (!sources[i].cb(fds[i].fd, fds[i].revents,
00245                                                   sources[i].cb_data))
00246                                         sr_session_source_remove(sources[i].fd);
00247                         }
00248                 }
00249         }
00250         g_free(fds);
00251 
00252         return SR_OK;
00253 }
00254 
00255 /**
00256  * Start a session.
00257  *
00258  * There can only be one session at a time.
00259  *
00260  * @return SR_OK upon success, SR_ERR upon errors.
00261  */
00262 SR_API int sr_session_start(void)
00263 {
00264         struct sr_dev *dev;
00265         GSList *l;
00266         int ret;
00267 
00268         if (!session) {
00269                 sr_err("session: %s: session was NULL; a session must be "
00270                        "created first, before starting it.", __func__);
00271                 return SR_ERR_BUG;
00272         }
00273 
00274         if (!session->devs) {
00275                 /* TODO: Actually the case? */
00276                 sr_err("session: %s: session->devs was NULL; a session "
00277                        "cannot be started without devices.", __func__);
00278                 return SR_ERR_BUG;
00279         }
00280 
00281         /* TODO: Check driver_index validity? */
00282 
00283         sr_info("session: starting");
00284 
00285         for (l = session->devs; l; l = l->next) {
00286                 dev = l->data;
00287                 /* TODO: Check for dev != NULL. */
00288                 if ((ret = dev->driver->dev_acquisition_start(
00289                                 dev->driver_index, dev)) != SR_OK) {
00290                         sr_err("session: %s: could not start an acquisition "
00291                                "(%d)", __func__, ret);
00292                         break;
00293                 }
00294         }
00295 
00296         /* TODO: What if there are multiple devices? Which return code? */
00297 
00298         return ret;
00299 }
00300 
00301 /**
00302  * Run the session.
00303  *
00304  * TODO: Various error checks etc.
00305  *
00306  * @return SR_OK upon success, SR_ERR_BUG upon errors.
00307  */
00308 SR_API int sr_session_run(void)
00309 {
00310         if (!session) {
00311                 sr_err("session: %s: session was NULL; a session must be "
00312                        "created first, before running it.", __func__);
00313                 return SR_ERR_BUG;
00314         }
00315 
00316         if (!session->devs) {
00317                 /* TODO: Actually the case? */
00318                 sr_err("session: %s: session->devs was NULL; a session "
00319                        "cannot be run without devices.", __func__);
00320                 return SR_ERR_BUG;
00321         }
00322 
00323         sr_info("session: running");
00324         session->running = TRUE;
00325 
00326         /* Do we have real sources? */
00327         if (num_sources == 1 && sources[0].fd == -1) {
00328                 /* Dummy source, freewheel over it. */
00329                 while (session->running)
00330                         sources[0].cb(-1, 0, sources[0].cb_data);
00331         } else {
00332                 /* Real sources, use g_poll() main loop. */
00333                 sr_session_run_poll();
00334         }
00335 
00336         return SR_OK;
00337 }
00338 
00339 /**
00340  * Halt the current session.
00341  *
00342  * This requests the current session be stopped as soon as possible, for
00343  * example on receiving an SR_DF_END packet.
00344  *
00345  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
00346  */
00347 SR_API int sr_session_halt(void)
00348 {
00349         if (!session) {
00350                 sr_err("session: %s: session was NULL", __func__);
00351                 return SR_ERR_BUG;
00352         }
00353 
00354         sr_info("session: halting");
00355         session->running = FALSE;
00356 
00357         return SR_OK;
00358 }
00359 
00360 /**
00361  * Stop the current session.
00362  *
00363  * The current session is stopped immediately, with all acquisition sessions
00364  * being stopped and hardware drivers cleaned up.
00365  *
00366  * @return SR_OK upon success, SR_ERR_BUG if no session exists.
00367  */
00368 SR_API int sr_session_stop(void)
00369 {
00370         struct sr_dev *dev;
00371         GSList *l;
00372 
00373         if (!session) {
00374                 sr_err("session: %s: session was NULL", __func__);
00375                 return SR_ERR_BUG;
00376         }
00377 
00378         sr_info("session: stopping");
00379         session->running = FALSE;
00380 
00381         for (l = session->devs; l; l = l->next) {
00382                 dev = l->data;
00383                 /* Check for dev != NULL. */
00384                 if (dev->driver) {
00385                         if (dev->driver->dev_acquisition_stop)
00386                                 dev->driver->dev_acquisition_stop(dev->driver_index, dev);
00387                         if (dev->driver->cleanup)
00388                                 dev->driver->cleanup();
00389                 }
00390         }
00391 
00392         return SR_OK;
00393 }
00394 
00395 /**
00396  * Debug helper.
00397  *
00398  * @param packet The packet to show debugging information for.
00399  */
00400 static void datafeed_dump(struct sr_datafeed_packet *packet)
00401 {
00402         struct sr_datafeed_logic *logic;
00403 
00404         switch (packet->type) {
00405         case SR_DF_HEADER:
00406                 sr_dbg("bus: received SR_DF_HEADER");
00407                 break;
00408         case SR_DF_TRIGGER:
00409                 sr_dbg("bus: received SR_DF_TRIGGER");
00410                 break;
00411         case SR_DF_LOGIC:
00412                 logic = packet->payload;
00413                 /* TODO: Check for logic != NULL. */
00414                 sr_dbg("bus: received SR_DF_LOGIC %" PRIu64 " bytes", logic->length);
00415                 break;
00416         case SR_DF_END:
00417                 sr_dbg("bus: received SR_DF_END");
00418                 break;
00419         default:
00420                 sr_dbg("bus: received unknown packet type %d", packet->type);
00421                 break;
00422         }
00423 }
00424 
00425 /**
00426  * Send a packet to whatever is listening on the datafeed bus.
00427  *
00428  * Hardware drivers use this to send a data packet to the frontend.
00429  *
00430  * @param dev TODO.
00431  * @param packet The datafeed packet to send to the session bus.
00432  *
00433  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
00434  */
00435 SR_PRIV int sr_session_send(struct sr_dev *dev,
00436                             struct sr_datafeed_packet *packet)
00437 {
00438         GSList *l;
00439         sr_datafeed_callback_t cb;
00440 
00441         if (!dev) {
00442                 sr_err("session: %s: dev was NULL", __func__);
00443                 return SR_ERR_ARG;
00444         }
00445 
00446         if (!packet) {
00447                 sr_err("session: %s: packet was NULL", __func__);
00448                 return SR_ERR_ARG;
00449         }
00450 
00451         for (l = session->datafeed_callbacks; l; l = l->next) {
00452                 if (sr_log_loglevel_get() >= SR_LOG_DBG)
00453                         datafeed_dump(packet);
00454                 cb = l->data;
00455                 /* TODO: Check for cb != NULL. */
00456                 cb(dev, packet);
00457         }
00458 
00459         return SR_OK;
00460 }
00461 
00462 /**
00463  * TODO.
00464  *
00465  * TODO: More error checks etc.
00466  *
00467  * @param fd TODO.
00468  * @param events TODO.
00469  * @param timeout TODO.
00470  * @param cb Callback function to add. Must not be NULL.
00471  * @param cb_data Data for the callback function. Can be NULL.
00472  *
00473  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
00474  *         SR_ERR_MALLOC upon memory allocation errors.
00475  */
00476 SR_API int sr_session_source_add(int fd, int events, int timeout,
00477                 sr_receive_data_callback_t cb, void *cb_data)
00478 {
00479         struct source *new_sources, *s;
00480 
00481         if (!cb) {
00482                 sr_err("session: %s: cb was NULL", __func__);
00483                 return SR_ERR_ARG;
00484         }
00485 
00486         /* Note: cb_data can be NULL, that's not a bug. */
00487 
00488         new_sources = g_try_malloc0(sizeof(struct source) * (num_sources + 1));
00489         if (!new_sources) {
00490                 sr_err("session: %s: new_sources malloc failed", __func__);
00491                 return SR_ERR_MALLOC;
00492         }
00493 
00494         if (sources) {
00495                 memcpy(new_sources, sources,
00496                        sizeof(struct source) * num_sources);
00497                 g_free(sources);
00498         }
00499 
00500         s = &new_sources[num_sources++];
00501         s->fd = fd;
00502         s->events = events;
00503         s->timeout = timeout;
00504         s->cb = cb;
00505         s->cb_data = cb_data;
00506         sources = new_sources;
00507 
00508         if (timeout != source_timeout && timeout > 0
00509             && (source_timeout == -1 || timeout < source_timeout))
00510                 source_timeout = timeout;
00511 
00512         return SR_OK;
00513 }
00514 
00515 /**
00516  * Remove the source belonging to the specified file descriptor.
00517  *
00518  * TODO: More error checks.
00519  *
00520  * @param fd TODO.
00521  *
00522  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
00523  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
00524  *         internal errors.
00525  */
00526 SR_API int sr_session_source_remove(int fd)
00527 {
00528         struct source *new_sources;
00529         int old, new;
00530 
00531         if (!sources) {
00532                 sr_err("session: %s: sources was NULL", __func__);
00533                 return SR_ERR_BUG;
00534         }
00535 
00536         /* TODO: Check if 'fd' valid. */
00537 
00538         new_sources = g_try_malloc0(sizeof(struct source) * num_sources);
00539         if (!new_sources) {
00540                 sr_err("session: %s: new_sources malloc failed", __func__);
00541                 return SR_ERR_MALLOC;
00542         }
00543 
00544         for (old = 0, new = 0; old < num_sources; old++) {
00545                 if (sources[old].fd != fd)
00546                         memcpy(&new_sources[new++], &sources[old],
00547                                sizeof(struct source));
00548         }
00549 
00550         if (old != new) {
00551                 g_free(sources);
00552                 sources = new_sources;
00553                 num_sources--;
00554         } else {
00555                 /* Target fd was not found. */
00556                 g_free(new_sources);
00557         }
00558 
00559         return SR_OK;
00560 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines