+/** */
+static unsigned int stlink_usb_block(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->version.stlink == 3)
+ return STLINKV3_MAX_RW8;
+ else
+ return STLINK_MAX_RW8;
+}
+
+
+
+#ifdef USE_LIBUSB_ASYNCIO
+
+static LIBUSB_CALL void sync_transfer_cb(struct libusb_transfer *transfer)
+{
+ int *completed = transfer->user_data;
+ *completed = 1;
+ /* caller interprets result and frees transfer */
+}
+
+
+static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
+{
+ int r, *completed = transfer->user_data;
+
+ /* Assuming a single libusb context exists. There no existing interface into this
+ * module to pass a libusb context.
+ */
+ struct libusb_context *ctx = NULL;
+
+ while (!*completed) {
+ r = libusb_handle_events_completed(ctx, completed);
+ if (r < 0) {
+ if (r == LIBUSB_ERROR_INTERRUPTED)
+ continue;
+ libusb_cancel_transfer(transfer);
+ continue;
+ }
+ }
+}
+
+
+static int transfer_error_status(const struct libusb_transfer *transfer)
+{
+ int r = 0;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ r = 0;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ r = LIBUSB_ERROR_TIMEOUT;
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ r = LIBUSB_ERROR_PIPE;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ r = LIBUSB_ERROR_OVERFLOW;
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ r = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ case LIBUSB_TRANSFER_CANCELLED:
+ r = LIBUSB_ERROR_IO;
+ break;
+ default:
+ r = LIBUSB_ERROR_OTHER;
+ break;
+ }
+
+ return r;
+}
+
+struct jtag_xfer {
+ int ep;
+ uint8_t *buf;
+ size_t size;
+ /* Internal */
+ int retval;
+ int completed;
+ size_t transfer_size;
+ struct libusb_transfer *transfer;
+};
+
+static int jtag_libusb_bulk_transfer_n(
+ jtag_libusb_device_handle * dev_handle,
+ struct jtag_xfer *transfers,
+ size_t n_transfers,
+ int timeout)
+{
+ int retval = 0;
+ int returnval = ERROR_OK;
+
+
+ for (size_t i = 0; i < n_transfers; ++i) {
+ transfers[i].retval = 0;
+ transfers[i].completed = 0;
+ transfers[i].transfer_size = 0;
+ transfers[i].transfer = libusb_alloc_transfer(0);
+
+ if (transfers[i].transfer == NULL) {
+ for (size_t j = 0; j < i; ++j)
+ libusb_free_transfer(transfers[j].transfer);
+
+ LOG_DEBUG("ERROR, failed to alloc usb transfers");
+ for (size_t k = 0; k < n_transfers; ++k)
+ transfers[k].retval = LIBUSB_ERROR_NO_MEM;
+ return ERROR_FAIL;
+ }
+ }
+
+ for (size_t i = 0; i < n_transfers; ++i) {
+ libusb_fill_bulk_transfer(
+ transfers[i].transfer,
+ dev_handle,
+ transfers[i].ep, transfers[i].buf, transfers[i].size,
+ sync_transfer_cb, &transfers[i].completed, timeout);
+ transfers[i].transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
+
+ retval = libusb_submit_transfer(transfers[i].transfer);
+ if (retval < 0) {
+ LOG_DEBUG("ERROR, failed to submit transfer %zu, error %d", i, retval);
+
+ /* Probably no point continuing to submit transfers once a submission fails.
+ * As a result, tag all remaining transfers as errors.
+ */
+ for (size_t j = i; j < n_transfers; ++j)
+ transfers[j].retval = retval;
+
+ returnval = ERROR_FAIL;
+ break;
+ }
+ }
+
+ /* Wait for every submitted USB transfer to complete.
+ */
+ for (size_t i = 0; i < n_transfers; ++i) {
+ if (transfers[i].retval == 0) {
+ sync_transfer_wait_for_completion(transfers[i].transfer);
+
+ retval = transfer_error_status(transfers[i].transfer);
+ if (retval) {
+ returnval = ERROR_FAIL;
+ transfers[i].retval = retval;
+ LOG_DEBUG("ERROR, transfer %zu failed, error %d", i, retval);
+ } else {
+ /* Assuming actual_length is only valid if there is no transfer error.
+ */
+ transfers[i].transfer_size = transfers[i].transfer->actual_length;
+ }
+ }
+
+ libusb_free_transfer(transfers[i].transfer);
+ transfers[i].transfer = NULL;
+ }
+
+ return returnval;
+}
+
+#endif
+
+