summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO25
-rw-r--r--examples/dpfp.c186
-rw-r--r--libusb/io.c458
-rw-r--r--libusb/libusb.h150
-rw-r--r--libusb/libusbi.h20
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;
};