diff options
author | Pete Batard <pbatard@gmail.com> | 2011-01-30 01:24:59 +0000 |
---|---|---|
committer | Pete Batard <pbatard@gmail.com> | 2011-01-30 01:24:59 +0000 |
commit | beeb853a0db49c30a7cb6e85d4af1305922945db (patch) | |
tree | a4a4cb5eeefc8c1ac9d9f7338028c5de877a4d74 | |
parent | 43aecbb8f05995517479fa91db5d1db25be50d03 (diff) | |
download | libusb-beeb853a0db49c30a7cb6e85d4af1305922945db.tar.gz |
merged latest official changes
* up to 295c9d12e25bc2dbdd8b42bd67a1f7120f0631a1
-rw-r--r-- | README | 6 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | libusb-1.0.pc.in | 2 | ||||
-rw-r--r-- | libusb/descriptor.c | 21 | ||||
-rw-r--r-- | libusb/io.c | 30 | ||||
-rw-r--r-- | libusb/libusbi.h | 18 | ||||
-rw-r--r-- | libusb/os/darwin_usb.c | 8 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 116 |
8 files changed, 118 insertions, 84 deletions
@@ -1,7 +1,8 @@ libusb ====== -libusb is a library for USB device access from Linux userspace. +libusb is a library for USB device access from Linux, Mac OS X and +Windows userspace. It is written in C and licensed under the LGPL-2.1 (see COPYING). libusb is abstracted internally in such a way that it can hopefully @@ -9,7 +10,7 @@ be ported to other operating systems. See the PORTING file for some information, if you fancy a challenge :) libusb homepage: -http://libusb.sourceforge.net +http://libusb.org/ Developers will wish to consult the API documentation: http://libusb.sourceforge.net/api-1.0/ @@ -18,4 +19,5 @@ Use the mailing list for questions, comments, etc: https://sourceforge.net/mailarchive/forum.php?forum_name=libusb-devel - Daniel Drake <dsd@gentoo.org> +- Peter Stuge <peter@stuge.se> (use the mailing list rather than mailing developers directly) diff --git a/configure.ac b/configure.ac index 0083d1b..3afc7d7 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,6 @@ case $host in AC_DEFINE(OS_DARWIN, [], [Darwin backend]) AC_SUBST(OS_DARWIN) AC_DEFINE([THREADS_POSIX], [], [Use Posix Threads]) - AC_DEFINE(USBI_OS_HANDLES_TIMEOUT, [], [Backend handles timeout]) AC_MSG_RESULT([Darwin/MacOS X]) backend="darwin" threads="posix" diff --git a/libusb-1.0.pc.in b/libusb-1.0.pc.in index 0abb16b..81b56ec 100644 --- a/libusb-1.0.pc.in +++ b/libusb-1.0.pc.in @@ -4,7 +4,7 @@ libdir=@libdir@ includedir=@includedir@ Name: libusb-1.0 -Description: C API for USB device access from Linux userspace +Description: C API for USB device access from Linux, Mac OS X and Windows userspace Version: @VERSION@ Libs: -L${libdir} -lusb-1.0 Libs.private: @PC_LIBS_PRIVATE@ diff --git a/libusb/descriptor.c b/libusb/descriptor.c index 54a47b4..11480e8 100644 --- a/libusb/descriptor.c +++ b/libusb/descriptor.c @@ -39,12 +39,12 @@ /* set host_endian if the w values are already in host endian format, * as opposed to bus endian. */ -int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest, - int host_endian) +int usbi_parse_descriptor(unsigned char *source, const char *descriptor, + void *dest, int host_endian) { unsigned char *sp = source, *dp = dest; uint16_t w; - char *cp; + const char *cp; for (cp = descriptor; *cp; cp++) { switch (*cp) { @@ -681,10 +681,17 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev, uint16_t langid; /* Asking for the zero'th index is special - it returns a string - * descriptor that contains all the language IDs supported by the device. - * Typically there aren't many - often only one. The language IDs are 16 - * bit numbers, and they start at the third byte in the descriptor. See - * USB 2.0 specification section 9.6.7 for more information. */ + * descriptor that contains all the language IDs supported by the + * device. Typically there aren't many - often only one. Language + * IDs are 16 bit numbers, and they start at the third byte in the + * descriptor. There's also no point in trying to read descriptor 0 + * with this function. See USB 2.0 specification section 9.6.7 for + * more information. + */ + + if (desc_index == 0) + return LIBUSB_ERROR_INVALID_PARAM; + r = libusb_get_string_descriptor(dev, 0, 0, tbuf, sizeof(tbuf)); if (r < 0) return r; diff --git a/libusb/io.c b/libusb/io.c index d4f6f3d..b23c76b 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -1710,16 +1710,6 @@ static void handle_timeout(struct usbi_transfer *itransfer) "async cancel failed %d errno=%d", r, errno); } -#ifdef USBI_OS_HANDLES_TIMEOUT -static int handle_timeouts_locked(struct libusb_context *ctx) -{ - return 0; -} -static int handle_timeouts(struct libusb_context *ctx) -{ - return 0; -} -#else static int handle_timeouts_locked(struct libusb_context *ctx) { int r; @@ -1747,7 +1737,7 @@ static int handle_timeouts_locked(struct libusb_context *ctx) return 0; /* ignore timeouts we've already handled */ - if (transfer->flags & USBI_TRANSFER_TIMED_OUT) + if (transfer->flags & (USBI_TRANSFER_TIMED_OUT | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) continue; /* if transfer has non-expired timeout, nothing more to do */ @@ -1771,7 +1761,6 @@ static int handle_timeouts(struct libusb_context *ctx) usbi_mutex_unlock(&ctx->flying_transfers_lock); return r; } -#endif #ifdef USBI_TIMERFD_AVAILABLE static int handle_timerfd_trigger(struct libusb_context *ctx) @@ -2094,9 +2083,7 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, */ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) { -#if defined(USBI_OS_HANDLES_TIMEOUT) - return 1; -#elif defined(USBI_TIMERFD_AVAILABLE) +#if defined(USBI_TIMERFD_AVAILABLE) USBI_GET_CONTEXT(ctx); return usbi_using_timerfd(ctx); #else @@ -2135,7 +2122,6 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) { -#ifndef USBI_OS_HANDLES_TIMEOUT struct usbi_transfer *transfer; struct timespec cur_ts; struct timeval cur_tv; @@ -2156,10 +2142,11 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, /* find next transfer which hasn't already been processed as timed out */ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { - if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) { - found = 1; - break; - } + if (transfer->flags & (USBI_TRANSFER_TIMED_OUT | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + found = 1; + break; } usbi_mutex_unlock(&ctx->flying_transfers_lock); @@ -2192,9 +2179,6 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, } return 1; -#else - return 0; -#endif } /** \ingroup poll diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 28c5b0e..caceb65 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -130,13 +130,13 @@ void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, #ifdef ENABLE_LOGGING #define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) #else -#define _usbi_log(ctx, level, ...) +#define _usbi_log(ctx, level, ...) do {} while(0) #endif #if defined(ENABLE_DEBUG_LOGGING) || defined(INCLUDE_DEBUG_LOGGING) #define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) #else -#define usbi_dbg(...) +#define usbi_dbg(...) do {} while(0) #endif #define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) @@ -289,8 +289,6 @@ struct libusb_device_handle { unsigned char os_priv[0]; }; -#define USBI_TRANSFER_TIMED_OUT (1<<0) - enum { USBI_CLOCK_MONOTONIC, USBI_CLOCK_REALTIME @@ -326,6 +324,14 @@ struct usbi_transfer { usbi_mutex_t lock; }; +enum usbi_transfer_flags { + /* The transfer has timed out */ + USBI_TRANSFER_TIMED_OUT = 1 << 0, + + /* Set by backend submit_transfer() if the OS handles timeout */ + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1 +}; + #define __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ + sizeof(struct usbi_transfer))) @@ -365,8 +371,8 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, enum libusb_transfer_status status); int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); -int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest, - int host_endian); +int usbi_parse_descriptor(unsigned char *source, const char *descriptor, + void *dest, int host_endian); int usbi_get_config_index_by_value(struct libusb_device *dev, uint8_t bConfigurationValue, int *idx); diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c index 4c19592..163439d 100644 --- a/libusb/os/darwin_usb.c +++ b/libusb/os/darwin_usb.c @@ -1141,6 +1141,8 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer, transfer->length, darwin_async_io_callback, itransfer); } else { + itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + if (is_read) ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, transfer->length, transfer->timeout, transfer->timeout, @@ -1248,6 +1250,8 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) { tpriv->req.completionTimeout = transfer->timeout; tpriv->req.noDataTimeout = transfer->timeout; + itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + /* all transfers in libusb-1.0 are async */ kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer); @@ -1362,6 +1366,9 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) } static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) { + if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) + result = kIOUSBTransactionTimeout; + switch (result) { case kIOReturnUnderrun: case kIOReturnSuccess: @@ -1376,6 +1383,7 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_ return LIBUSB_TRANSFER_OVERFLOW; case kIOUSBTransactionTimeout: usbi_err (ITRANSFER_CTX (itransfer), "transfer error: timed out"); + itransfer->flags |= USBI_TRANSFER_TIMED_OUT; return LIBUSB_TRANSFER_TIMED_OUT; default: usbi_err (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index b7d9066..9f9b140 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -74,18 +74,18 @@ static const char *usbfs_path = NULL; /* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically * allows us to mark URBs as being part of a specific logical transfer when - * we submit them to the kernel. then, on any error error except a - * cancellation, all URBs within that transfer will be cancelled with the - * endpoint is disabled, meaning that no more data can creep in during the - * time it takes to cancel the remaining URBs. + * we submit them to the kernel. then, on any error except a cancellation, all + * URBs within that transfer will be cancelled and no more URBs will be + * accepted for the transfer, meaning that no more data can creep in. * * The BULK_CONTINUATION flag must be set on all URBs within a bulk transfer * (in either direction) except the first. - * For IN transfers, we must also set SHORT_NOT_OK on all the URBs. - * For OUT transfers, SHORT_NOT_OK must not be set. The effective behaviour - * (where an OUT transfer does not complete, the rest of the URBs in the - * transfer get cancelled) is already in effect, and setting this flag is - * disallowed (a kernel with USB debugging enabled will reject such URBs). + * For IN transfers, we must also set SHORT_NOT_OK on all URBs except the + * last; it means that the kernel should treat a short reply as an error. + * For OUT transfers, SHORT_NOT_OK must not be set. it isn't needed (OUT + * transfers can't be short unless there's already some sort of error), and + * setting this flag is disallowed (a kernel with USB debugging enabled will + * reject such URBs). */ static int supports_flag_bulk_continuation = -1; @@ -221,17 +221,25 @@ static clockid_t find_monotonic_clock(void) static int check_flag_bulk_continuation(void) { struct utsname uts; - int sublevel; + int major, minor, sublevel; if (uname(&uts) < 0) return -1; if (strlen(uts.release) < 4) return 0; - if (strncmp(uts.release, "2.6.", 4) != 0) + if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &sublevel) != 3) return 0; - - sublevel = atoi(uts.release + 4); - return sublevel >= 32; + if (major < 2) + return 0; + if (major == 2) { + if (minor < 6) + return 0; + if (minor == 6) { + if (sublevel < 32) + return 0; + } + } + return 1; } static int op_init(struct libusb_context *ctx) @@ -1452,7 +1460,9 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, if (COMPLETED_EARLY == tpriv->reap_action) return 0; - for (j = 0; j < i; j++) { + /* The URBs are discarded in reverse order of + * submission, to avoid races. */ + for (j = i - 1; j >= 0; j--) { int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &urbs[j]); if (tmp && errno != EINVAL) usbi_warn(TRANSFER_CTX(transfer), @@ -1609,7 +1619,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) /* The URBs we haven't submitted yet we count as already * retired. */ tpriv->num_retired = num_urbs - i; - for (j = 0; j < i; j++) { + for (j = i - 1; j >= 0; j--) { int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urbs[j]); if (tmp && errno != EINVAL) usbi_warn(TRANSFER_CTX(transfer), @@ -1732,7 +1742,7 @@ static int cancel_bulk_transfer(struct usbi_transfer *itransfer) if (tpriv->reap_action != ERROR) tpriv->reap_action = CANCELLED; - for (i = 0; i < tpriv->num_urbs; i++) { + for (i = tpriv->num_urbs - 1; i >= 0; i--) { int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[i]); if (tmp && errno != EINVAL) usbi_warn(TRANSFER_CTX(transfer), @@ -1754,7 +1764,7 @@ static int cancel_iso_transfer(struct usbi_transfer *itransfer) return LIBUSB_ERROR_NOT_FOUND; tpriv->reap_action = CANCELLED; - for (i = 0; i < tpriv->num_urbs; i++) { + for (i = tpriv->num_urbs - 1; i >= 0; i--) { int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, tpriv->iso_urbs[i]); if (tmp && errno != EINVAL) usbi_warn(TRANSFER_CTX(transfer), @@ -1832,6 +1842,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, * 2. we receive a short URB which marks the early completion condition, * so we start cancelling the remaining URBs. however, we're too * slow and another URB completes (or at least completes partially). + * (this can't happen since we always use BULK_CONTINUATION.) * * When this happens, our objectives are not to lose any "surplus" data, * and also to stick it at the end of the previously-received data @@ -1860,16 +1871,23 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, goto out_unlock; } - if (urb->status == 0 || urb->status == -EREMOTEIO || - (urb->status == -EOVERFLOW && urb->actual_length > 0)) - itransfer->transferred += urb->actual_length; - + itransfer->transferred += urb->actual_length; + /* Many of these errors can occur on *any* urb of a multi-urb + * transfer. When they do, we tear down the rest of the transfer. + */ switch (urb->status) { case 0: break; case -EREMOTEIO: /* short transfer */ break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ESHUTDOWN: + usbi_dbg("device removed"); + tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE; + goto cancel_remaining; case -EPIPE: usbi_dbg("detected endpoint stall"); if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) @@ -1884,18 +1902,13 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, case -ETIME: case -EPROTO: case -EILSEQ: - /* These can happen on *any* urb of a multi-urb transfer, so - * save a status and tear down rest of the transfer */ usbi_dbg("low level error %d", urb->status); tpriv->reap_action = ERROR; - if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) - tpriv->reap_status = LIBUSB_TRANSFER_ERROR; goto cancel_remaining; default: usbi_warn(ITRANSFER_CTX(itransfer), "unrecognised urb status %d", urb->status); - if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) - tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + tpriv->reap_action = ERROR; goto cancel_remaining; } @@ -1913,17 +1926,20 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, goto out_unlock; cancel_remaining: + if (ERROR == tpriv->reap_action && LIBUSB_TRANSFER_COMPLETED == tpriv->reap_status) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */ goto completed; /* cancel remaining urbs and wait for their completion before * reporting results */ - while (++urb_idx < tpriv->num_urbs) { + for (int i = tpriv->num_urbs - 1; i > urb_idx; i--) { /* remaining URBs with continuation flag are * automatically cancelled by the kernel */ - if (tpriv->urbs[urb_idx].flags & USBFS_URB_BULK_CONTINUATION) + if (tpriv->urbs[i].flags & USBFS_URB_BULK_CONTINUATION) continue; - int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[urb_idx]); + int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[i]); if (tmp && errno != EINVAL) usbi_warn(TRANSFER_CTX(transfer), "unrecognised discard errno %d", errno); @@ -1951,6 +1967,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer, int num_urbs = tpriv->num_urbs; int urb_idx = 0; int i; + enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; usbi_mutex_lock(&itransfer->lock); for (i = 0; i < num_urbs; i++) { @@ -1968,16 +1985,14 @@ static int handle_iso_completion(struct usbi_transfer *itransfer, usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status, urb_idx, num_urbs); - if (urb->status == 0) { - /* copy isochronous results back in */ + /* copy isochronous results back in */ - for (i = 0; i < urb->number_of_packets; i++) { - struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; - struct libusb_iso_packet_descriptor *lib_desc = - &transfer->iso_packet_desc[tpriv->iso_packet_offset++]; - lib_desc->status = urb_desc->status; - lib_desc->actual_length = urb_desc->actual_length; - } + for (i = 0; i < urb->number_of_packets; i++) { + struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; + struct libusb_iso_packet_descriptor *lib_desc = + &transfer->iso_packet_desc[tpriv->iso_packet_offset++]; + lib_desc->status = urb_desc->status; + lib_desc->actual_length = urb_desc->actual_length; } tpriv->num_retired++; @@ -1985,6 +2000,9 @@ static int handle_iso_completion(struct usbi_transfer *itransfer, if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */ usbi_dbg("CANCEL: urb status %d", urb->status); + if (status == LIBUSB_TRANSFER_COMPLETED) + status = LIBUSB_TRANSFER_ERROR; + if (tpriv->num_retired == num_urbs) { usbi_dbg("CANCEL: last URB handled, reporting"); free_iso_urbs(tpriv); @@ -2003,6 +2021,12 @@ static int handle_iso_completion(struct usbi_transfer *itransfer, switch (urb->status) { case 0: break; + case -ENOENT: /* cancelled */ + break; + case -ESHUTDOWN: + usbi_dbg("device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; case -ETIME: case -EPROTO: case -EILSEQ: @@ -2020,7 +2044,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer, usbi_dbg("last URB in transfer --> complete!"); free_iso_urbs(tpriv); usbi_mutex_unlock(&itransfer->lock); - return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); + return usbi_handle_transfer_completion(itransfer, status); } out: @@ -2037,8 +2061,7 @@ static int handle_control_completion(struct usbi_transfer *itransfer, usbi_mutex_lock(&itransfer->lock); usbi_dbg("handling completion status %d", urb->status); - if (urb->status == 0) - itransfer->transferred += urb->actual_length; + itransfer->transferred += urb->actual_length; if (tpriv->reap_action == CANCELLED) { if (urb->status != 0 && urb->status != -ENOENT) @@ -2052,9 +2075,14 @@ static int handle_control_completion(struct usbi_transfer *itransfer, switch (urb->status) { case 0: - itransfer->transferred = urb->actual_length; status = LIBUSB_TRANSFER_COMPLETED; break; + case -ENOENT: /* cancelled */ + break; + case -ESHUTDOWN: + usbi_dbg("device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; case -EPIPE: usbi_dbg("unsupported control request"); status = LIBUSB_TRANSFER_STALL; |