* Submit a transfer. This function will fire off the USB transfer and then
* return immediately.
*
- * It is undefined behaviour to submit a transfer that has already been
- * submitted but has not yet completed.
- *
* \param transfer the transfer to submit
* \returns 0 on success
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted.
* \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer)
/** \ingroup asyncio
* Asynchronously cancel a previously submitted transfer.
- * It is undefined behaviour to call this function on a transfer that is
- * already being cancelled or has already completed.
* This function returns immediately, but this does not indicate cancellation
* is complete. Your callback function will be invoked at some later time
* with a transfer status of
*
* \param transfer the transfer to cancel
* \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is already complete or
+ * cancelled.
* \returns a LIBUSB_ERROR code on failure
*/
API_EXPORTED int libusb_cancel_transfer(struct libusb_transfer *transfer)
}
free(tpriv->iso_urbs);
+ tpriv->iso_urbs = NULL;
}
static int submit_bulk_transfer(struct usbi_transfer *itransfer,
int i;
size_t alloc_size;
+ if (tpriv->urbs)
+ return LIBUSB_ERROR_BUSY;
+
/* usbfs places a 16kb limit on bulk URBs. we divide up larger requests
* into smaller units to meet such restriction, then fire off all the
* units at once. it would be simpler if we just fired one unit at a time,
if (i == 0) {
usbi_dbg("first URB failed, easy peasy");
free(urbs);
+ tpriv->urbs = NULL;
return r;
}
unsigned int packet_len;
unsigned char *urb_buffer = transfer->buffer;
+ if (tpriv->iso_urbs)
+ return LIBUSB_ERROR_BUSY;
+
/* usbfs places a 32kb limit on iso URBs. we divide up larger requests
* into smaller units to meet such restriction, then fire off all the
* units at once. it would be simpler if we just fired one unit at a time,
struct usbfs_urb *urb;
int r;
+ if (tpriv->urbs)
+ return LIBUSB_ERROR_BUSY;
+
if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH)
return LIBUSB_ERROR_INVALID_PARAM;
r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
if (r < 0) {
free(urb);
+ tpriv->urbs = NULL;
if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
__device_handle_priv(transfer->dev_handle);
int r;
+ if (!tpriv->urbs)
+ return LIBUSB_ERROR_NOT_FOUND;
+
tpriv->reap_action = CANCELLED;
r = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, tpriv->urbs);
if(r) {
return 0;
}
-static void cancel_bulk_transfer(struct usbi_transfer *itransfer)
+static int cancel_bulk_transfer(struct usbi_transfer *itransfer)
{
struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
struct libusb_transfer *transfer =
__device_handle_priv(transfer->dev_handle);
int i;
+ if (!tpriv->urbs)
+ return LIBUSB_ERROR_NOT_FOUND;
+
tpriv->reap_action = CANCELLED;
for (i = 0; i < tpriv->num_urbs; i++) {
int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[i]);
usbi_warn(TRANSFER_CTX(transfer),
"unrecognised discard errno %d", errno);
}
+ return 0;
}
-static void cancel_iso_transfer(struct usbi_transfer *itransfer)
+static int cancel_iso_transfer(struct usbi_transfer *itransfer)
{
struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
struct libusb_transfer *transfer =
__device_handle_priv(transfer->dev_handle);
int i;
+ if (!tpriv->iso_urbs)
+ return LIBUSB_ERROR_NOT_FOUND;
+
tpriv->reap_action = CANCELLED;
for (i = 0; i < tpriv->num_urbs; i++) {
int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, tpriv->iso_urbs[i]);
usbi_warn(TRANSFER_CTX(transfer),
"unrecognised discard errno %d", errno);
}
+ return 0;
}
static int op_cancel_transfer(struct usbi_transfer *itransfer)
return cancel_control_transfer(itransfer);
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
- cancel_bulk_transfer(itransfer);
- return 0;
+ return cancel_bulk_transfer(itransfer);
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
- cancel_iso_transfer(itransfer);
- return 0;
+ return cancel_iso_transfer(itransfer);
default:
usbi_err(TRANSFER_CTX(transfer),
"unknown endpoint type %d", transfer->type);
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
free(tpriv->urbs);
+ tpriv->urbs = NULL;
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
free_iso_urbs(tpriv);
usbi_dbg("CANCEL: last URB handled, reporting");
if (tpriv->reap_action == CANCELLED) {
free(tpriv->urbs);
+ tpriv->urbs = NULL;
usbi_handle_transfer_cancellation(itransfer);
return 0;
}
out:
free(tpriv->urbs);
+ tpriv->urbs = NULL;
usbi_handle_transfer_completion(itransfer, status);
return 0;
}
usbi_warn(ITRANSFER_CTX(itransfer),
"cancel: unrecognised urb status %d", urb->status);
free(tpriv->urbs);
+ tpriv->urbs = NULL;
usbi_handle_transfer_cancellation(itransfer);
return 0;
}
}
free(tpriv->urbs);
+ tpriv->urbs = NULL;
usbi_handle_transfer_completion(itransfer, status);
return 0;
}