From 66348c90ea4570bf999ac301089e006d0cce1926 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 9 Mar 2008 00:58:09 +0000 Subject: [PATCH] Separate transfer allocation and submission Leads to some hefty API changes. Now we're much more similar to the Linux kernel model. Problems with dealing with asynchronous control transfers are passed on to the user, basically you must allocate a buffer, start with the setup, and put the data after. This won't make much sense until documented (soon...) --- TODO | 25 ++- examples/dpfp.c | 186 +++++++++------------- libusb/io.c | 458 ++++++++++++++++++++++++++----------------------------- libusb/libusb.h | 150 ++++++++++++------ libusb/libusbi.h | 20 +-- 5 files changed, 411 insertions(+), 428 deletions(-) diff --git a/TODO b/TODO index ddf3439..fdc72e8 100644 --- a/TODO +++ b/TODO @@ -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; }; -- 2.7.4