]> sigrok.org Git - libsigrok.git/blobdiff - src/session.c
Don't set _POSIX_C_SOURCE for VXI/RPC related files.
[libsigrok.git] / src / session.c
index d8a3b00b5efabe157ab546498bee39139b249853..158b33cf856a21e8f547982a4742d9cab918b4e2 100644 (file)
@@ -22,7 +22,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 /** @cond PRIVATE */
@@ -61,19 +61,28 @@ struct datafeed_callback {
 
 /**
  * Create a new session.
- * Currently, there can be only one session at a time within the same process.
+ *
+ * @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.
  *
  * @retval SR_OK Success.
- * @retval SR_ERR_BUG A session exists already.
+ * @retval SR_ERR_ARG Invalid argument.
  *
  * @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;
 
+       if (!new_session)
+               return SR_ERR_ARG;
+
        session = g_malloc0(sizeof(struct sr_session));
 
+       session->ctx = ctx;
        session->source_timeout = -1;
        session->running = FALSE;
        session->abort_session = FALSE;
@@ -88,6 +97,8 @@ SR_API int sr_session_new(struct sr_session **new_session)
  * Destroy a session.
  * This frees up all memory used by the session.
  *
+ * @param session The session to destroy. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid session passed.
  *
@@ -105,6 +116,8 @@ SR_API int sr_session_destroy(struct sr_session *session)
        if (session->trigger)
                sr_trigger_free(session->trigger);
 
+       g_slist_free_full(session->owned_devs, (GDestroyNotify)sr_dev_inst_free);
+
        g_free(session);
 
        return SR_OK;
@@ -116,6 +129,8 @@ SR_API int sr_session_destroy(struct sr_session *session)
  * The session itself (i.e., the struct sr_session) is not free'd and still
  * exists after this function returns.
  *
+ * @param session The session to use. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_BUG Invalid session passed.
  *
@@ -145,6 +160,7 @@ SR_API int sr_session_dev_remove_all(struct sr_session *session)
 /**
  * Add a device instance to a session.
  *
+ * @param session The session to add to. Must not be NULL.
  * @param sdi The device instance to add to a session. Must not
  *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
  *            not be NULL.
@@ -178,8 +194,6 @@ SR_API int sr_session_dev_add(struct sr_session *session,
 
        /* If sdi->driver is NULL, this is a virtual device. */
        if (!sdi->driver) {
-               sr_dbg("%s: sdi->driver was NULL, this seems to be "
-                      "a virtual device; continuing", __func__);
                /* Just add the device, don't run dev_open(). */
                session->devs = g_slist_append(session->devs, (gpointer)sdi);
                sdi->session = session;
@@ -218,6 +232,7 @@ SR_API int sr_session_dev_add(struct sr_session *session,
 /**
  * List all device instances attached to a session.
  *
+ * @param session The session to use. Must not be NULL.
  * @param devlist A pointer where the device instance list will be
  *                stored on return. If no devices are in the session,
  *                this will be NULL. Each element in the list points
@@ -246,6 +261,8 @@ SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist)
 /**
  * Remove all datafeed callbacks in a session.
  *
+ * @param session The session to use. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid session passed.
  *
@@ -267,6 +284,7 @@ SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session)
 /**
  * Add a datafeed callback to a session.
  *
+ * @param session The session to use. Must not be NULL.
  * @param cb Function to call when a chunk of data is received.
  *           Must not be NULL.
  * @param cb_data Opaque pointer passed in by the caller.
@@ -291,9 +309,7 @@ SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
                return SR_ERR_ARG;
        }
 
-       if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
-               return SR_ERR_MALLOC;
-
+       cb_struct = g_malloc0(sizeof(struct datafeed_callback));
        cb_struct->cb = cb;
        cb_struct->cb_data = cb_data;
 
@@ -303,13 +319,40 @@ SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
        return SR_OK;
 }
 
+/**
+ * Get the trigger assigned to this session.
+ *
+ * @param session The session to use.
+ *
+ * @retval NULL Invalid (NULL) session was passed to the function.
+ * @retval other The trigger assigned to this session (can be NULL).
+ *
+ * @since 0.4.0
+ */
 SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session)
 {
+       if (!session)
+               return NULL;
+
        return session->trigger;
 }
 
+/**
+ * Set the trigger of this session.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param trig The trigger to assign to this session. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
 SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig)
 {
+       if (!session)
+               return SR_ERR_ARG;
+
        session->trigger = trig;
 
        return SR_OK;
@@ -322,6 +365,7 @@ SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger
  * but driven by another scheduler, this can be used to poll the devices
  * from within that scheduler.
  *
+ * @param session The session to use. Must not be NULL.
  * @param block If TRUE, this call will wait for any of the session's
  *              sources to fire an event on the file descriptors, or
  *              any of their timeouts to activate. In other words, this
@@ -330,15 +374,35 @@ SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger
  *              of file descriptor or timeout status.
  *
  * @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;
+#ifdef HAVE_LIBUSB_1_0
+       int usb_timeout;
+       struct timeval tv;
+#endif
+
+       timeout = block ? session->source_timeout : 0;
 
-       ret = g_poll(session->pollfds, session->num_sources,
-                       block ? session->source_timeout : 0);
+#ifdef HAVE_LIBUSB_1_0
+       if (session->ctx->usb_source_present) {
+               timeout = block ? 0 : session->source_timeout;
+               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);
        for (i = 0; i < session->num_sources; i++) {
                if (session->pollfds[i].revents > 0 || (ret == 0
                        && session->source_timeout == session->sources[i].timeout)) {
@@ -371,7 +435,6 @@ static int sr_session_iteration(struct sr_session *session, gboolean block)
        return SR_OK;
 }
 
-
 static int verify_trigger(struct sr_trigger *trigger)
 {
        struct sr_trigger_stage *stage;
@@ -407,9 +470,12 @@ static int verify_trigger(struct sr_trigger *trigger)
 
        return SR_OK;
 }
+
 /**
  * Start a session.
  *
+ * @param session The session to use. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid session passed.
  *
@@ -418,8 +484,9 @@ static int verify_trigger(struct sr_trigger *trigger)
 SR_API int sr_session_start(struct sr_session *session)
 {
        struct sr_dev_inst *sdi;
-       GSList *l;
-       int ret;
+       struct sr_channel *ch;
+       GSList *l, *c;
+       int enabled_channels, ret;
 
        if (!session) {
                sr_err("%s: session was NULL", __func__);
@@ -440,6 +507,21 @@ SR_API int sr_session_start(struct sr_session *session)
        ret = SR_OK;
        for (l = session->devs; l; l = l->next) {
                sdi = l->data;
+               enabled_channels = 0;
+               for (c = sdi->channels; c; c = c->next) {
+                       ch = c->data;
+                       if (ch->enabled) {
+                               enabled_channels++;
+                               break;
+                       }
+               }
+               if (enabled_channels == 0) {
+                       ret = SR_ERR;
+                       sr_err("%s using connection %s has no enabled channels!",
+                                       sdi->driver->name, sdi->connection_id);
+                       break;
+               }
+
                if ((ret = sr_config_commit(sdi)) != SR_OK) {
                        sr_err("Failed to commit device settings before "
                               "starting acquisition (%s)", sr_strerror(ret));
@@ -460,6 +542,8 @@ SR_API int sr_session_start(struct sr_session *session)
 /**
  * Run a session.
  *
+ * @param session The session to use. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid session passed.
  *
@@ -505,6 +589,8 @@ SR_API int sr_session_run(struct sr_session *session)
  * This must be called from within the session thread, to prevent freeing
  * resources that the session thread will try to use.
  *
+ * @param session The session to use. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid session passed.
  *
@@ -545,6 +631,8 @@ SR_PRIV int sr_session_stop_sync(struct sr_session *session)
  * to wait for the session thread to return before assuming that the session is
  * completely decommissioned.
  *
+ * @param session The session to use. Must not be NULL.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid session passed.
  *
@@ -573,17 +661,22 @@ static void datafeed_dump(const struct sr_datafeed_packet *packet)
 {
        const struct sr_datafeed_logic *logic;
        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, "
@@ -594,15 +687,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_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;
@@ -623,10 +718,13 @@ static void datafeed_dump(const struct sr_datafeed_packet *packet)
  * @private
  */
 SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
-                           const struct sr_datafeed_packet *packet)
+               const struct sr_datafeed_packet *packet)
 {
        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__);
