summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--libusb/io.c51
-rw-r--r--libusb/libusb.h16
-rw-r--r--libusb/os/linux_usbfs.c13
-rw-r--r--libusb/sync.c7
5 files changed, 82 insertions, 7 deletions
diff --git a/TODO b/TODO
index 907835b..5cb36db 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,4 @@
new sysfs descriptors format
-configuration API
contexts
event handling/threads issue
@@ -11,3 +10,4 @@ offer API to create/destroy handle_events thread
isochronous sync I/O?
exposing of parent-child device relationships
"usb primer" introduction docs
+more examples
diff --git a/libusb/io.c b/libusb/io.c
index f659d24..9760432 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -186,6 +186,49 @@ if (r == 0 && actual_length == sizeof(data)) {
* \ref asyncio "asynchronous I/O API" documentation pages.
*/
+
+/**
+ * \page packetoverflow Packets and overflows
+ *
+ * \section packets Packet abstraction
+ *
+ * The USB specifications describe how data is transmitted in packets, with
+ * constraints on packet size defined by endpoint descriptors. The host must
+ * not send data payloads larger than the endpoint's maximum packet size.
+ *
+ * libusb and the underlying OS abstract out the packet concept, allowing you
+ * to request transfers of any size. Internally, the request will be divided
+ * up into correctly-sized packets. You do not have to be concerned with
+ * packet sizes, but there is one exception when considering overflows.
+ *
+ * \section overflow Bulk/interrupt transfer overflows
+ *
+ * When requesting data on a bulk endpoint, libusb requires you to supply a
+ * buffer and the maximum number of bytes of data that libusb can put in that
+ * buffer. However, the size of the buffer is not communicated to the device -
+ * the device is just asked to send any amount of data.
+ *
+ * There is no problem if the device sends an amount of data that is less than
+ * or equal to the buffer size. libusb reports this condition to you through
+ * the \ref libusb_transfer::actual_length "libusb_transfer.actual_length"
+ * field.
+ *
+ * Problems may occur if the device attempts to send more data than can fit in
+ * the buffer. libusb reports LIBUSB_TRANSFER_OVERFLOW for this condition but
+ * other behaviour is largely undefined: actual_length may or may not be
+ * accurate, the chunk of data that can fit in the buffer (before overflow)
+ * may or may not have been transferred.
+ *
+ * Overflows are nasty, but can be avoided. Even though you were told to
+ * ignore packets above, think about the lower level details: each transfer is
+ * split into packets (typically small, with a maximum size of 512 bytes).
+ * Overflows can only happen if the final packet in an incoming data transfer
+ * is smaller than the actual packet that the device wants to transfer.
+ * Therefore, you will never see an overflow if your transfer buffer size is a
+ * multiple of the endpoint's packet size: the final packet will either
+ * fill up completely or will be only partially filled.
+ */
+
/**
* @defgroup asyncio Asynchronous device I/O
*
@@ -293,6 +336,14 @@ if (r == 0 && actual_length == sizeof(data)) {
* Freeing the transfer after it has been cancelled but before cancellation
* has completed will result in undefined behaviour.
*
+ * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints
+ *
+ * If your device does not have predictable transfer sizes (or it misbehaves),
+ * your application may submit a request for data on an IN endpoint which is
+ * smaller than the data that the device wishes to send. In some circumstances
+ * this will cause an overflow, which is a nasty condition to deal with. See
+ * the \ref packetoverflow page for discussion.
+ *
* \section asyncctrl Considerations for control transfers
*
* The <tt>libusb_transfer</tt> structure is generic and hence does not
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 295de38..40d1a87 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -593,20 +593,23 @@ enum libusb_error {
/** Operation timed out */
LIBUSB_ERROR_TIMEOUT = -7,
+ /** Overflow */
+ LIBUSB_ERROR_OVERFLOW = -8,
+
/** Pipe error */
- LIBUSB_ERROR_PIPE = -8,
+ LIBUSB_ERROR_PIPE = -9,
/** System call interrupted (perhaps due to signal) */
- LIBUSB_ERROR_INTERRUPTED = -9,
+ LIBUSB_ERROR_INTERRUPTED = -10,
/** Insufficient memory */
- LIBUSB_ERROR_NO_MEM = -10,
+ LIBUSB_ERROR_NO_MEM = -11,
/** Operation not supported or unimplemented on this platform */
- LIBUSB_ERROR_NOT_SUPPORTED = -11,
+ LIBUSB_ERROR_NOT_SUPPORTED = -12,
/** Other error */
- LIBUSB_ERROR_OTHER = -12,
+ LIBUSB_ERROR_OTHER = -99,
};
/** \ingroup asyncio
@@ -631,6 +634,9 @@ enum libusb_transfer_status {
/** Device was disconnected */
LIBUSB_TRANSFER_NO_DEVICE,
+
+ /** Device sent more data than requested */
+ LIBUSB_TRANSFER_OVERFLOW,
};
/** \ingroup asyncio
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 95c3e25..fb50d34 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -1546,7 +1546,8 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status,
urb_idx + 1, num_urbs);
- if (urb->status == 0)
+ if (urb->status == 0 ||
+ (urb->status == -EOVERFLOW && urb->actual_length > 0))
itransfer->transferred += urb->actual_length;
if (tpriv->reap_action != NORMAL) {
@@ -1570,6 +1571,11 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
usbi_err("CANCEL: completed URB not awaiting reap?");
else
tpriv->awaiting_reap--;
+ } else if (urb->status == -EPIPE || urb->status == -EOVERFLOW) {
+ if (tpriv->awaiting_reap == 0)
+ usbi_err("CANCEL: completed URB not awaiting reap?");
+ else
+ tpriv->awaiting_reap--;
} else {
usbi_warn("unhandled CANCEL urb status %d", urb->status);
}
@@ -1594,6 +1600,11 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
usbi_dbg("detected endpoint stall");
status = LIBUSB_TRANSFER_STALL;
goto out;
+ } else if (urb->status == -EOVERFLOW) {
+ /* overflow can only ever occur in the last urb */
+ usbi_dbg("overflow, actual_length=%d", urb->actual_length);
+ status = LIBUSB_TRANSFER_OVERFLOW;
+ goto out;
} else if (urb->status != 0) {
usbi_warn("unrecognised urb status %d", urb->status);
}
diff --git a/libusb/sync.c b/libusb/sync.c
index 3a71428..55450c4 100644
--- a/libusb/sync.c
+++ b/libusb/sync.c
@@ -191,6 +191,9 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
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;
@@ -238,6 +241,8 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates
* <tt>transferred</tt>)
* \returns LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
+ * \ref packetoverflow
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failures
*/
@@ -285,6 +290,8 @@ 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 LIBUSB_ERROR_OVERFLOW if the device offered more data, see
+ * \ref packetoverflow
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other error
*/