diff options
-rw-r--r-- | TODO | 25 | ||||
-rw-r--r-- | examples/dpfp.c | 186 | ||||
-rw-r--r-- | libusb/io.c | 458 | ||||
-rw-r--r-- | libusb/libusb.h | 150 | ||||
-rw-r--r-- | libusb/libusbi.h | 20 |
5 files changed, 411 insertions, 428 deletions
@@ -1,9 +1,6 @@ -high priority -============= -cancellation race concerns - better tracking of kernel feedback? - for 1.0 ======= +cancellation race concerns - better tracking of kernel feedback? API docs isochronous endpoint I/O thread safety @@ -11,19 +8,21 @@ abstraction for cross-platform-ness error codes fixme review -for 1.1 or future -================== -optional timerfd support (runtime detection) -notifications of hotplugged/unplugged devices -use poll() rather than select()? +add some libusb_transfer flags: + - treat short transfers as errors + - unlink behaviour control + - setup packet prepends data (no copying needed) + - update timeout with time remaining 1.0 API style/naming points to reconsider ========================================= - -struct libusb_(bulk|control)_transfer or parameters? devh in general -separate transfer allocation and submission config struct/function naming typedef _cb or _cb_fn or _cb_t? typedef as-is or pointers? libusb_dev_t rather than libusb_dev *? -remove "_transfer" from libusb_{control,bulk}_transfer_request? + +for 1.1 or future +================== +optional timerfd support (runtime detection) +notifications of hotplugged/unplugged devices +use poll() rather than select()? diff --git a/examples/dpfp.c b/examples/dpfp.c index 880b3a2..0c2806e 100644 --- a/examples/dpfp.c +++ b/examples/dpfp.c @@ -46,8 +46,8 @@ enum { }; static int next_state(void); -static int submit_irq_urb(void); -static int submit_img_urb(void); +static int submit_irq_transfer(void); +static int submit_img_transfer(void); enum { STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, @@ -62,23 +62,11 @@ static int state = 0; static struct libusb_dev_handle *devh = NULL; static unsigned char imgbuf[0x1b340]; static unsigned char irqbuf[INTR_LENGTH]; -static libusb_transfer *img_urbh = NULL; -static libusb_transfer *irq_urbh = NULL; +static struct libusb_transfer *img_transfer = NULL; +static struct libusb_transfer *irq_transfer = NULL; static int img_idx = 0; static int do_exit = 0; -static struct libusb_bulk_transfer_request imgrq = { - .endpoint = EP_DATA, - .data = imgbuf, - .length = sizeof(imgbuf), -}; - -static struct libusb_bulk_transfer_request intrrq = { - .endpoint = EP_INTR, - .data = irqbuf, - .length = sizeof(irqbuf), -}; - static int find_dpfp_device(void) { devh = libusb_open_device_with_vid_pid(0x05ba, 0x000a); @@ -88,18 +76,11 @@ static int find_dpfp_device(void) static int print_f0_data(void) { unsigned char data[0x10]; - struct libusb_control_transfer_request rq = { - .requesttype = CTRL_IN, - .request = USB_RQ, - .value = 0xf0, - .index = 0, - .length = sizeof(data), - .data = data, - }; int r; unsigned int i; - r = libusb_control_transfer(devh, &rq, 0); + r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, + sizeof(data), 0); if (r < 0) { fprintf(stderr, "F0 error %d\n", r); return r; @@ -118,17 +99,9 @@ static int print_f0_data(void) static int get_hwstat(unsigned char *status) { - struct libusb_control_transfer_request rq = { - .requesttype = CTRL_IN, - .request = USB_RQ, - .value = 0x07, - .index = 0, - .length = 1, - .data = status, - }; int r; - r = libusb_control_transfer(devh, &rq, 0); + r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); if (r < 0) { fprintf(stderr, "read hwstat error %d\n", r); return r; @@ -145,18 +118,9 @@ static int get_hwstat(unsigned char *status) static int set_hwstat(unsigned char data) { int r; - struct libusb_control_transfer_request rq = { - .requesttype = CTRL_OUT, - .request = USB_RQ, - .value = 0x07, - .index = 0, - .length = 1, - .data = &data, - }; printf("set hwstat to %02x\n", data); - - r = libusb_control_transfer(devh, &rq, 0); + r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); if (r < 0) { fprintf(stderr, "set hwstat error %d\n", r); return r; @@ -172,18 +136,9 @@ static int set_hwstat(unsigned char data) static int set_mode(unsigned char data) { int r; - struct libusb_control_transfer_request rq = { - .requesttype = CTRL_OUT, - .request = USB_RQ, - .value = 0x4e, - .index = 0, - .length = 1, - .data = &data, - }; - printf("set mode %02x\n", data); - r = libusb_control_transfer(devh, &rq, 0); + r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); if (r < 0) { fprintf(stderr, "set mode error %d\n", r); return r; @@ -196,56 +151,51 @@ static int set_mode(unsigned char data) return 0; } -static void cb_mode_changed(struct libusb_dev_handle *_devh, - struct libusb_transfer *urbh, enum libusb_transfer_status status, - struct libusb_control_setup *setup, unsigned char *data, int actual_length, - void *user_data) +static void cb_mode_changed(struct libusb_transfer *transfer) { - if (status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "mode change URB not completed!\n"); + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "mode change transfer not completed!\n"); do_exit = 2; } printf("async cb_mode_changed\n"); if (next_state() < 0) do_exit = 2; + free(transfer->buffer); + libusb_free_transfer(transfer); } static int set_mode_async(unsigned char data) { - libusb_transfer *urbh; - struct libusb_control_transfer_request rq = { - .requesttype = CTRL_OUT, - .request = USB_RQ, - .value = 0x4e, - .index = 0, - .length = 1, - .data = &data, - }; + int bufsize = LIBUSB_CONTROL_SETUP_SIZE + 1; + unsigned char *buf = malloc(bufsize); + struct libusb_transfer *transfer; - printf("async set mode %02x\n", data); - - urbh = libusb_async_control_transfer(devh, &rq, cb_mode_changed, NULL, - 1000); - if (!urbh) { - fprintf(stderr, "set mode submit error\n"); - return -1; + if (!buf) + return -ENOMEM; + + transfer = libusb_alloc_transfer(); + if (!transfer) { + free(buf); + return -ENOMEM; } - return 0; + printf("async set mode %02x\n", data); + libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); + buf[LIBUSB_CONTROL_SETUP_SIZE] = data; + libusb_fill_control_transfer(transfer, devh, buf, bufsize, + cb_mode_changed, NULL, 1000); + + return libusb_submit_transfer(transfer); } static int do_sync_intr(unsigned char *data) { - struct libusb_bulk_transfer_request request = { - .endpoint = EP_INTR, - .data = data, - .length = INTR_LENGTH, - }; int r; int transferred; - r = libusb_interrupt_transfer(devh, &request, &transferred, 1000); + r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, + &transferred, 1000); if (r < 0) { fprintf(stderr, "intr error %d\n", r); return r; @@ -328,14 +278,12 @@ static int next_state(void) return 0; } -static void cb_irq(libusb_dev_handle *_devh, libusb_transfer *urbh, - enum libusb_transfer_status status, unsigned char endpoint, int rqlength, - unsigned char *data, int actual_length, void *user_data) +static void cb_irq(struct libusb_transfer *transfer) { - unsigned char irqtype = data[0]; + unsigned char irqtype = transfer->buffer[0]; - if (status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "irq URB status %d?\n", status); + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "irq transfer status %d?\n", transfer->status); do_exit = 2; return; } @@ -363,16 +311,14 @@ static void cb_irq(libusb_dev_handle *_devh, libusb_transfer *urbh, } break; } - if (submit_irq_urb() < 0) + if (submit_irq_transfer() < 0) do_exit = 2; } -static void cb_img(libusb_dev_handle *_devh, libusb_transfer *urbh, - enum libusb_transfer_status status, unsigned char endpoint, int rqlength, - unsigned char *data, int actual_length, void *user_data) +static void cb_img(struct libusb_transfer *transfer) { - if (status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "img URB status %d?\n", status); + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "img transfer status %d?\n", transfer->status); do_exit = 2; return; } @@ -383,35 +329,31 @@ static void cb_img(libusb_dev_handle *_devh, libusb_transfer *urbh, do_exit = 2; return; } - if (submit_img_urb() < 0) + if (submit_img_transfer() < 0) do_exit = 2; } -static int submit_irq_urb(void) +static int submit_irq_transfer(void) { - libusb_transfer_free(irq_urbh); - irq_urbh = libusb_async_interrupt_transfer(devh, &intrrq, cb_irq, NULL, 0); - return irq_urbh != NULL; + return libusb_submit_transfer(irq_transfer); } -static int submit_img_urb(void) +static int submit_img_transfer(void) { - libusb_transfer_free(img_urbh); - img_urbh = libusb_async_bulk_transfer(devh, &imgrq, cb_img, NULL, 0); - return img_urbh != NULL; + return libusb_submit_transfer(img_transfer); } static int init_capture(void) { int r; - r = submit_irq_urb(); + r = submit_irq_transfer(); if (r < 0) return r; - r = submit_img_urb(); + r = submit_img_transfer(); if (r < 0) { - libusb_transfer_cancel_sync(devh, img_urbh); + libusb_cancel_transfer_sync(devh, img_transfer); return r; } @@ -454,6 +396,24 @@ static int do_init(void) return 0; } +static int alloc_transfers(void) +{ + img_transfer = libusb_alloc_transfer(); + if (!img_transfer) + return -ENOMEM; + + irq_transfer = libusb_alloc_transfer(); + if (!irq_transfer) + return -ENOMEM; + + libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, + sizeof(imgbuf), cb_img, NULL, 0); + libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, + sizeof(irqbuf), cb_irq, NULL, 0); + + return 0; +} + static void sighandler(int signum) { do_exit = 1; @@ -493,6 +453,10 @@ int main(void) /* async from here onwards */ + r = alloc_transfers(); + if (r < 0) + goto out_deinit; + r = init_capture(); if (r < 0) goto out_deinit; @@ -512,11 +476,11 @@ int main(void) printf("shutting down...\n"); - r = libusb_transfer_cancel_sync(devh, irq_urbh); + r = libusb_cancel_transfer_sync(devh, irq_transfer); if (r < 0) goto out_deinit; - r = libusb_transfer_cancel_sync(devh, img_urbh); + r = libusb_cancel_transfer_sync(devh, img_transfer); if (r < 0) goto out_deinit; @@ -526,8 +490,8 @@ int main(void) r = 1; out_deinit: - libusb_transfer_free(img_urbh); - libusb_transfer_free(irq_urbh); + libusb_free_transfer(img_transfer); + libusb_free_transfer(irq_transfer); set_mode(0); set_hwstat(0x80); out_release: diff --git a/libusb/io.c b/libusb/io.c index 0271dcb..accecab 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -32,6 +32,8 @@ #include "libusbi.h" +#define TRANSFER_TO_PRIV(trf) (container_of((trf), struct usbi_transfer, pub)) + /* this is a list of in-flight rb_handles, sorted by timeout expiration. * URBs to timeout the soonest are placed at the beginning of the list, URBs * that will time out later are placed after, and urbs with infinite timeout @@ -49,11 +51,11 @@ void usbi_io_init() fd_removed_cb = NULL; } -static int calculate_timeout(struct libusb_transfer *transfer, - unsigned int timeout) +static int calculate_timeout(struct usbi_transfer *transfer) { int r; struct timespec current_time; + unsigned int timeout = transfer->pub.timeout; if (!timeout) return 0; @@ -76,9 +78,9 @@ static int calculate_timeout(struct libusb_transfer *transfer, return 0; } -static void add_to_flying_list(struct libusb_transfer *transfer) +static void add_to_flying_list(struct usbi_transfer *transfer) { - struct libusb_transfer *cur; + struct usbi_transfer *cur; struct timeval *timeout = &transfer->timeout; /* if we have no other flying transfers, start the list with this one */ @@ -110,16 +112,30 @@ static void add_to_flying_list(struct libusb_transfer *transfer) list_add_tail(&transfer->list, &flying_transfers); } -static int submit_transfer(struct libusb_dev_handle *devh, - struct libusb_transfer *transfer) +static int submit_transfer(struct usbi_transfer *itransfer) { int r; - struct usb_urb *urb = &transfer->urb; - int to_be_transferred = transfer->transfer_len - transfer->transferred; + struct usb_urb *urb = &itransfer->urb; + struct libusb_transfer *transfer = &itransfer->pub; + int to_be_transferred = transfer->length - itransfer->transferred; + + switch (transfer->endpoint_type) { + case LIBUSB_ENDPOINT_TYPE_CONTROL: + urb->type = USB_URB_TYPE_CONTROL; + break; + case LIBUSB_ENDPOINT_TYPE_BULK: + urb->type = USB_URB_TYPE_BULK; + break; + case LIBUSB_ENDPOINT_TYPE_INTERRUPT: + urb->type = USB_URB_TYPE_INTERRUPT; + break; + default: + usbi_err("unknown endpoint type %d", transfer->endpoint_type); + return -EINVAL; + } - urb->type = transfer->urb_type; urb->endpoint = transfer->endpoint; - urb->buffer = transfer->buffer + transfer->transferred; + urb->buffer = transfer->buffer + itransfer->transferred; urb->buffer_length = MIN(to_be_transferred, MAX_URB_BUFFER_LENGTH); /* FIXME: for requests that we have to split into multiple URBs, we should @@ -130,153 +146,95 @@ static int submit_transfer(struct libusb_dev_handle *devh, usbi_dbg("transferring %d from %d bytes", urb->buffer_length, to_be_transferred); - r = ioctl(devh->fd, IOCTL_USB_SUBMITURB, &transfer->urb); + r = ioctl(transfer->dev_handle->fd, IOCTL_USB_SUBMITURB, urb); if (r < 0) { usbi_err("submiturb failed error %d errno=%d", r, errno); return r; } - add_to_flying_list(transfer); + add_to_flying_list(itransfer); return 0; } -API_EXPORTED struct libusb_transfer *libusb_async_control_transfer( - struct libusb_dev_handle *devh, - struct libusb_control_transfer_request *request, - libusb_control_cb_fn callback, void *user_data, unsigned int timeout) +API_EXPORTED size_t libusb_get_transfer_alloc_size(void) { - struct libusb_transfer *transfer = malloc(sizeof(*transfer)); - struct libusb_control_setup *setup; - unsigned char *urbdata; - int urbdata_length = sizeof(struct libusb_control_setup) + request->length; - int r; + return sizeof(struct usbi_transfer); +} - if (!transfer) - return NULL; +void __init_transfer(struct usbi_transfer *transfer) +{ memset(transfer, 0, sizeof(*transfer)); - transfer->devh = devh; - transfer->callback = callback; - transfer->user_data = user_data; - r = calculate_timeout(transfer, timeout); - if (r < 0) { - free(transfer); - return NULL; - } - - urbdata = malloc(urbdata_length); - if (!urbdata) { - free(transfer); - return NULL; - } - - usbi_dbg("RQT=%02x RQ=%02x VAL=%04x IDX=%04x length=%d", - request->requesttype, request->request, request->value, - request->index, request->length); - - setup = (struct libusb_control_setup *) urbdata; - setup->bRequestType = request->requesttype; - setup->bRequest = request->request; - setup->wValue = cpu_to_le16(request->value); - setup->wIndex = cpu_to_le16(request->index); - setup->wLength = cpu_to_le16(request->length); - - if ((request->requesttype & 0x80) == LIBUSB_ENDPOINT_OUT) - memcpy(urbdata + sizeof(struct libusb_control_setup), request->data, - request->length); +} - transfer->urb_type = USB_URB_TYPE_CONTROL; - transfer->buffer = urbdata; - transfer->transfer_len = urbdata_length; +API_EXPORTED void libusb_init_transfer(struct libusb_transfer *transfer) +{ + __init_transfer(TRANSFER_TO_PRIV(transfer)); +} - r = submit_transfer(devh, transfer); - if (r < 0) { - free(transfer); - free(urbdata); +API_EXPORTED struct libusb_transfer *libusb_alloc_transfer(void) +{ + struct usbi_transfer *transfer = malloc(sizeof(*transfer)); + if (!transfer) return NULL; - } - return transfer; + __init_transfer(transfer); + return &transfer->pub; } -static struct libusb_transfer *submit_bulk_transfer( - struct libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, - libusb_bulk_cb_fn callback, void *user_data, unsigned int timeout, - unsigned char urbtype) +API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer) { - struct libusb_transfer *transfer = malloc(sizeof(*transfer)); + struct usbi_transfer *itransfer = TRANSFER_TO_PRIV(transfer); int r; - usbi_dbg("length %d timeout %d", request->length, timeout); + itransfer->transferred = 0; + r = calculate_timeout(itransfer); + if (r < 0) + return r; - if (!transfer) - return NULL; - memset(transfer, 0, sizeof(*transfer)); - r = calculate_timeout(transfer, timeout); - if (r < 0) { - free(transfer); - return NULL; - } - transfer->devh = devh; - transfer->callback = callback; - transfer->user_data = user_data; - transfer->flags |= USBI_TRANSFER_DATA_BELONGS_TO_USER; - transfer->endpoint = request->endpoint; - transfer->urb_type = urbtype; - transfer->buffer = request->data; - transfer->transfer_len = request->length; + if (transfer->endpoint_type == LIBUSB_ENDPOINT_TYPE_CONTROL) { + struct libusb_control_setup *setup = + (struct libusb_control_setup *) transfer->buffer; + + usbi_dbg("RQT=%02x RQ=%02x VAL=%04x IDX=%04x length=%d", + setup->bRequestType, setup->bRequest, setup->wValue, setup->wIndex, + setup->wLength); - r = submit_transfer(devh, transfer); - if (r < 0) { - free(transfer); - return NULL; + setup->wValue = cpu_to_le16(setup->wValue); + setup->wIndex = cpu_to_le16(setup->wIndex); + setup->wLength = cpu_to_le16(setup->wLength); } - return transfer; -} - -API_EXPORTED struct libusb_transfer *libusb_async_bulk_transfer( - struct libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, - libusb_bulk_cb_fn callback, void *user_data, unsigned int timeout) -{ - return submit_bulk_transfer(devh, request, callback, user_data, timeout, - USB_URB_TYPE_BULK); -} - -API_EXPORTED struct libusb_transfer *libusb_async_interrupt_transfer( - struct libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, libusb_bulk_cb_fn callback, - void *user_data, unsigned int timeout) -{ - return submit_bulk_transfer(devh, request, callback, user_data, timeout, - USB_URB_TYPE_INTERRUPT); + return submit_transfer(itransfer); } -API_EXPORTED int libusb_transfer_cancel(struct libusb_dev_handle *devh, +API_EXPORTED int libusb_cancel_transfer(struct libusb_dev_handle *devh, struct libusb_transfer *transfer) { + struct usbi_transfer *itransfer = TRANSFER_TO_PRIV(transfer); int r; + usbi_dbg(""); - r = ioctl(devh->fd, IOCTL_USB_DISCARDURB, &transfer->urb); + r = ioctl(devh->fd, IOCTL_USB_DISCARDURB, &itransfer->urb); if (r < 0) usbi_err("cancel transfer failed error %d", r); return r; } -API_EXPORTED int libusb_transfer_cancel_sync(struct libusb_dev_handle *devh, +API_EXPORTED int libusb_cancel_transfer_sync(struct libusb_dev_handle *devh, struct libusb_transfer *transfer) { + struct usbi_transfer *itransfer = TRANSFER_TO_PRIV(transfer); int r; + usbi_dbg(""); - r = ioctl(devh->fd, IOCTL_USB_DISCARDURB, &transfer->urb); + r = ioctl(devh->fd, IOCTL_USB_DISCARDURB, &itransfer->urb); if (r < 0) { usbi_err("cancel transfer failed error %d", r); return r; } - transfer->flags |= USBI_TRANSFER_SYNC_CANCELLED; - while (transfer->flags & USBI_TRANSFER_SYNC_CANCELLED) { + itransfer->flags |= USBI_TRANSFER_SYNC_CANCELLED; + while (itransfer->flags & USBI_TRANSFER_SYNC_CANCELLED) { r = libusb_poll(); if (r < 0) return r; @@ -285,33 +243,22 @@ API_EXPORTED int libusb_transfer_cancel_sync(struct libusb_dev_handle *devh, return 0; } -int handle_transfer_completion(struct libusb_dev_handle *devh, - struct libusb_transfer *transfer, enum libusb_transfer_status status) +static void handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status) { - struct usb_urb *urb = &transfer->urb; + struct libusb_transfer *transfer = &itransfer->pub; if (status == LIBUSB_TRANSFER_SILENT_COMPLETION) - return 0; + return; - if (transfer->urb_type == USB_URB_TYPE_CONTROL) { - libusb_control_cb_fn callback = transfer->callback; - if (callback) - callback(devh, transfer, status, urb->buffer, - urb->buffer + sizeof(struct libusb_control_setup), - transfer->transferred, transfer->user_data); - } else if (urb->type == USB_URB_TYPE_BULK || - urb->type == USB_URB_TYPE_INTERRUPT) { - libusb_bulk_cb_fn callback = transfer->callback; - if (callback) - callback(devh, transfer, status, transfer->endpoint, - transfer->transfer_len, transfer->buffer, - transfer->transferred, transfer->user_data); - } - return 0; + transfer->status = status; + transfer->actual_length = itransfer->transferred; + if (transfer->callback) + transfer->callback(transfer); } -static int handle_transfer_cancellation(struct libusb_dev_handle *devh, - struct libusb_transfer *transfer) +static void handle_transfer_cancellation(struct libusb_dev_handle *devh, + struct usbi_transfer *transfer) { /* if the URB is being cancelled synchronously, raise cancellation * completion event by unsetting flag, and ensure that user callback does @@ -320,28 +267,29 @@ static int handle_transfer_cancellation(struct libusb_dev_handle *devh, if (transfer->flags & USBI_TRANSFER_SYNC_CANCELLED) { transfer->flags &= ~USBI_TRANSFER_SYNC_CANCELLED; usbi_dbg("detected sync. cancel"); - return handle_transfer_completion(devh, transfer, - LIBUSB_TRANSFER_SILENT_COMPLETION); + handle_transfer_completion(transfer, LIBUSB_TRANSFER_SILENT_COMPLETION); + return; } /* if the URB was cancelled due to timeout, report timeout to the user */ if (transfer->flags & USBI_TRANSFER_TIMED_OUT) { usbi_dbg("detected timeout cancellation"); - return handle_transfer_completion(devh, transfer, - LIBUSB_TRANSFER_TIMED_OUT); + handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT); + return; } /* otherwise its a normal async cancel */ - return handle_transfer_completion(devh, transfer, - LIBUSB_TRANSFER_CANCELLED); + handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED); } static int reap_for_devh(struct libusb_dev_handle *devh) { int r; struct usb_urb *urb; + struct usbi_transfer *itransfer; struct libusb_transfer *transfer; int trf_requested; + int length; r = ioctl(devh->fd, IOCTL_USB_REAPURBNDELAY, &urb); if (r == -1 && errno == EAGAIN) @@ -351,58 +299,65 @@ static int reap_for_devh(struct libusb_dev_handle *devh) return r; } - transfer = container_of(urb, struct libusb_transfer, urb); + itransfer = container_of(urb, struct usbi_transfer, urb); + transfer = &itransfer->pub; usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status, urb->actual_length); - list_del(&transfer->list); + list_del(&itransfer->list); + + if (urb->status == -2) { + handle_transfer_cancellation(devh, itransfer); + return 0; + } - if (urb->status == -2) - return handle_transfer_cancellation(devh, transfer); /* FIXME: research what other status codes may exist */ if (urb->status != 0) usbi_warn("unrecognised urb status %d", urb->status); /* determine how much data was asked for */ - trf_requested = MIN(transfer->transfer_len - transfer->transferred, + length = transfer->length; + if (transfer->endpoint_type == LIBUSB_ENDPOINT_TYPE_CONTROL) + length -= LIBUSB_CONTROL_SETUP_SIZE; + trf_requested = MIN(length - itransfer->transferred, MAX_URB_BUFFER_LENGTH); - transfer->transferred += urb->actual_length; + itransfer->transferred += urb->actual_length; /* if we were provided less data than requested, then our transfer is * done */ if (urb->actual_length < trf_requested) { usbi_dbg("less data than requested (%d/%d) --> all done", urb->actual_length, trf_requested); - return handle_transfer_completion(devh, transfer, - LIBUSB_TRANSFER_COMPLETED); + handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); + return 0; } /* if we've transferred all data, we're done */ - if (transfer->transferred == transfer->transfer_len) { + if (itransfer->transferred == length) { usbi_dbg("transfer complete --> all done"); - return handle_transfer_completion(devh, transfer, - LIBUSB_TRANSFER_COMPLETED); + handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); + return 0; } /* otherwise, we have more data to transfer */ usbi_dbg("more data to transfer..."); memset(urb, 0, sizeof(*urb)); - return submit_transfer(devh, transfer); + return submit_transfer(itransfer); } -static void handle_timeout(struct libusb_transfer *transfer) +static void handle_timeout(struct usbi_transfer *itransfer) { /* handling timeouts is tricky, as we may race with the kernel: we may * detect a timeout racing with the condition that the urb has actually * completed. we asynchronously cancel the URB and report timeout * to the user when the URB cancellation completes (or not at all if the * URB actually gets delivered as per this race) */ + struct libusb_transfer *transfer = &itransfer->pub; int r; - - transfer->flags |= USBI_TRANSFER_TIMED_OUT; - r = libusb_transfer_cancel(transfer->devh, transfer); + itransfer->flags |= USBI_TRANSFER_TIMED_OUT; + r = libusb_cancel_transfer(transfer->dev_handle, transfer); if (r < 0) usbi_warn("async cancel failed %d errno=%d", r, errno); } @@ -411,7 +366,7 @@ static int handle_timeouts(void) { struct timespec systime_ts; struct timeval systime; - struct libusb_transfer *transfer; + struct usbi_transfer *transfer; int r; if (list_empty(&flying_transfers)) @@ -526,7 +481,7 @@ API_EXPORTED int libusb_poll(void) API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) { - struct libusb_transfer *transfer; + struct usbi_transfer *transfer; struct timespec cur_ts; struct timeval cur_tv; struct timeval *next_timeout; @@ -577,64 +532,73 @@ API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) return 1; } -struct sync_ctrl_handle { - enum libusb_transfer_status status; - unsigned char *data; - int actual_length; -}; - -static void ctrl_transfer_cb(struct libusb_dev_handle *devh, - struct libusb_transfer *transfer, enum libusb_transfer_status status, - struct libusb_control_setup *setup, unsigned char *data, int actual_length, - void *user_data) +static void ctrl_transfer_cb(struct libusb_transfer *transfer) { - struct sync_ctrl_handle *ctrlh = (struct sync_ctrl_handle *) user_data; - usbi_dbg("actual_length=%d", actual_length); - - if (status == LIBUSB_TRANSFER_COMPLETED) { - /* copy results into user-defined buffer */ - if (setup->bRequestType & LIBUSB_ENDPOINT_IN) - memcpy(ctrlh->data, data, actual_length); - } - - ctrlh->status = status; - ctrlh->actual_length = actual_length; - /* caller frees transfer */ + int *completed = transfer->user_data; + *completed = 1; + usbi_dbg("actual_length=%d", transfer->actual_length); + /* caller interprets result and frees transfer */ } -API_EXPORTED int libusb_control_transfer(struct libusb_dev_handle *devh, - struct libusb_control_transfer_request *request, unsigned int timeout) +API_EXPORTED int libusb_control_transfer(libusb_dev_handle *dev_handle, + uint8_t bRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout) { - struct libusb_transfer *transfer; - struct sync_ctrl_handle ctrlh; - - memset(&ctrlh, 0, sizeof(ctrlh)); - ctrlh.data = request->data; + struct libusb_transfer *transfer = libusb_alloc_transfer(); + unsigned char *buffer; + int length = wLength + LIBUSB_CONTROL_SETUP_SIZE; + int completed = 0; + int r; - transfer = libusb_async_control_transfer(devh, request, ctrl_transfer_cb, - &ctrlh, timeout); if (!transfer) - return -1; + return -ENOMEM; + + buffer = malloc(length); + if (!buffer) { + libusb_free_transfer(transfer); + return -ENOMEM; + } - while (!ctrlh.status) { - int r = libusb_poll(); + libusb_fill_control_setup(buffer, bRequestType, bRequest, wValue, wIndex, + wLength); + if ((bRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); + + libusb_fill_control_transfer(transfer, dev_handle, buffer, length, + ctrl_transfer_cb, &completed, timeout); + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + while (!completed) { + r = libusb_poll(); if (r < 0) { - libusb_transfer_cancel_sync(devh, transfer); - libusb_transfer_free(transfer); + libusb_cancel_transfer_sync(dev_handle, transfer); + libusb_free_transfer(transfer); return r; } } - libusb_transfer_free(transfer); - switch (ctrlh.status) { + if ((bRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + memcpy(data, libusb_control_transfer_get_data(transfer), + transfer->actual_length); + + switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: - return ctrlh.actual_length; + r = transfer->actual_length; + break; case LIBUSB_TRANSFER_TIMED_OUT: - return -ETIMEDOUT; + r = -ETIMEDOUT; + break; default: - usbi_warn("unrecognised status code %d", ctrlh.status); - return -1; + usbi_warn("unrecognised status code %d", transfer->status); + r = -1; } + + libusb_free_transfer(transfer); + return r; } struct sync_bulk_handle { @@ -642,79 +606,87 @@ struct sync_bulk_handle { int actual_length; }; -static void bulk_transfer_cb(struct libusb_dev_handle *devh, - struct libusb_transfer *transfer, enum libusb_transfer_status status, - unsigned char endpoint, int rqlength, unsigned char *data, - int actual_length, void *user_data) +static void bulk_transfer_cb(struct libusb_transfer *transfer) { - struct sync_bulk_handle *bulkh = (struct sync_bulk_handle *) user_data; - usbi_dbg(""); - bulkh->status = status; - bulkh->actual_length = actual_length; - /* caller frees transfer */ + int *completed = transfer->user_data; + *completed = 1; + usbi_dbg("actual_length=%d", transfer->actual_length); + /* caller interprets results and frees transfer */ } -static int do_sync_bulk_transfer(struct libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, int *transferred, - unsigned int timeout, unsigned char urbtype) +static int do_sync_bulk_transfer(struct libusb_dev_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + int *transferred, unsigned int timeout, unsigned char endpoint_type) { - struct libusb_transfer *transfer; - struct sync_bulk_handle bulkh; - - memset(&bulkh, 0, sizeof(bulkh)); + struct libusb_transfer *transfer = libusb_alloc_transfer(); + int completed = 0; + int r; - transfer = submit_bulk_transfer(devh, request, bulk_transfer_cb, &bulkh, - timeout, urbtype); if (!transfer) - return -1; + return -ENOMEM; + + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, + bulk_transfer_cb, &completed, timeout); + transfer->endpoint_type = endpoint_type; + + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } - while (!bulkh.status) { - int r = libusb_poll(); + while (!completed) { + r = libusb_poll(); if (r < 0) { - libusb_transfer_cancel_sync(devh, transfer); - libusb_transfer_free(transfer); + libusb_cancel_transfer_sync(dev_handle, transfer); + libusb_free_transfer(transfer); return r; } } - *transferred = bulkh.actual_length; - libusb_transfer_free(transfer); - - switch (bulkh.status) { + *transferred = transfer->actual_length; + switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: - return 0; + r = 0; + break; case LIBUSB_TRANSFER_TIMED_OUT: - return -ETIMEDOUT; + r = -ETIMEDOUT; + break; default: - usbi_warn("unrecognised status code %d", bulkh.status); - return -1; + usbi_warn("unrecognised status code %d", transfer->status); + r = -1; } + + libusb_free_transfer(transfer); + return r; } -API_EXPORTED int libusb_interrupt_transfer(struct libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, int *transferred, +/* FIXME: should transferred be the return value? */ +API_EXPORTED int libusb_bulk_transfer(struct libusb_dev_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout) { - return do_sync_bulk_transfer(devh, request, transferred, timeout, - USB_URB_TYPE_INTERRUPT); + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_ENDPOINT_TYPE_BULK); } -API_EXPORTED int libusb_bulk_transfer(struct libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, int *transferred, +/* FIXME: do we need an interval param here? usbfs doesn't expose it? */ +API_EXPORTED int libusb_interrupt_transfer(struct libusb_dev_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout) { - return do_sync_bulk_transfer(devh, request, transferred, timeout, - USB_URB_TYPE_BULK); + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_ENDPOINT_TYPE_INTERRUPT); } -API_EXPORTED void libusb_transfer_free(struct libusb_transfer *transfer) +API_EXPORTED void libusb_free_transfer(struct libusb_transfer *transfer) { + struct usbi_transfer *itransfer; if (!transfer) return; - if (!(transfer->flags & USBI_TRANSFER_DATA_BELONGS_TO_USER)) - free(transfer->urb.buffer); - free(transfer); + itransfer = TRANSFER_TO_PRIV(transfer); + free(itransfer); } API_EXPORTED void libusb_set_pollfd_notifiers(libusb_pollfd_added_cb added_cb, diff --git a/libusb/libusb.h b/libusb/libusb.h index ea4fbc4..a7496f3 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -168,8 +168,6 @@ struct libusb_config_descriptor { int extralen; }; -/* off-the-wire structures */ - struct libusb_control_setup { uint8_t bRequestType; uint8_t bRequest; @@ -178,6 +176,8 @@ struct libusb_control_setup { uint16_t wLength; } __attribute__((packed)); +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + /* libusb */ struct libusb_device; @@ -186,9 +186,6 @@ typedef struct libusb_device libusb_device; struct libusb_dev_handle; typedef struct libusb_dev_handle libusb_dev_handle; -struct libusb_transfer; -typedef struct libusb_transfer libusb_transfer; - enum libusb_transfer_status { LIBUSB_TRANSFER_SILENT_COMPLETION = 0, LIBUSB_TRANSFER_COMPLETED, @@ -196,30 +193,24 @@ enum libusb_transfer_status { LIBUSB_TRANSFER_CANCELLED, }; -struct libusb_control_transfer_request { - uint8_t requesttype; - uint8_t request; - uint16_t value; - uint16_t index; - uint16_t length; - unsigned char *data; -}; +struct libusb_transfer; -typedef void (*libusb_control_cb_fn)(libusb_dev_handle *devh, - libusb_transfer *transfer, enum libusb_transfer_status status, - struct libusb_control_setup *setup, unsigned char *data, - int actual_length, void *user_data); +typedef void (*libusb_transfer_cb_fn)(struct libusb_transfer *transfer); -struct libusb_bulk_transfer_request { +struct libusb_transfer { + libusb_dev_handle *dev_handle; unsigned char endpoint; - unsigned char *data; + unsigned char endpoint_type; + unsigned int timeout; + enum libusb_transfer_status status; + int length; -}; + int actual_length; + libusb_transfer_cb_fn callback; + void *user_data; -typedef void (*libusb_bulk_cb_fn)(libusb_dev_handle *devh, - libusb_transfer *transfer, enum libusb_transfer_status status, - unsigned char endpoint, int rqlength, unsigned char *data, - int actual_length, void *user_data); + unsigned char *buffer; +}; int libusb_init(void); void libusb_exit(void); @@ -242,31 +233,98 @@ libusb_dev_handle *libusb_open_device_with_vid_pid(uint16_t vendor_id, /* async I/O */ -libusb_transfer *libusb_async_control_transfer(libusb_dev_handle *devh, - struct libusb_control_transfer_request *request, - libusb_control_cb_fn callback, void *user_data, unsigned int timeout); -libusb_transfer *libusb_async_bulk_transfer(libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, libusb_bulk_cb_fn callback, - void *user_data, unsigned int timeout); -libusb_transfer *libusb_async_interrupt_transfer(libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, libusb_bulk_cb_fn callback, - void *user_data, unsigned int timeout); - -int libusb_transfer_cancel(libusb_dev_handle *devh, libusb_transfer *transfer); -int libusb_transfer_cancel_sync(libusb_dev_handle *devh, - libusb_transfer *transfer); -void libusb_transfer_free(libusb_transfer *transfer); +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *) transfer->buffer; +} + +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + setup->bRequestType = bRequestType; + setup->bRequest = bRequest; + setup->wValue = wValue; + setup->wIndex = wIndex; + setup->wLength = wLength; +} + +size_t libusb_get_transfer_alloc_size(void); +void libusb_init_transfer(struct libusb_transfer *transfer); + +struct libusb_transfer *libusb_alloc_transfer(void); +int libusb_submit_transfer(struct libusb_transfer *transfer); +int libusb_cancel_transfer(libusb_dev_handle *devh, + struct libusb_transfer *transfer); +int libusb_cancel_transfer_sync(libusb_dev_handle *devh, + struct libusb_transfer *transfer); +void libusb_free_transfer(struct libusb_transfer *transfer); + +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_dev_handle *dev_handle, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->endpoint_type = LIBUSB_ENDPOINT_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_dev_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->endpoint_type = LIBUSB_ENDPOINT_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_dev_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->endpoint_type = LIBUSB_ENDPOINT_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} /* sync I/O */ -int libusb_control_transfer(libusb_dev_handle *devh, - struct libusb_control_transfer_request *request, unsigned int timeout); -int libusb_bulk_transfer(libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, int *transferred, - unsigned int timeout); -int libusb_interrupt_transfer(libusb_dev_handle *devh, - struct libusb_bulk_transfer_request *request, int *transferred, - unsigned int timeout); +int libusb_control_transfer(libusb_dev_handle *dev_handle, + uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, + unsigned char *data, uint16_t length, unsigned int timeout); + +int libusb_bulk_transfer(libusb_dev_handle *dev_handle, unsigned char endpoint, + unsigned char *data, int length, int *actual_length, unsigned int timeout); + +int libusb_interrupt_transfer(libusb_dev_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); /* polling and timeouts */ struct libusb_pollfd { diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 185e54d..be0fa55 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -157,27 +157,17 @@ struct libusb_dev_handle { int fd; }; -enum libusb_transfer_type { - LIBUSB_TRANSFER_CONTROL, - LIBUSB_TRANSFER_BULK, -}; +#define USBI_TRANSFER_SYNC_CANCELLED (1<<0) +#define USBI_TRANSFER_TIMED_OUT (1<<1) -#define USBI_TRANSFER_DATA_BELONGS_TO_USER (1<<0) -#define USBI_TRANSFER_SYNC_CANCELLED (1<<1) -#define USBI_TRANSFER_TIMED_OUT (1<<2) +struct usbi_transfer { + /* must come first */ + struct libusb_transfer pub; -struct libusb_transfer { - struct libusb_dev_handle *devh; struct usb_urb urb; struct list_head list; struct timeval timeout; - unsigned char urb_type; - unsigned char endpoint; - int transfer_len; int transferred; - unsigned char *buffer; - void *callback; - void *user_data; uint8_t flags; }; |