diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-05-05 16:22:33 +0100 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-05-05 16:45:11 +0100 |
commit | a304eca71f22c6df7d70a901483b30f1b8e93378 (patch) | |
tree | 1ca25c3ec1db242980523f3ef47cc6dcf9f166c1 | |
parent | 7da521954ba661d3f537440c31a84b66e974d56b (diff) | |
download | libusb-a304eca71f22c6df7d70a901483b30f1b8e93378.tar.gz |
Detect endpoint halts and unsupported control requests
-rw-r--r-- | libusb/io.c | 4 | ||||
-rw-r--r-- | libusb/libusb.h | 11 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 25 | ||||
-rw-r--r-- | libusb/sync.c | 10 |
4 files changed, 41 insertions, 9 deletions
diff --git a/libusb/io.c b/libusb/io.c index 453ecce..ac153ce 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -332,6 +332,10 @@ if (r == 0 && actual_length == sizeof(data)) { * and libusb_control_transfer_get_setup() functions within your transfer * callback. * + * Even though control endpoints do not halt, a completed control transfer + * may have a LIBUSB_TRANSFER_STALL status code. This indicates the control + * request was not supported. + * * \section asyncintr Considerations for interrupt transfers * * All interrupt transfers are performed using the polling interval presented diff --git a/libusb/libusb.h b/libusb/libusb.h index 5cce3fb..b568d36 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -524,9 +524,10 @@ enum libusb_error { LIBUSB_ERROR_NOT_FOUND = -4, LIBUSB_ERROR_BUSY = -5, LIBUSB_ERROR_TIMEOUT = -6, - LIBUSB_ERROR_NO_MEM = -7, - LIBUSB_ERROR_NOT_SUPPORTED = -8, - LIBUSB_ERROR_OTHER = -9, + LIBUSB_ERROR_PIPE = -7, + LIBUSB_ERROR_NO_MEM = -8, + LIBUSB_ERROR_NOT_SUPPORTED = -9, + LIBUSB_ERROR_OTHER = -10, }; /** \ingroup asyncio @@ -544,6 +545,10 @@ enum libusb_transfer_status { /** Transfer was cancelled */ LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, }; /** \ingroup asyncio diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index ec7907f..22d6b6a 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -1006,9 +1006,13 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, return 0; } - /* FIXME: research what other status codes may exist */ - if (urb->status != 0) + if (urb->status == -EPIPE) { + usbi_dbg("detected endpoint stall"); + usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_STALL); + return 0; + } else if (urb->status != 0) { usbi_warn("unrecognised urb status %d", urb->status); + } /* if we're the last urb or we got less data than requested then we're * done */ @@ -1090,7 +1094,6 @@ static int handle_iso_completion(struct usbi_transfer *itransfer, return 0; } - /* FIXME: research what other status codes may exist */ if (urb->status != 0) usbi_warn("unrecognised urb status %d", urb->status); @@ -1109,6 +1112,8 @@ static int handle_control_completion(struct usbi_transfer *itransfer, struct usbfs_urb *urb) { struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int status; + usbi_dbg("handling completion status %d", urb->status); if (urb->status == 0) @@ -1122,13 +1127,21 @@ static int handle_control_completion(struct usbi_transfer *itransfer, return 0; } - /* FIXME: research what other status codes may exist */ - if (urb->status != 0) + if (urb->status == -EPIPE) { + usbi_dbg("unsupported control request"); + status = LIBUSB_TRANSFER_STALL; + goto out; + } else if (urb->status != 0) { usbi_warn("unrecognised urb status %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + goto out; + } itransfer->transferred = urb->actual_length; + status = LIBUSB_TRANSFER_COMPLETED; +out: free(tpriv->urbs); - usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); + usbi_handle_transfer_completion(itransfer, status); return 0; } diff --git a/libusb/sync.c b/libusb/sync.c index 77d01db..6f1f07b 100644 --- a/libusb/sync.c +++ b/libusb/sync.c @@ -60,6 +60,8 @@ static void ctrl_transfer_cb(struct libusb_transfer *transfer) * value 0. * \returns 0 on success * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the + * device * \returns another LIBUSB_ERROR code on other failures */ API_EXPORTED int libusb_control_transfer(libusb_device_handle *dev_handle, @@ -116,6 +118,9 @@ API_EXPORTED int libusb_control_transfer(libusb_device_handle *dev_handle, case LIBUSB_TRANSFER_TIMED_OUT: r = LIBUSB_ERROR_TIMEOUT; break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; default: usbi_warn("unrecognised status code %d", transfer->status); r = LIBUSB_ERROR_OTHER; @@ -174,6 +179,9 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, case LIBUSB_TRANSFER_TIMED_OUT: r = LIBUSB_ERROR_TIMEOUT; break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; default: usbi_warn("unrecognised status code %d", transfer->status); r = LIBUSB_ERROR_OTHER; @@ -217,6 +225,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * \returns 0 on success (and populates <tt>transferred</tt>) * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates * <tt>transferred</tt>) + * \returns LIBUSB_ERROR_PIPE if the endpoint halted * \returns another LIBUSB_ERROR code on other failures */ API_EXPORTED int libusb_bulk_transfer(struct libusb_device_handle *dev_handle, @@ -262,6 +271,7 @@ API_EXPORTED int libusb_bulk_transfer(struct libusb_device_handle *dev_handle, * * \returns 0 on success (and populates <tt>transferred</tt>) * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the endpoint halted * \returns another LIBUSB_ERROR code on other error */ API_EXPORTED int libusb_interrupt_transfer( |