From 3695285d5d743190ed88f982b0c531066e68b686 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Mon, 17 Oct 2016 16:57:43 -0700 Subject: [PATCH] usblib: Wrap USBDEVFS_REAPURBNDELAY ioctl Test: Test is submitted alongside this change Bug: 31288102 Change-Id: I99311879150ef3f647c65205b35254736dde6de7 --- libusbhost/include/usbhost/usbhost.h | 5 +-- libusbhost/usbhost.c | 50 ++++++++++++++++++---------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h index 84594c818..82c15505b 100644 --- a/libusbhost/include/usbhost/usbhost.h +++ b/libusbhost/include/usbhost/usbhost.h @@ -232,10 +232,11 @@ void usb_request_free(struct usb_request *req); /* Submits a read or write request on the specified device */ int usb_request_queue(struct usb_request *req); - /* Waits for the results of a previous usb_request_queue operation. + /* Waits for the results of a previous usb_request_queue operation. timeoutMillis == -1 requests + * to wait forever. * Returns a usb_request, or NULL for error. */ -struct usb_request *usb_request_wait(struct usb_device *dev); +struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis); /* Cancels a pending usb_request_queue() operation. */ int usb_request_cancel(struct usb_request *req); diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 68aca1774..856a72803 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -14,6 +14,10 @@ * limitations under the License. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + // #define DEBUG 1 #if DEBUG @@ -43,6 +47,7 @@ #include #include #include +#include #include #include @@ -681,29 +686,38 @@ int usb_request_queue(struct usb_request *req) return res; } -struct usb_request *usb_request_wait(struct usb_device *dev) +struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis) { - struct usbdevfs_urb *urb = NULL; - struct usb_request *req = NULL; + // Poll until a request becomes available if there is a timeout + if (timeoutMillis > 0) { + struct pollfd p = {.fd = dev->fd, .events = POLLOUT, .revents = 0}; - while (1) { - int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb); - D("USBDEVFS_REAPURB returned %d\n", res); - if (res < 0) { - if(errno == EINTR) { - continue; - } - D("[ reap urb - error ]\n"); + int res = poll(&p, 1, timeoutMillis); + + if (res != 1 || p.revents != POLLOUT) { + D("[ poll - event %d, error %d]\n", p.revents, errno); return NULL; - } else { - D("[ urb @%p status = %d, actual = %d ]\n", - urb, urb->status, urb->actual_length); - req = (struct usb_request*)urb->usercontext; - req->actual_length = urb->actual_length; } - break; } - return req; + + // Read the request. This should usually succeed as we polled before, but it can fail e.g. when + // two threads are reading usb requests at the same time and only a single request is available. + struct usbdevfs_urb *urb = NULL; + int res = TEMP_FAILURE_RETRY(ioctl(dev->fd, timeoutMillis == -1 ? USBDEVFS_REAPURB : + USBDEVFS_REAPURBNDELAY, &urb)); + D("%s returned %d\n", timeoutMillis == -1 ? "USBDEVFS_REAPURB" : "USBDEVFS_REAPURBNDELAY", res); + + if (res < 0) { + D("[ reap urb - error %d]\n", errno); + return NULL; + } else { + D("[ urb @%p status = %d, actual = %d ]\n", urb, urb->status, urb->actual_length); + + struct usb_request *req = (struct usb_request*)urb->usercontext; + req->actual_length = urb->actual_length; + + return req; + } } int usb_request_cancel(struct usb_request *req)