]> sigrok.org Git - libsigrok.git/blobdiff - src/session.c
session: Do not expect meaningful errno on non-UNIX
[libsigrok.git] / src / session.c
index f7d3bc8abfbd55539b5efc707d1dab5331850474..138f4222263ebc6b1027df1b92348144429951d4 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 /** @cond PRIVATE */
@@ -62,6 +63,7 @@ struct datafeed_callback {
 /**
  * Create a new session.
  *
+ * @param ctx         The context in which to create the new session.
  * @param new_session This will contain a pointer to the newly created
  *                    session if the return value is SR_OK, otherwise the value
  *                    is undefined and should not be used. Must not be NULL.
@@ -71,7 +73,8 @@ struct datafeed_callback {
  *
  * @since 0.4.0
  */
-SR_API int sr_session_new(struct sr_session **new_session)
+SR_API int sr_session_new(struct sr_context *ctx,
+               struct sr_session **new_session)
 {
        struct sr_session *session;
 
@@ -80,6 +83,7 @@ SR_API int sr_session_new(struct sr_session **new_session)
 
        session = g_malloc0(sizeof(struct sr_session));
 
+       session->ctx = ctx;
        session->source_timeout = -1;
        session->running = FALSE;
        session->abort_session = FALSE;
@@ -355,6 +359,22 @@ SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger
        return SR_OK;
 }
 
+static gboolean sr_session_check_aborted(struct sr_session *session)
+{
+       gboolean stop;
+
+       g_mutex_lock(&session->stop_mutex);
+       stop = session->abort_session;
+       if (stop) {
+               sr_session_stop_sync(session);
+               /* But once is enough. */
+               session->abort_session = FALSE;
+       }
+       g_mutex_unlock(&session->stop_mutex);
+
+       return stop;
+}
+
 /**
  * Call every device in the current session's callback.
  *
@@ -367,47 +387,85 @@ SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger
  *              sources to fire an event on the file descriptors, or
  *              any of their timeouts to activate. In other words, this
  *              can be used as a select loop.
- *              If FALSE, all sources have their callback run, regardless
- *              of file descriptor or timeout status.
- *
+ *              If FALSE, return immediately if none of the sources has
+ *              events pending.
  * @retval SR_OK Success.
- * @retval SR_ERR Error occured.
+ * @retval SR_ERR Error occurred.
  */
 static int sr_session_iteration(struct sr_session *session, gboolean block)
 {
        unsigned int i;
-       int ret;
+       int ret, timeout;
+       int revents;
+       gboolean stop_checked;
+       gboolean stopped;
+       struct source *source;
+       GPollFD *pollfd;
+#ifdef HAVE_LIBUSB_1_0
+       int usb_timeout;
+       struct timeval tv;
+#endif
+
+       timeout = block ? session->source_timeout : 0;
+
+#ifdef HAVE_LIBUSB_1_0
+       if (session->ctx->usb_source_present) {
+               ret = libusb_get_next_timeout(session->ctx->libusb_ctx, &tv);
+               if (ret < 0) {
+                       sr_err("Error getting libusb timeout: %s",
+                               libusb_error_name(ret));
+                       return SR_ERR;
+               } else if (ret == 1) {
+                       usb_timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+                       timeout = MIN(timeout, usb_timeout);
+               }
+       }
+#endif
+
+       ret = g_poll(session->pollfds, session->num_sources, timeout);
+#ifdef G_OS_UNIX
+       if (ret < 0 && errno != EINTR) {
+               sr_err("Error in poll: %s", g_strerror(errno));
+               return SR_ERR;
+       }
+#else
+       if (ret < 0) {
+               sr_err("Error in poll: %d", ret);
+               return SR_ERR;
+       }
+#endif
+       stop_checked = FALSE;
+       stopped = FALSE;
 
-       ret = g_poll(session->pollfds, session->num_sources,
-                       block ? session->source_timeout : 0);
        for (i = 0; i < session->num_sources; i++) {
-               if (session->pollfds[i].revents > 0 || (ret == 0
-                       && session->source_timeout == session->sources[i].timeout)) {
+               source = &session->sources[i];
+               pollfd = &session->pollfds[i];
+               revents = (ret > 0) ? pollfd->revents : 0;
+
+               if (revents > 0 || (ret == 0
+                       && session->source_timeout == source->timeout)) {
                        /*
                         * Invoke the source's callback on an event,
                         * or if the poll timed out and this source
                         * asked for that timeout.
                         */
-                       if (!session->sources[i].cb(session->pollfds[i].fd,
-                                       session->pollfds[i].revents,
-                                       session->sources[i].cb_data))
+                       if (!source->cb(pollfd->fd, revents, source->cb_data))
                                sr_session_source_remove(session,
-                                               session->sources[i].poll_object);
-               }
-               /*
-                * We want to take as little time as possible to stop
-                * the session if we have been told to do so. Therefore,
-                * we check the flag after processing every source, not
-                * just once per main event loop.
-                */
-               g_mutex_lock(&session->stop_mutex);
-               if (session->abort_session) {
-                       sr_session_stop_sync(session);
-                       /* But once is enough. */
-                       session->abort_session = FALSE;
+                                               source->poll_object);
+                       /*
+                        * We want to take as little time as possible to stop
+                        * the session if we have been told to do so. Therefore,
+                        * we check the flag after processing every source, not
+                        * just once per main event loop.
+                        */
+                       if (!stopped) {
+                               stopped = sr_session_check_aborted(session);
+                               stop_checked = TRUE;
+                       }
                }
-               g_mutex_unlock(&session->stop_mutex);
        }
+       if (!stop_checked)
+               sr_session_check_aborted(session);
 
        return SR_OK;
 }
@@ -447,6 +505,7 @@ static int verify_trigger(struct sr_trigger *trigger)
 
        return SR_OK;
 }
