+static void send_data_async_complete_cb(struct libusb_transfer *transfer)
+{
+ am7xxx_device *dev = (am7xxx_device *)(transfer->user_data);
+ int *completed = &(dev->transfer_completed);
+ int transferred = transfer->actual_length;
+ int ret;
+
+ if (transferred != transfer->length) {
+ error(dev->ctx, "transferred: %d (expected %u)\n",
+ transferred, transfer->length);
+ }
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ ret = 0;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ ret = LIBUSB_ERROR_TIMEOUT;
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ ret = LIBUSB_ERROR_PIPE;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ ret = LIBUSB_ERROR_OVERFLOW;
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ ret = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ case LIBUSB_TRANSFER_CANCELLED:
+ ret = LIBUSB_ERROR_IO;
+ break;
+ default:
+ error(dev->ctx, "unrecognised status code %d", transfer->status);
+ ret = LIBUSB_ERROR_OTHER;
+ }
+
+ if (ret < 0)
+ error(dev->ctx, "libusb transfer failed: %s",
+ libusb_error_name(ret));
+
+ libusb_free_transfer(transfer);
+ transfer = NULL;
+
+ *completed = 1;
+}
+
+static inline void wait_for_trasfer_completed(am7xxx_device *dev)
+{
+ while (!dev->transfer_completed) {
+ int ret = libusb_handle_events_completed(dev->ctx->usb_context,
+ &(dev->transfer_completed));
+ if (ret < 0) {
+ if (ret == LIBUSB_ERROR_INTERRUPTED)
+ continue;
+ error(dev->ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying",
+ libusb_error_name(ret));
+ libusb_cancel_transfer(dev->transfer);
+ continue;
+ }
+ }
+}
+
+static int send_data_async(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
+{
+ int ret;
+ uint8_t *transfer_buffer;
+
+ dev->transfer = libusb_alloc_transfer(0);
+ if (dev->transfer == NULL) {
+ error(dev->ctx, "cannot allocate transfer (%s)\n",
+ strerror(errno));
+ return -ENOMEM;
+ }
+
+ /* Make a copy of the buffer so the caller can safely reuse it just
+ * after libusb_submit_transfer() has returned. This technique
+ * requires more allocations than a proper double-buffering approach
+ * but it takes a lot less code. */
+ transfer_buffer = malloc(len);
+ if (transfer_buffer == NULL) {
+ error(dev->ctx, "cannot allocate transfer buffer (%s)\n",
+ strerror(errno));
+ ret = -ENOMEM;
+ goto err;
+ }
+ memcpy(transfer_buffer, buffer, len);
+
+ dev->transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
+ libusb_fill_bulk_transfer(dev->transfer, dev->usb_device, 0x1,
+ transfer_buffer, len,
+ send_data_async_complete_cb, dev, 0);
+
+ /* wait for the previous transfer to complete */
+ wait_for_trasfer_completed(dev);
+
+ trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
+
+ dev->transfer_completed = 0;
+ ret = libusb_submit_transfer(dev->transfer);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ libusb_free_transfer(dev->transfer);
+ dev->transfer = NULL;
+ return ret;
+}
+