+static void unserialize_header(uint8_t *buffer, struct am7xxx_header *h)
+{
+ uint8_t **buffer_iterator = &buffer;
+
+ h->packet_type = get_le32(buffer_iterator);
+ h->unknown0 = get_8(buffer_iterator);
+ h->header_data_len = get_8(buffer_iterator);
+ h->unknown2 = get_8(buffer_iterator);
+ h->unknown3 = get_8(buffer_iterator);
+ h->header_data.data.field0 = get_le32(buffer_iterator);
+ h->header_data.data.field1 = get_le32(buffer_iterator);
+ h->header_data.data.field2 = get_le32(buffer_iterator);
+ h->header_data.data.field3 = get_le32(buffer_iterator);
+}
+
+static int read_header(am7xxx_device *dev, struct am7xxx_header *h)
+{
+ int ret;
+
+ ret = read_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
+ if (ret < 0)
+ goto out;
+
+ unserialize_header(dev->buffer, h);
+
+ debug_dump_header(dev->ctx, h);
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+static int send_header(am7xxx_device *dev, struct am7xxx_header *h)
+{
+ int ret;
+
+ debug_dump_header(dev->ctx, h);
+
+ serialize_header(h, dev->buffer);
+ ret = send_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
+ if (ret < 0)
+ error(dev->ctx, "failed to send data\n");
+
+ return ret;
+}
+
+/* When level == AM7XXX_LOG_FATAL do not check the log_level from the context
+ * and print the message unconditionally, this makes it possible to print
+ * fatal messages even early on initialization, before the context has been
+ * set up */
+static void log_message(am7xxx_context *ctx,
+ int level,
+ const char *function,
+ int line,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ if (level == AM7XXX_LOG_FATAL || (ctx && level <= ctx->log_level)) {
+ if (function) {
+ fprintf(stderr, "%s", function);
+ if (line)
+ fprintf(stderr, "[%d]", line);
+ fprintf(stderr, ": ");
+ }
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ return;
+}
+
+static am7xxx_device *add_new_device(am7xxx_context *ctx)
+{
+ am7xxx_device **devices_list;
+ am7xxx_device *new_device;
+
+ if (ctx == NULL) {
+ fatal("context must not be NULL!\n");
+ return NULL;
+ }
+
+ devices_list = &(ctx->devices_list);
+
+ new_device = malloc(sizeof(*new_device));
+ if (new_device == NULL) {
+ fatal("cannot allocate a new device (%s)\n", strerror(errno));
+ return NULL;
+ }
+ memset(new_device, 0, sizeof(*new_device));
+
+ new_device->ctx = ctx;
+
+ if (*devices_list == NULL) {
+ *devices_list = new_device;
+ } else {
+ am7xxx_device *prev = *devices_list;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = new_device;
+ }
+ return new_device;
+}
+
+static am7xxx_device *find_device(am7xxx_context *ctx,
+ unsigned int device_index)
+{
+ unsigned int i = 0;
+ am7xxx_device *current;
+
+ if (ctx == NULL) {
+ fatal("context must not be NULL!\n");
+ return NULL;
+ }
+
+ current = ctx->devices_list;
+ while (current && i++ < device_index)
+ current = current->next;
+
+ return current;
+}
+
+typedef enum {
+ SCAN_OP_BUILD_DEVLIST,
+ SCAN_OP_OPEN_DEVICE,
+} scan_op;
+
+/**
+ * This is where the central logic of multi-device support is.
+ *
+ * When 'op' == SCAN_OP_BUILD_DEVLIST the parameters 'open_device_index' and
+ * 'dev' are ignored; the function returns 0 on success and a negative value
+ * on error.
+ *
+ * When 'op' == SCAN_OP_OPEN_DEVICE the function opens the supported USB
+ * device with index 'open_device_index' and returns the correspondent
+ * am7xxx_device in the 'dev' parameter; the function returns 0 on success,
+ * 1 if the device was already open and a negative value on error.
+ *
+ * NOTES:
+ * if scan_devices() fails when called with 'op' == SCAN_OP_BUILD_DEVLIST,
+ * the caller might want to call am7xxx_shutdown() in order to remove
+ * devices possibly added before the failure.
+ */
+static int scan_devices(am7xxx_context *ctx, scan_op op,
+ unsigned int open_device_index, am7xxx_device **dev)