@@ -638,6 +736,45 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
                return SR_ERR_ARG;
        }
 
+       if (!sdi->session) {
+               sr_err("%s: session was NULL", __func__);
+               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;
+               }
+       }
+
+       /*
+        * 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);
@@ -651,6 +788,7 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
 /**
  * Add an event source for a file descriptor.
  *
+ * @param session The session to use. Must not be NULL.
  * @param pollfd The GPollFD.
  * @param[in] timeout Max time to wait before the callback is called,
  *              ignored if 0.
@@ -660,7 +798,6 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
  *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
  */
 static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
                int timeout, sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
@@ -675,19 +812,10 @@ static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
 
        /* Note: cb_data can be NULL, that's not a bug. */
 
-       new_pollfds = g_try_realloc(session->pollfds,
+       new_pollfds = g_realloc(session->pollfds,
                        sizeof(GPollFD) * (session->num_sources + 1));
-       if (!new_pollfds) {
-               sr_err("%s: new_pollfds malloc failed", __func__);
-               return SR_ERR_MALLOC;
-       }
-
-       new_sources = g_try_realloc(session->sources, sizeof(struct source) *
+       new_sources = g_realloc(session->sources, sizeof(struct source) *
                        (session->num_sources + 1));
-       if (!new_sources) {
-               sr_err("%s: new_sources malloc failed", __func__);
-               return SR_ERR_MALLOC;
-       }
 
        new_pollfds[session->num_sources] = *pollfd;
        s = &new_sources[session->num_sources++];
@@ -708,6 +836,7 @@ static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
 /**
  * Add an event source for a file descriptor.
  *
+ * @param session The session to use. Must not be NULL.
  * @param fd The file descriptor.
  * @param events Events to check for.
  * @param timeout Max time to wait before the callback is called, ignored if 0.
@@ -716,7 +845,6 @@ static int _sr_session_source_add(struct sr_session *session, GPollFD *pollfd,
  *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
  *
  * @since 0.3.0
  */
@@ -725,8 +853,6 @@ SR_API int sr_session_source_add(struct sr_session *session, int fd,
 {
        GPollFD p;
 
-       (void) session;
-
        p.fd = fd;
        p.events = events;
 
@@ -736,6 +862,7 @@ SR_API int sr_session_source_add(struct sr_session *session, int fd,
 /**
  * Add an event source for a GPollFD.
  *
+ * @param session The session to use. Must not be NULL.
  * @param pollfd The GPollFD.
  * @param timeout Max time to wait before the callback is called, ignored if 0.
  * @param cb Callback function to add. Must not be NULL.
@@ -743,7 +870,6 @@ SR_API int sr_session_source_add(struct sr_session *session, int fd,
  *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
  *
  * @since 0.3.0
  */
@@ -751,15 +877,14 @@ SR_API int sr_session_source_add_pollfd(struct sr_session *session,
                GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
                void *cb_data)
 {
-       (void) session;
-
        return _sr_session_source_add(session, pollfd, timeout, cb,
-                                     cb_data, (gintptr)pollfd);
+                       cb_data, (gintptr)pollfd);
 }
 
 /**
  * Add an event source for a GIOChannel.
  *
+ * @param session The session to use. Must not be NULL.
  * @param channel The GIOChannel.
  * @param events Events to poll on.
  * @param timeout Max time to wait before the callback is called, ignored if 0.
@@ -768,7 +893,6 @@ SR_API int sr_session_source_add_pollfd(struct sr_session *session,
  *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
  *
  * @since 0.3.0
  */
@@ -778,8 +902,6 @@ SR_API int sr_session_source_add_channel(struct sr_session *session,
 {
        GPollFD p;
 
-       (void) session;
-
 #ifdef _WIN32
        g_io_channel_win32_make_pollfd(channel, events, &p);
 #else
@@ -793,19 +915,15 @@ SR_API int sr_session_source_add_channel(struct sr_session *session,
 /**
  * Remove the source belonging to the specified channel.
  *
- * @todo Add more error checks and logging.
- *
+ * @param session The session to use. Must not be NULL.
  * @param poll_object The channel for which the source should be removed.
  *
  * @retval SR_OK Success
  * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR_MALLOC Memory allocation error
  * @retval SR_ERR_BUG Internal error
  */
 static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object)
 {
-       struct source *new_sources;
-       GPollFD *new_pollfds;
        unsigned int old;
 
        if (!session->sources || !session->num_sources) {
@@ -822,29 +940,17 @@ static int _sr_session_source_remove(struct sr_session *session, gintptr poll_ob
        if (old == session->num_sources)
                return SR_OK;
 
-       session->num_sources -= 1;
+       session->num_sources--;
 
        if (old != session->num_sources) {
-               memmove(&session->pollfds[old], &session->pollfds[old+1],
+               memmove(&session->pollfds[old], &session->pollfds[old + 1],
                        (session->num_sources - old) * sizeof(GPollFD));
-               memmove(&session->sources[old], &session->sources[old+1],
+               memmove(&session->sources[old], &session->sources[old + 1],
                        (session->num_sources - old) * sizeof(struct source));
        }
 
-       new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
-       if (!new_pollfds && session->num_sources > 0) {
-               sr_err("%s: new_pollfds malloc failed", __func__);
-               return SR_ERR_MALLOC;
-       }
-
-       new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
-       if (!new_sources && session->num_sources > 0) {
-               sr_err("%s: new_sources malloc failed", __func__);
-               return SR_ERR_MALLOC;
-       }
-
-       session->pollfds = new_pollfds;
-       session->sources = new_sources;
+       session->pollfds = g_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
+       session->sources = g_realloc(session->sources, sizeof(struct source) * session->num_sources);
 
        return SR_OK;
 }
@@ -852,25 +958,24 @@ static int _sr_session_source_remove(struct sr_session *session, gintptr poll_ob
 /**
  * Remove the source belonging to the specified file descriptor.
  *
+ * @param session The session to use. Must not be NULL.
  * @param fd The file descriptor for which the source should be removed.
  *
  * @retval SR_OK Success
  * @retval SR_ERR_ARG Invalid argument
- * @retval SR_ERR_MALLOC Memory allocation error.
  * @retval SR_ERR_BUG Internal error.
  *
  * @since 0.3.0
  */
 SR_API int sr_session_source_remove(struct sr_session *session, int fd)
 {
-       (void) session;
-
        return _sr_session_source_remove(session, (gintptr)fd);
 }
 
 /**
  * Remove the source belonging to the specified poll descriptor.
  *
+ * @param session The session to use. Must not be NULL.
  * @param pollfd The poll descriptor for which the source should be removed.
  *
  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
@@ -882,19 +987,17 @@ SR_API int sr_session_source_remove(struct sr_session *session, int fd)
 SR_API int sr_session_source_remove_pollfd(struct sr_session *session,
                GPollFD *pollfd)
 {
-       (void) session;
-
        return _sr_session_source_remove(session, (gintptr)pollfd);
 }
 
 /**
  * Remove the source belonging to the specified channel.
  *
+ * @param session The session to use. Must not be NULL.
  * @param channel The channel for which the source should be removed.
  *
  * @retval SR_OK Success.
  * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
  * @return SR_ERR_BUG Internal error.
  *
  * @since 0.2.0
@@ -902,9 +1005,117 @@ SR_API int sr_session_source_remove_pollfd(struct sr_session *session,
 SR_API int sr_session_source_remove_channel(struct sr_session *session,
                GIOChannel *channel)
 {
-       (void) session;
-
        return _sr_session_source_remove(session, (gintptr)channel);
 }
 
+static void copy_src(struct sr_config *src, struct sr_datafeed_meta *meta_copy)
+{
+       g_variant_ref(src->data);
+       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,
+               struct sr_datafeed_packet **copy)
+{
+       const struct sr_datafeed_meta *meta;
+       struct sr_datafeed_meta *meta_copy;
+       const struct sr_datafeed_logic *logic;
+       struct sr_datafeed_logic *logic_copy;
+       const struct sr_datafeed_analog *analog;
+       struct sr_datafeed_analog *analog_copy;
+       uint8_t *payload;
+
+       *copy = g_malloc0(sizeof(struct sr_datafeed_packet));
+       (*copy)->type = packet->type;
+
+       switch (packet->type) {
+       case SR_DF_TRIGGER:
+       case SR_DF_END:
+               /* No payload. */
+               break;
+       case SR_DF_HEADER:
+               payload = g_malloc(sizeof(struct sr_datafeed_header));
+               memcpy(payload, packet->payload, sizeof(struct sr_datafeed_header));
+               (*copy)->payload = payload;
+               break;
+       case SR_DF_META:
+               meta = packet->payload;
+               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:
+               logic = packet->payload;
+               logic_copy = g_malloc(sizeof(logic));
+               logic_copy->length = logic->length;
+               logic_copy->unitsize = logic->unitsize;
+               memcpy(logic_copy->data, logic->data, logic->length * logic->unitsize);
+               (*copy)->payload = logic_copy;
+               break;
+       case SR_DF_ANALOG:
+               analog = packet->payload;
+               analog_copy = g_malloc(sizeof(analog));
+               analog_copy->channels = g_slist_copy(analog->channels);
+               analog_copy->num_samples = analog->num_samples;
+               analog_copy->mq = analog->mq;
+               analog_copy->unit = analog->unit;
+               analog_copy->mqflags = analog->mqflags;
+               memcpy(analog_copy->data, analog->data,
+                               analog->num_samples * sizeof(float));
+               (*copy)->payload = analog_copy;
+               break;
+       default:
+               sr_err("Unknown packet type %d", packet->type);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+void sr_packet_free(struct sr_datafeed_packet *packet)
+{
+       const struct sr_datafeed_meta *meta;
+       const struct sr_datafeed_logic *logic;
+       const struct sr_datafeed_analog *analog;
+       struct sr_config *src;
+       GSList *l;
+
+       switch (packet->type) {
+       case SR_DF_TRIGGER:
+       case SR_DF_END:
+               /* No payload. */
+               break;
+       case SR_DF_HEADER:
+               /* Payload is a simple struct. */
+               g_free((void *)packet->payload);
+               break;
+       case SR_DF_META:
+               meta = packet->payload;
+               for (l = meta->config; l; l = l->next) {
+                       src = l->data;
+                       g_variant_unref(src->data);
+                       g_free(src);
+               }
+               g_slist_free(meta->config);
+               g_free((void *)packet->payload);
+               break;
+       case SR_DF_LOGIC:
+               logic = packet->payload;
+               g_free(logic->data);
+               g_free((void *)packet->payload);
+               break;
+       case SR_DF_ANALOG:
+               analog = packet->payload;
+               g_slist_free(analog->channels);
+               g_free(analog->data);
+               g_free((void *)packet->payload);
+               break;
+       default:
+               sr_err("Unknown packet type %d", packet->type);
+       }
+       g_free(packet);
+
+}
+
 /** @} */