Detect endpoint halts and unsupported control requests
authorDaniel Drake <dsd@gentoo.org>
Mon, 5 May 2008 15:22:33 +0000 (16:22 +0100)
committerDaniel Drake <dsd@gentoo.org>
Mon, 5 May 2008 15:45:11 +0000 (16:45 +0100)
libusb/io.c
libusb/libusb.h
libusb/os/linux_usbfs.c
libusb/sync.c

index 453ecce..ac153ce 100644 (file)
@@ -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
index 5cce3fb..b568d36 100644 (file)
@@ -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
index ec7907f..22d6b6a 100644 (file)
@@ -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;
 }
 
index 77d01db..6f1f07b 100644 (file)
@@ -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(