From d5f82893fab3f1c13b1af4ba17aac72479bad7d5 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Fri, 20 Jun 2008 23:04:53 -0500 Subject: [PATCH] Overflow handling --- TODO | 2 +- libusb/io.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ libusb/libusb.h | 16 +++++++++++----- libusb/os/linux_usbfs.c | 13 ++++++++++++- libusb/sync.c | 7 +++++++ 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 libusb_transfer 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 * transferred) * \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 transferred) * \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 */ -- 2.7.4