+
 /**
  * Start a session.
  *
@@ -639,16 +698,20 @@ static void datafeed_dump(const struct sr_datafeed_packet *packet)
        const struct sr_datafeed_analog *analog;
        const struct sr_datafeed_analog2 *analog2;
 
+       /* Please use the same order as in libsigrok.h. */
        switch (packet->type) {
        case SR_DF_HEADER:
                sr_dbg("bus: Received SR_DF_HEADER packet.");
                break;
-       case SR_DF_TRIGGER:
-               sr_dbg("bus: Received SR_DF_TRIGGER packet.");
+       case SR_DF_END:
+               sr_dbg("bus: Received SR_DF_END packet.");
                break;
        case SR_DF_META:
                sr_dbg("bus: Received SR_DF_META packet.");
                break;
+       case SR_DF_TRIGGER:
+               sr_dbg("bus: Received SR_DF_TRIGGER packet.");
+               break;
        case SR_DF_LOGIC:
                logic = packet->payload;
                sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
@@ -659,20 +722,17 @@ static void datafeed_dump(const struct sr_datafeed_packet *packet)
                sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
                       analog->num_samples);
                break;
-       case SR_DF_ANALOG2:
-               analog2 = packet->payload;
-               sr_dbg("bus: Received SR_DF_ANALOG2 packet (%d samples).",
-                      analog2->num_samples);
-               break;
-       case SR_DF_END:
-               sr_dbg("bus: Received SR_DF_END packet.");
-               break;
        case SR_DF_FRAME_BEGIN:
                sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
                break;
        case SR_DF_FRAME_END:
                sr_dbg("bus: Received SR_DF_FRAME_END packet.");
                break;
+       case SR_DF_ANALOG2:
+               analog2 = packet->payload;
+               sr_dbg("bus: Received SR_DF_ANALOG2 packet (%d samples).",
+                      analog2->num_samples);
+               break;
        default:
                sr_dbg("bus: Received unknown packet type: %d.", packet->type);
                break;
@@ -697,6 +757,9 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
 {
        GSList *l;
        struct datafeed_callback *cb_struct;
+       struct sr_datafeed_packet *packet_in, *packet_out;
+       struct sr_transform *t;
+       int ret;
 
        if (!sdi) {
                sr_err("%s: sdi was NULL", __func__);
@@ -713,6 +776,41 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
                return SR_ERR_BUG;
        }
 
+       /*
+        * Pass the packet to the first transform module. If that returns
+        * another packet (instead of NULL), pass that packet to the next
+        * transform module in the list, and so on.
+        */
+       packet_in = (struct sr_datafeed_packet *)packet;
+       for (l = sdi->session->transforms; l; l = l->next) {
+               t = l->data;
+               sr_spew("Running transform module '%s'.", t->module->id);
+               ret = t->module->receive(t, packet_in, &packet_out);
+               if (ret < 0) {
+                       sr_err("Error while running transform module: %d.", ret);
+                       return SR_ERR;
+               }
+               if (!packet_out) {
+                       /*
+                        * If any of the transforms don't return an output
+                        * packet, abort.
+                        */
+                       sr_spew("Transform module didn't return a packet, aborting.");
+                       return SR_OK;
+               } else {
+                       /*
+                        * Use this transform module's output packet as input
+                        * for the next transform module.
+                        */
+                       packet_in = packet_out;
+               }
+       }
+       packet = packet_in;
+
+       /*
+        * If the last transform did output a packet, pass it to all datafeed
+        * callbacks.
+        */
        for (l = sdi->session->datafeed_callbacks; l; l = l->next) {
                if (sr_log_loglevel_get() >= SR_LOG_DBG)
                        datafeed_dump(packet);
@@ -793,6 +891,7 @@ SR_API int sr_session_source_add(struct sr_session *session, int fd,
 
        p.fd = fd;
        p.events = events;
+       p.revents = 0;
 
        return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)fd);
 }
@@ -845,6 +944,7 @@ SR_API int sr_session_source_add_channel(struct sr_session *session,
 #else
        p.fd = g_io_channel_unix_get_fd(channel);
        p.events = events;
+       p.revents = 0;
 #endif
 
        return _sr_session_source_add(session, &p, timeout, cb, cb_data, (gintptr)channel);
@@ -946,15 +1046,11 @@ SR_API int sr_session_source_remove_channel(struct sr_session *session,
        return _sr_session_source_remove(session, (gintptr)channel);
 }
 
-static void *copy_src(struct sr_config *src)
+static void copy_src(struct sr_config *src, struct sr_datafeed_meta *meta_copy)
 {
-       struct sr_config *new_src;
-
-       new_src = g_malloc(sizeof(struct sr_config));
-       memcpy(new_src, src, sizeof(struct sr_config));
        g_variant_ref(src->data);
-
-       return new_src;
+       meta_copy->config = g_slist_append(meta_copy->config,
+                                          g_memdup(src, sizeof(struct sr_config)));
 }
 
 SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
@@ -983,9 +1079,8 @@ SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
                break;
        case SR_DF_META:
                meta = packet->payload;
-               meta_copy = g_malloc(sizeof(struct sr_datafeed_meta));
-               meta_copy->config = g_slist_copy_deep(meta->config,
-                               (GCopyFunc)copy_src, NULL);
+               meta_copy = g_malloc0(sizeof(struct sr_datafeed_meta));
+               g_slist_foreach(meta->config, (GFunc)copy_src, meta_copy->config);
                (*copy)->payload = meta_copy;
                break;
        case SR_DF_LOGIC: