summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-03-13 12:36:56 +0000
committerDaniel Drake <dsd@gentoo.org>2008-03-13 12:46:08 +0000
commitc0c9432d38b22784070dce3a7874c62c31786a27 (patch)
tree825ca9817d3609a0645dbe3f1a1e46aa40dc1ff9
parentde4c5341d168697baa4c0901c406deb47e78aae7 (diff)
downloadlibusb-c0c9432d38b22784070dce3a7874c62c31786a27.tar.gz
Beginnings of cross-platform abstraction
This also includes a libusb_get_pollfds API change
-rw-r--r--TODO1
-rw-r--r--configure.ac14
-rw-r--r--examples/dpfp.c2
-rw-r--r--libusb/Makefile.am10
-rw-r--r--libusb/core.c397
-rw-r--r--libusb/io.c221
-rw-r--r--libusb/libusb.h2
-rw-r--r--libusb/libusbi.h85
-rw-r--r--libusb/os/linux_usbfs.c584
-rw-r--r--libusb/os/linux_usbfs.h (renamed from libusb/usbfs.h)78
10 files changed, 874 insertions, 520 deletions
diff --git a/TODO b/TODO
index 8506952..de81ef9 100644
--- a/TODO
+++ b/TODO
@@ -4,7 +4,6 @@ cancellation race concerns - better tracking of kernel feedback?
API docs
isochronous endpoint I/O
thread safety
-abstraction for cross-platform-ness
error codes
fixme review
review functionality missing over 0.1
diff --git a/configure.ac b/configure.ac
index 58b4aa2..e8ae187 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,6 +10,20 @@ AC_C_INLINE
AM_PROG_CC_C_O
AC_DEFINE([_GNU_SOURCE], [], [Use GNU extensions])
+AC_MSG_CHECKING([operating system])
+case $host in
+*-linux*)
+ AC_DEFINE(OS_LINUX, [], [Linux backend])
+ AC_SUBST(OS_LINUX)
+ AC_MSG_RESULT([Linux])
+ backend="linux"
+ ;;
+*)
+ AC_MSG_ERROR([unsupported operating system])
+esac
+
+AM_CONDITIONAL([OS_LINUX], [test "x$backend" == "xlinux"])
+
# Library versioning
lt_major="0"
lt_revision="0"
diff --git a/examples/dpfp.c b/examples/dpfp.c
index 70944f8..47d9fc6 100644
--- a/examples/dpfp.c
+++ b/examples/dpfp.c
@@ -270,7 +270,7 @@ static int next_state(void)
printf("unrecognised state %d\n", state);
}
if (r < 0) {
- fprintf(stderr, "error detected changing state");
+ fprintf(stderr, "error detected changing state\n");
return r;
}
diff --git a/libusb/Makefile.am b/libusb/Makefile.am
index 462bd2f..fb5d033 100644
--- a/libusb/Makefile.am
+++ b/libusb/Makefile.am
@@ -1,7 +1,15 @@
lib_LTLIBRARIES = libusb-1.0.la
+LINUX_USBFS_SRC = os/linux_usbfs.h os/linux_usbfs.c
+
+EXTRA_DIST = $(LINUX_USBFS_SRC)
+
+if OS_LINUX
+OS_SRC = $(LINUX_USBFS_SRC)
+endif
+
libusb_1_0_la_CFLAGS = -fvisibility=hidden $(AM_CFLAGS)
-libusb_1_0_la_SOURCES = libusbi.h usbfs.h core.c descriptor.c io.c sync.c
+libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c $(OS_SRC)
libusb_1_0_la_LIBADD = -lrt
hdrdir = $(includedir)/libusb-1.0
diff --git a/libusb/core.c b/libusb/core.c
index 8d7abcf..68d32ce 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -20,66 +20,24 @@
#include <config.h>
-#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
-#include <features.h>
#include <poll.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "libusb.h"
#include "libusbi.h"
-struct list_head usb_devs;
-struct list_head open_devs;
-static const char *usbfs_path = NULL;
+#ifdef OS_LINUX
+const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
+#else
+#error "Unsupported OS"
+#endif
-static int check_usb_vfs(const char *dirname)
-{
- DIR *dir;
- struct dirent *entry;
- int found = 0;
-
- dir = opendir(dirname);
- if (!dir)
- return 0;
-
- while ((entry = readdir(dir)) != NULL) {
- if (entry->d_name[0] == '.')
- continue;
-
- /* We assume if we find any files that it must be the right place */
- found = 1;
- break;
- }
-
- closedir(dir);
- return found;
-}
-
-static const char *find_usbfs_path(void)
-{
- const char *path = "/dev/bus/usb";
- const char *ret = NULL;
-
- if (check_usb_vfs(path)) {
- ret = path;
- } else {
- path = "/proc/bus/usb";
- if (check_usb_vfs(path))
- ret = path;
- }
-
- usbi_dbg("found usbfs at %s", ret);
- return ret;
-}
+static struct list_head usb_devs;
+struct list_head usbi_open_devs;
/* we traverse usbfs without knowing how many devices we are going to find.
* so we create this discovered_devs model which is similar to a linked-list
@@ -87,11 +45,6 @@ static const char *find_usbfs_path(void)
* eliminating the need for a list node in the libusb_device structure
* itself. */
#define DISCOVERED_DEVICES_SIZE_STEP 8
-struct discovered_devs {
- size_t len;
- size_t capacity;
- struct libusb_device *devices[0];
-};
static struct discovered_devs *discovered_devs_alloc(void)
{
@@ -107,7 +60,7 @@ static struct discovered_devs *discovered_devs_alloc(void)
/* append a device to the discovered devices collection. may realloc itself,
* returning new discdevs. returns NULL on realloc failure. */
-static struct discovered_devs *discovered_devs_append(
+struct discovered_devs *discovered_devs_append(
struct discovered_devs *discdevs, struct libusb_device *dev)
{
size_t len = discdevs->len;
@@ -144,115 +97,21 @@ static void discovered_devs_free(struct discovered_devs *discdevs)
free(discdevs);
}
-static struct libusb_device *device_new(uint8_t busnum, uint8_t devaddr)
+struct libusb_device *usbi_alloc_device(unsigned long session_id)
{
- char path[PATH_MAX + 1];
- unsigned char raw_desc[DEVICE_DESC_LENGTH];
- struct libusb_device *dev = malloc(sizeof(*dev));
- int fd = 0;
- int i;
- int r;
- int tmp;
-
+ size_t priv_size = usbi_backend->device_priv_size;
+ struct libusb_device *dev = malloc(sizeof(*dev) + priv_size);
if (!dev)
return NULL;
dev->refcnt = 1;
- dev->nodepath = NULL;
- dev->config = NULL;
-
- snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, busnum, devaddr);
- usbi_dbg("%s", path);
- fd = open(path, O_RDWR);
- if (!fd) {
- usbi_dbg("open '%s' failed, ret=%d errno=%d", path, fd, errno);
- /* FIXME this might not be an error if the file has gone away due
- * to unplugging */
- goto err;
- }
-
- r = read(fd, raw_desc, DEVICE_DESC_LENGTH);
- if (r < 0) {
- usbi_err("read failed ret=%d errno=%d", r, errno);
- goto err;
- }
- /* FIXME: short read handling? */
-
- usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", &dev->desc);
-
- /* Now try to fetch the rest of the descriptors */
- if (dev->desc.bNumConfigurations > USB_MAXCONFIG) {
- usbi_err("too many configurations");
- goto err;
- }
-
- if (dev->desc.bNumConfigurations < 1) {
- usbi_dbg("no configurations?");
- goto err;
- }
-
- tmp = dev->desc.bNumConfigurations * sizeof(struct libusb_config_descriptor);
- dev->config = malloc(tmp);
- if (!dev->config)
- goto err;
-
- memset(dev->config, 0, tmp);
- for (i = 0; i < dev->desc.bNumConfigurations; i++) {
- unsigned char buffer[8], *bigbuffer;
- struct libusb_config_descriptor config;
-
- /* Get the first 8 bytes to figure out what the total length is */
- r = read(fd, buffer, sizeof(buffer));
- if (r < sizeof(buffer)) {
- usbi_err("short descriptor read (%d/%d)", r, sizeof(buffer));
- goto err;
- }
-
- usbi_parse_descriptor(buffer, "bbw", &config);
-
- bigbuffer = malloc(config.wTotalLength);
- if (!bigbuffer)
- goto err;
-
- /* Read the rest of the config descriptor */
- memcpy(bigbuffer, buffer, sizeof(buffer));
-
- tmp = config.wTotalLength - 8;
- r = read(fd, bigbuffer + 8, tmp);
- if (r < tmp) {
- usbi_err("short descriptor read (%d/%d)", r, tmp);
- free(bigbuffer);
- goto err;
- }
-
- r = usbi_parse_configuration(&dev->config[i], bigbuffer);
- if (r > 0)
- usbi_warn("descriptor data still left\n");
- free(bigbuffer);
- }
-
- dev->nodepath = strdup(path);
- if (!dev->nodepath)
- goto err;
-
- dev->session_data = busnum << 8 | devaddr;
+ dev->session_data = session_id;
list_add(&dev->list, &usb_devs);
- close(fd);
+ memset(&dev->os_priv, 0, priv_size);
return dev;
-
-err:
- if (fd)
- close(fd);
- if (dev->config)
- free(dev->config);
- if (dev->nodepath)
- free(dev->nodepath);
- if (dev)
- free(dev);
- return NULL;
}
-static struct libusb_device *get_device_by_session_id(unsigned long session_id)
+struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id)
{
struct libusb_device *dev;
@@ -263,103 +122,9 @@ static struct libusb_device *get_device_by_session_id(unsigned long session_id)
return NULL;
}
-/* open a device file, set up the libusb_device structure for it, and add it to
- * discdevs. on failure (non-zero return) the pre-existing discdevs should
- * be destroyed (and devices freed). on success, the new discdevs pointer
- * should be used it may have been moved. */
-static int scan_device(struct discovered_devs **_discdevs, uint8_t busnum,
- uint8_t devaddr)
-{
- struct discovered_devs *discdevs;
- unsigned long session_id;
- struct libusb_device *dev;
- int need_unref = 0;
- int r = 0;
-
- /* FIXME: session ID is not guaranteed unique as addresses can wrap and
- * will be reused. instead we should add a simple sysfs attribute with
- * a session ID. */
- session_id = busnum << 8 | devaddr;
- usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
- session_id);
-
- dev = get_device_by_session_id(session_id);
- if (dev) {
- usbi_dbg("using existing device for %d/%d (session %ld)",
- busnum, devaddr, session_id);
- } else {
- usbi_dbg("allocating new device for %d/%d (session %ld)",
- busnum, devaddr, session_id);
- dev = device_new(busnum, devaddr);
- if (!dev) {
- r = -EIO;
- goto out;
- }
- need_unref = 1;
- }
-
- discdevs = discovered_devs_append(*_discdevs, dev);
- if (!discdevs)
- r = -ENOMEM;
- else
- *_discdevs = discdevs;
-
-out:
- if (need_unref)
- libusb_device_unref(dev);
- return r;
-}
-
-/* open a bus directory and adds all discovered devices to discdevs. on
- * failure (non-zero return) the pre-existing discdevs should be destroyed
- * (and devices freed). on success, the new discdevs pointer should be used
- * as it may have been moved. */
-static int scan_busdir(struct discovered_devs **_discdevs, uint8_t busnum)
-{
- DIR *dir;
- char dirpath[PATH_MAX + 1];
- struct dirent *entry;
- struct discovered_devs *discdevs = *_discdevs;
- int r = 0;
-
- snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum);
- usbi_dbg("%s", dirpath);
- dir = opendir(dirpath);
- if (!dir) {
- usbi_err("opendir '%s' failed, errno=%d", dirpath, errno);
- /* FIXME: should handle valid race conditions like hub unplugged
- * during directory iteration - this is not an error */
- return -1;
- }
-
- while ((entry = readdir(dir))) {
- int devaddr;
-
- if (entry->d_name[0] == '.')
- continue;
-
- devaddr = atoi(entry->d_name);
- if (devaddr == 0) {
- usbi_dbg("unknown dir entry %s", entry->d_name);
- continue;
- }
-
- r = scan_device(&discdevs, busnum, (uint8_t) devaddr);
- if (r < 0)
- goto out;
- }
-
- *_discdevs = discdevs;
-out:
- closedir(dir);
- return r;
-}
-
API_EXPORTED int libusb_get_device_list(struct libusb_device ***list)
{
- DIR *buses;
struct discovered_devs *discdevs = discovered_devs_alloc();
- struct dirent *entry;
struct libusb_device **ret;
int r = 0;
size_t i;
@@ -369,30 +134,9 @@ API_EXPORTED int libusb_get_device_list(struct libusb_device ***list)
if (!discdevs)
return -ENOMEM;
- buses = opendir(usbfs_path);
- if (!buses) {
- usbi_err("opendir buses failed errno=%d", errno);
- return -1;
- }
-
- while ((entry = readdir(buses))) {
- struct discovered_devs *discdevs_new = discdevs;
- int busnum;
-
- if (entry->d_name[0] == '.')
- continue;
-
- busnum = atoi(entry->d_name);
- if (busnum == 0) {
- usbi_dbg("unknown dir entry %s", entry->d_name);
- continue;
- }
-
- r = scan_busdir(&discdevs_new, busnum);
- if (r < 0)
- goto out;
- discdevs = discdevs_new;
- }
+ r = usbi_backend->get_device_list(discdevs);
+ if (r < 0)
+ goto out;
/* convert discovered_devs into a list */
len = discdevs->len;
@@ -411,7 +155,6 @@ API_EXPORTED int libusb_get_device_list(struct libusb_device ***list)
out:
discovered_devs_free(discdevs);
- closedir(buses);
return r;
}
@@ -445,9 +188,13 @@ API_EXPORTED void libusb_device_unref(struct libusb_device *dev)
if (--dev->refcnt == 0) {
usbi_dbg("destroy device %04x:%04x", dev->desc.idVendor,
dev->desc.idProduct);
+
+ if (usbi_backend->destroy_device)
+ usbi_backend->destroy_device(dev);
+
list_del(&dev->list);
- free(dev->config);
- free(dev->nodepath);
+ if (dev->config)
+ free(dev->config);
free(dev);
}
}
@@ -466,27 +213,26 @@ API_EXPORTED struct libusb_config_descriptor *libusb_get_config_descriptor(
API_EXPORTED struct libusb_device_handle *libusb_open(struct libusb_device *dev)
{
- struct libusb_device_handle *devh;
- int fd;
+ struct libusb_device_handle *handle;
+ size_t priv_size = usbi_backend->device_handle_priv_size;
+ int r;
usbi_dbg("open %04x:%04x", dev->desc.idVendor, dev->desc.idProduct);
- fd = open(dev->nodepath, O_RDWR);
- if (!fd) {
- usbi_err("open failed, code %d errno %d", fd, errno);
+ handle = malloc(sizeof(*handle) + priv_size);
+ if (!handle)
return NULL;
- }
- devh = malloc(sizeof(*devh));
- if (!devh) {
- close(fd);
+ handle->dev = libusb_device_ref(dev);
+ memset(&handle->os_priv, 0, priv_size);
+ r = usbi_backend->open(handle);
+ if (r < 0) {
+ libusb_device_unref(dev);
+ free(handle);
return NULL;
}
- devh->fd = fd;
- devh->dev = libusb_device_ref(dev);
- list_add(&devh->list, &open_devs);
- usbi_add_pollfd(fd, POLLOUT);
- return devh;
+ list_add(&handle->list, &usbi_open_devs);
+ return handle;
}
/* convenience function for finding a device with a particular vendor/product
@@ -499,7 +245,7 @@ API_EXPORTED struct libusb_device_handle *libusb_open_device_with_vid_pid(
struct libusb_device **devs;
struct libusb_device *found = NULL;
struct libusb_device *dev;
- struct libusb_device_handle *devh;
+ struct libusb_device_handle *handle = NULL;
size_t i = 0;
if (libusb_get_device_list(&devs) < 0)
@@ -515,17 +261,16 @@ API_EXPORTED struct libusb_device_handle *libusb_open_device_with_vid_pid(
}
if (found)
- devh = libusb_open(found);
+ handle = libusb_open(found);
libusb_free_device_list(devs, 1);
- return devh;
+ return handle;
}
-static void do_close(struct libusb_device_handle *devh)
+static void do_close(struct libusb_device_handle *dev_handle)
{
- usbi_remove_pollfd(devh->fd);
- close(devh->fd);
- libusb_device_unref(devh->dev);
+ usbi_backend->close(dev_handle);
+ libusb_device_unref(dev_handle->dev);
}
API_EXPORTED void libusb_close(struct libusb_device_handle *dev_handle)
@@ -548,38 +293,29 @@ API_EXPORTED struct libusb_device *libusb_get_device(
API_EXPORTED int libusb_claim_interface(struct libusb_device_handle *dev,
int iface)
{
- int r;
usbi_dbg("interface %d", iface);
-
- r = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &iface);
- if (r < 0)
- usbi_err("claim interface failed, error %d", r);
- return r;
+ return usbi_backend->claim_interface(dev, iface);
}
API_EXPORTED int libusb_release_interface(struct libusb_device_handle *dev,
int iface)
{
- int r;
usbi_dbg("interface %d", iface);
-
- r = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &iface);
- if (r < 0)
- usbi_err("release interface failed, error %d", r);
- return r;
+ return usbi_backend->release_interface(dev, iface);
}
API_EXPORTED int libusb_init(void)
{
usbi_dbg("");
- usbfs_path = find_usbfs_path();
- if (!usbfs_path) {
- usbi_err("could not find usbfs");
- return -ENODEV;
+
+ if (usbi_backend->init) {
+ int r = usbi_backend->init();
+ if (r < 0)
+ return r;
}
list_init(&usb_devs);
- list_init(&open_devs);
+ list_init(&usbi_open_devs);
usbi_io_init();
return 0;
}
@@ -588,37 +324,14 @@ API_EXPORTED void libusb_exit(void)
{
struct libusb_device_handle *devh;
usbi_dbg("");
- if (!list_empty(&open_devs)) {
+ if (!list_empty(&usbi_open_devs)) {
usbi_dbg("naughty app left some devices open!\n");
- list_for_each_entry(devh, &open_devs, list)
+ list_for_each_entry(devh, &usbi_open_devs, list)
do_close(devh);
+ /* FIXME where do the open handles get freed? */
}
-}
-
-API_EXPORTED size_t libusb_get_pollfds(struct libusb_pollfd **pollfds)
-{
- struct libusb_device_handle *devh;
- struct libusb_pollfd *ret;
- size_t cnt = 0;
- size_t i = 0;
-
- /* count number of open devices */
- list_for_each_entry(devh, &open_devs, list)
- cnt++;
-
- /* create array */
- ret = calloc(cnt, sizeof(struct libusb_pollfd));
- if (!ret)
- return -ENOMEM;
-
- /* add fds */
- list_for_each_entry(devh, &open_devs, list) {
- ret[i++].fd = devh->fd;
- ret[i].events = POLLOUT;
- }
-
- *pollfds = ret;
- return cnt;
+ if (usbi_backend->exit)
+ usbi_backend->exit();
}
void usbi_log(enum usbi_log_level level, const char *function,
diff --git a/libusb/io.c b/libusb/io.c
index 118c8b0..36f5ceb 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -20,6 +20,7 @@
#include <config.h>
#include <errno.h>
+#include <poll.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
@@ -39,6 +40,9 @@
* are always placed at the very end. */
static struct list_head flying_transfers;
+/* list of poll fd's */
+static struct list_head pollfds;
+
/* user callbacks for pollfd changes */
static libusb_pollfd_added_cb fd_added_cb = NULL;
static libusb_pollfd_removed_cb fd_removed_cb = NULL;
@@ -46,6 +50,7 @@ static libusb_pollfd_removed_cb fd_removed_cb = NULL;
void usbi_io_init()
{
list_init(&flying_transfers);
+ list_init(&pollfds);
fd_added_cb = NULL;
fd_removed_cb = NULL;
}
@@ -113,43 +118,9 @@ static void add_to_flying_list(struct usbi_transfer *transfer)
static int submit_transfer(struct usbi_transfer *itransfer)
{
- int r;
- 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->endpoint = transfer->endpoint;
- 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
- * submit all the URBs instantly: submit, submit, submit, reap, reap, reap
- * rather than: submit, reap, submit, reap, submit, reap
- * this will improve performance and fix bugs concerning behaviour when
- * the user submits two similar multiple-urb requests */
- usbi_dbg("transferring %d from %d bytes", urb->buffer_length,
- to_be_transferred);
-
- r = ioctl(transfer->dev_handle->fd, IOCTL_USB_SUBMITURB, urb);
- if (r < 0) {
- usbi_err("submiturb failed error %d errno=%d", r, errno);
+ int r = usbi_backend->submit_transfer(itransfer);
+ if (r < 0)
return r;
- }
add_to_flying_list(itransfer);
return 0;
@@ -157,7 +128,7 @@ static int submit_transfer(struct usbi_transfer *itransfer)
API_EXPORTED size_t libusb_get_transfer_alloc_size(void)
{
- return sizeof(struct usbi_transfer);
+ return sizeof(struct usbi_transfer) + usbi_backend->transfer_priv_size;
}
void __init_transfer(struct usbi_transfer *transfer)
@@ -172,7 +143,8 @@ API_EXPORTED void libusb_init_transfer(struct libusb_transfer *transfer)
API_EXPORTED struct libusb_transfer *libusb_alloc_transfer(void)
{
- struct usbi_transfer *transfer = malloc(sizeof(*transfer));
+ struct usbi_transfer *transfer =
+ malloc(sizeof(*transfer) + usbi_backend->transfer_priv_size);
if (!transfer)
return NULL;
@@ -212,7 +184,7 @@ API_EXPORTED int libusb_cancel_transfer(struct libusb_transfer *transfer)
int r;
usbi_dbg("");
- r = ioctl(transfer->dev_handle->fd, IOCTL_USB_DISCARDURB, &itransfer->urb);
+ r = usbi_backend->cancel_transfer(itransfer);
if (r < 0)
usbi_err("cancel transfer failed error %d", r);
return r;
@@ -224,7 +196,7 @@ API_EXPORTED int libusb_cancel_transfer_sync(struct libusb_transfer *transfer)
int r;
usbi_dbg("");
- r = ioctl(transfer->dev_handle->fd, IOCTL_USB_DISCARDURB, &itransfer->urb);
+ r = usbi_backend->cancel_transfer(itransfer);
if (r < 0) {
usbi_err("cancel transfer failed error %d", r);
return r;
@@ -240,7 +212,7 @@ API_EXPORTED int libusb_cancel_transfer_sync(struct libusb_transfer *transfer)
return 0;
}
-static void handle_transfer_completion(struct usbi_transfer *itransfer,
+void usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
enum libusb_transfer_status status)
{
struct libusb_transfer *transfer = &itransfer->pub;
@@ -267,7 +239,7 @@ static void handle_transfer_completion(struct usbi_transfer *itransfer,
libusb_free_transfer(transfer);
}
-static void handle_transfer_cancellation(struct usbi_transfer *transfer)
+void usbi_handle_transfer_cancellation(struct usbi_transfer *transfer)
{
/* if the URB is being cancelled synchronously, raise cancellation
* completion event by unsetting flag, and ensure that user callback does
@@ -276,83 +248,20 @@ static void handle_transfer_cancellation(struct usbi_transfer *transfer)
if (transfer->flags & USBI_TRANSFER_SYNC_CANCELLED) {
transfer->flags &= ~USBI_TRANSFER_SYNC_CANCELLED;
usbi_dbg("detected sync. cancel");
- handle_transfer_completion(transfer, LIBUSB_TRANSFER_SILENT_COMPLETION);
+ usbi_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");
- handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
+ usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
return;
}
/* otherwise its a normal async cancel */
- handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED);
-}
-
-static int reap_for_devh(struct libusb_device_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)
- return r;
- if (r < 0) {
- usbi_err("reap failed error %d errno=%d", r, errno);
- return r;
- }
-
- 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(&itransfer->list);
-
- if (urb->status == -2) {
- handle_transfer_cancellation(itransfer);
- return 0;
- }
-
- /* 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 */
- 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);
-
- 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);
- handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
- return 0;
- }
-
- /* if we've transferred all data, we're done */
- if (itransfer->transferred == length) {
- usbi_dbg("transfer complete --> all done");
- 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(itransfer);
+ usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED);
}
static void handle_timeout(struct usbi_transfer *itransfer)
@@ -416,10 +325,14 @@ static int handle_timeouts(void)
static int poll_io(struct timeval *tv)
{
- struct libusb_device_handle *devh;
int r;
int maxfd = 0;
- fd_set writefds;
+ fd_set readfds, writefds;
+ fd_set *_readfds = NULL;
+ fd_set *_writefds = NULL;
+ struct usbi_pollfd *ipollfd;
+ int have_readfds = 0;
+ int have_writefds = 0;
struct timeval select_timeout;
struct timeval timeout;
@@ -438,19 +351,33 @@ static int poll_io(struct timeval *tv)
select_timeout = *tv;
}
+ FD_ZERO(&readfds);
FD_ZERO(&writefds);
- list_for_each_entry(devh, &open_devs, list) {
- int fd = devh->fd;
- FD_SET(fd, &writefds);
+ list_for_each_entry(ipollfd, &pollfds, list) {
+ struct libusb_pollfd *pollfd = &ipollfd->pollfd;
+ int fd = pollfd->fd;
+ if (pollfd->events & POLLIN) {
+ have_readfds = 1;
+ FD_SET(fd, &readfds);
+ }
+ if (pollfd->events & POLLOUT) {
+ have_writefds = 1;
+ FD_SET(fd, &writefds);
+ }
if (fd > maxfd)
maxfd = fd;
}
+ if (have_readfds)
+ _readfds = &readfds;
+ if (have_writefds)
+ _writefds = &writefds;
+
usbi_dbg("select() with timeout in %d.%06ds", select_timeout.tv_sec,
select_timeout.tv_usec);
- r = select(maxfd + 1, NULL, &writefds, NULL, &select_timeout);
- usbi_dbg("select() returned %d with %d.%06ds remaining", r, select_timeout.tv_sec,
- select_timeout.tv_usec);
+ r = select(maxfd + 1, _readfds, _writefds, NULL, &select_timeout);
+ usbi_dbg("select() returned %d with %d.%06ds remaining",
+ r, select_timeout.tv_sec, select_timeout.tv_usec);
if (r == 0) {
*tv = select_timeout;
return handle_timeouts();
@@ -461,15 +388,9 @@ static int poll_io(struct timeval *tv)
return r;
}
- list_for_each_entry(devh, &open_devs, list) {
- if (!FD_ISSET(devh->fd, &writefds))
- continue;
- r = reap_for_devh(devh);
- if (r == -1 && errno == EAGAIN)
- continue;
- if (r < 0)
- return r;
- }
+ r = usbi_backend->handle_events(_readfds, _writefds);
+ if (r < 0)
+ return r;
/* FIXME check return value? */
return handle_timeouts();
@@ -561,17 +482,63 @@ API_EXPORTED void libusb_set_pollfd_notifiers(libusb_pollfd_added_cb added_cb,
fd_removed_cb = removed_cb;
}
-void usbi_add_pollfd(int fd, short events)
+int usbi_add_pollfd(int fd, short events)
{
+ struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
+ if (!ipollfd)
+ return -ENOMEM;
+
usbi_dbg("add fd %d events %d", fd, events);
+ ipollfd->pollfd.fd = fd;
+ ipollfd->pollfd.events = events;
+ list_add(&ipollfd->list, &pollfds);
+
if (fd_added_cb)
fd_added_cb(fd, events);
+ return 0;
}
void usbi_remove_pollfd(int fd)
{
+ struct usbi_pollfd *ipollfd;
+ int found = 0;
+
usbi_dbg("remove fd %d", fd);
+ list_for_each_entry(ipollfd, &pollfds, list)
+ if (ipollfd->pollfd.fd == fd) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ usbi_err("couldn't find fd %d to remove", fd);
+ return;
+ }
+
+ list_del(&ipollfd->list);
+ free(ipollfd);
if (fd_removed_cb)
fd_removed_cb(fd);
}
+API_EXPORTED struct libusb_pollfd **libusb_get_pollfds(void)
+{
+ struct libusb_pollfd **ret;
+ struct usbi_pollfd *ipollfd;
+ size_t i = 0;
+ size_t cnt = 0;
+
+ list_for_each_entry(ipollfd, &pollfds, list)
+ cnt++;
+
+ ret = calloc(cnt + 1, sizeof(struct libusb_pollfd *));
+ if (!ret)
+ return NULL;
+
+ list_for_each_entry(ipollfd, &pollfds, list)
+ ret[i++] = (struct libusb_pollfd *) ipollfd;
+ ret[cnt] = NULL;
+
+ return ret;
+}
+
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 4f38ddf..414803c 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -351,7 +351,7 @@ struct libusb_pollfd {
int libusb_poll_timeout(struct timeval *tv);
int libusb_poll(void);
int libusb_get_next_timeout(struct timeval *tv);
-size_t libusb_get_pollfds(struct libusb_pollfd **pollfds);
+struct libusb_pollfd **libusb_get_pollfds(void);
typedef void (*libusb_pollfd_added_cb)(int fd, short events);
typedef void (*libusb_pollfd_removed_cb)(int fd);
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 2ef2573..2d56d64 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -25,11 +25,10 @@
#include <endian.h>
#include <stddef.h>
-#include <sys/ioctl.h>
+#include <sys/select.h>
#include <time.h>
#include <libusb.h>
-#include <usbfs.h>
#define DEVICE_DESC_LENGTH 18
@@ -146,15 +145,15 @@ struct libusb_device {
struct list_head list;
int refcnt;
unsigned long session_data;
- char *nodepath;
struct libusb_device_descriptor desc;
struct libusb_config_descriptor *config;
+ unsigned char os_priv[0];
};
struct libusb_device_handle {
struct list_head list;
struct libusb_device *dev;
- int fd;
+ unsigned char os_priv[0];
};
#define USBI_TRANSFER_SYNC_CANCELLED (1<<0)
@@ -164,11 +163,12 @@ struct usbi_transfer {
/* must come first */
struct libusb_transfer pub;
- struct usb_urb urb;
struct list_head list;
struct timeval timeout;
int transferred;
uint8_t flags;
+
+ unsigned char os_priv[0];
};
/* bus structures */
@@ -181,15 +181,84 @@ struct usb_descriptor_header {
/* shared data and functions */
-extern struct list_head open_devs;
+extern struct list_head usbi_open_devs;
void usbi_io_init(void);
-void usbi_add_pollfd(int fd, short events);
-void usbi_remove_pollfd(int fd);
+
+struct libusb_device *usbi_alloc_device(unsigned long session_id);
+struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id);
+
+void usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
+ enum libusb_transfer_status status);
+void usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest);
int usbi_parse_configuration(struct libusb_config_descriptor *config,
unsigned char *buffer);
+/* polling */
+
+struct usbi_pollfd {
+ /* must come first */
+ struct libusb_pollfd pollfd;
+
+ struct list_head list;
+};
+
+int usbi_add_pollfd(int fd, short events);
+void usbi_remove_pollfd(int fd);
+
+/* device discovery */
+
+/* we traverse usbfs without knowing how many devices we are going to find.
+ * so we create this discovered_devs model which is similar to a linked-list
+ * which grows when required. it can be freed once discovery has completed,
+ * eliminating the need for a list node in the libusb_device structure
+ * itself. */
+struct discovered_devs {
+ size_t len;
+ size_t capacity;
+ struct libusb_device *devices[0];
+};
+
+struct discovered_devs *discovered_devs_append(
+ struct discovered_devs *discdevs, struct libusb_device *dev);
+
+/* OS abstraction */
+
+struct usbi_os_backend {
+ const char *name;
+ int (*init)(void);
+ void (*exit)(void);
+
+ int (*get_device_list)(struct discovered_devs *discdevs);
+
+ int (*open)(struct libusb_device_handle *handle);
+ void (*close)(struct libusb_device_handle *handle);
+
+ int (*claim_interface)(struct libusb_device_handle *handle, int iface);
+ int (*release_interface)(struct libusb_device_handle *handle, int iface);
+
+ void (*destroy_device)(struct libusb_device *dev);
+
+ int (*submit_transfer)(struct usbi_transfer *itransfer);
+ int (*cancel_transfer)(struct usbi_transfer *itransfer);
+
+ int (*handle_events)(fd_set *readfds, fd_set *writefds);
+
+ /* number of bytes to reserve for libusb_device.os_priv */
+ size_t device_priv_size;
+
+ /* number of bytes to reserve for libusb_device_handle.os_priv */
+ size_t device_handle_priv_size;
+
+ /* number of bytes to reserve for usbi_transfer.os_priv */
+ size_t transfer_priv_size;
+};
+
+extern const struct usbi_os_backend * const usbi_backend;
+
+extern const struct usbi_os_backend linux_usbfs_backend;
+
#endif
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
new file mode 100644
index 0000000..bfd526b
--- /dev/null
+++ b/libusb/os/linux_usbfs.c
@@ -0,0 +1,584 @@
+/*
+ * Linux usbfs backend for libusb
+ * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libusb.h"
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+static const char *usbfs_path = NULL;
+
+struct linux_device_priv {
+ char *nodepath;
+};
+
+struct linux_device_handle_priv {
+ int fd;
+};
+
+struct linux_transfer_priv {
+ struct usbfs_urb urb;
+};
+
+static struct linux_device_priv *__device_priv(struct libusb_device *dev)
+{
+ return (struct linux_device_priv *) dev->os_priv;
+}
+
+static struct linux_device_handle_priv *__device_handle_priv(
+ struct libusb_device_handle *handle)
+{
+ return (struct linux_device_handle_priv *) handle->os_priv;
+}
+
+static struct linux_transfer_priv *__transfer_priv(
+ struct usbi_transfer *transfer)
+{
+ return (struct linux_transfer_priv *) transfer->os_priv;
+}
+
+#define TRANSFER_PRIV_GET_ITRANSFER(tpriv) \
+ ((struct usbi_transfer *) \
+ container_of((tpriv), struct usbi_transfer, os_priv))
+
+static int check_usb_vfs(const char *dirname)
+{
+ DIR *dir;
+ struct dirent *entry;
+ int found = 0;
+
+ dir = opendir(dirname);
+ if (!dir)
+ return 0;
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.')
+ continue;
+
+ /* We assume if we find any files that it must be the right place */
+ found = 1;
+ break;
+ }
+
+ closedir(dir);
+ return found;
+}
+
+static const char *find_usbfs_path(void)
+{
+ const char *path = "/dev/bus/usb";
+ const char *ret = NULL;
+
+ if (check_usb_vfs(path)) {
+ ret = path;
+ } else {
+ path = "/proc/bus/usb";
+ if (check_usb_vfs(path))
+ ret = path;
+ }
+
+ usbi_dbg("found usbfs at %s", ret);
+ return ret;
+}
+
+static int op_init(void)
+{
+ usbfs_path = find_usbfs_path();
+ if (!usbfs_path) {
+ usbi_err("could not find usbfs");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int initialize_device(struct libusb_device *dev, uint8_t busnum,
+ uint8_t devaddr)
+{
+ struct linux_device_priv *priv = __device_priv(dev);
+ char path[PATH_MAX + 1];
+ unsigned char raw_desc[DEVICE_DESC_LENGTH];
+ int fd = 0;
+ int i;
+ int r;
+ int tmp;
+
+ priv->nodepath = NULL;
+ dev->config = NULL;
+
+ snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, busnum, devaddr);
+ usbi_dbg("%s", path);
+ fd = open(path, O_RDWR);
+ if (!fd) {
+ usbi_dbg("open '%s' failed, ret=%d errno=%d", path, fd, errno);
+ /* FIXME this might not be an error if the file has gone away due
+ * to unplugging */
+ r = -EIO;
+ goto err;
+ }
+
+ /* FIXME: move config parsing into main lib */
+ r = read(fd, raw_desc, DEVICE_DESC_LENGTH);
+ if (r < 0) {
+ usbi_err("read failed ret=%d errno=%d", r, errno);
+ goto err;
+ }
+ /* FIXME: short read handling? */
+
+ usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", &dev->desc);
+
+ /* Now try to fetch the rest of the descriptors */
+ if (dev->desc.bNumConfigurations > USB_MAXCONFIG) {
+ usbi_err("too many configurations");
+ r = -EINVAL;
+ goto err;
+ }
+
+ if (dev->desc.bNumConfigurations < 1) {
+ usbi_dbg("no configurations?");
+ r = -EINVAL;
+ goto err;
+ }
+
+ tmp = dev->desc.bNumConfigurations * sizeof(struct libusb_config_descriptor);
+ dev->config = malloc(tmp);
+ if (!dev->config) {
+ r = -ENOMEM;
+ goto err;
+ }
+
+ memset(dev->config, 0, tmp);
+ for (i = 0; i < dev->desc.bNumConfigurations; i++) {
+ unsigned char buffer[8], *bigbuffer;
+ struct libusb_config_descriptor config;
+
+ /* Get the first 8 bytes to figure out what the total length is */
+ r = read(fd, buffer, sizeof(buffer));
+ if (r < sizeof(buffer)) {
+ usbi_err("short descriptor read (%d/%d)", r, sizeof(buffer));
+ r = -EIO;
+ goto err;
+ }
+
+ usbi_parse_descriptor(buffer, "bbw", &config);
+
+ bigbuffer = malloc(config.wTotalLength);
+ if (!bigbuffer) {
+ r = -ENOMEM;
+ goto err;
+ }
+
+ /* Read the rest of the config descriptor */
+ memcpy(bigbuffer, buffer, sizeof(buffer));
+
+ tmp = config.wTotalLength - 8;
+ r = read(fd, bigbuffer + 8, tmp);
+ if (r < tmp) {
+ usbi_err("short descriptor read (%d/%d)", r, tmp);
+ free(bigbuffer);
+ r = -EIO;
+ goto err;
+ }
+
+ r = usbi_parse_configuration(&dev->config[i], bigbuffer);
+ if (r > 0)
+ usbi_warn("descriptor data still left\n");
+ free(bigbuffer);
+ }
+
+ priv->nodepath = strdup(path);
+ if (!priv->nodepath) {
+ r = -ENOMEM;
+ goto err;
+ }
+
+ close(fd);
+ return 0;
+
+err:
+ if (fd)
+ close(fd);
+ if (dev->config) {
+ free(dev->config);
+ dev->config = NULL;
+ }
+ if (priv->nodepath) {
+ free(priv->nodepath);
+ priv->nodepath = NULL;
+ }
+ return r;
+}
+
+/* open a device file, set up the libusb_device structure for it, and add it to
+ * discdevs. on failure (non-zero return) the pre-existing discdevs should
+ * be destroyed (and devices freed). on success, the new discdevs pointer
+ * should be used it may have been moved. */
+static int scan_device(struct discovered_devs **_discdevs, uint8_t busnum,
+ uint8_t devaddr)
+{
+ struct discovered_devs *discdevs;
+ unsigned long session_id;
+ struct libusb_device *dev;
+ int need_unref = 0;
+ int r = 0;
+
+ /* FIXME: session ID is not guaranteed unique as addresses can wrap and
+ * will be reused. instead we should add a simple sysfs attribute with
+ * a session ID. */
+ session_id = busnum << 8 | devaddr;
+ usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
+ session_id);
+
+ dev = usbi_get_device_by_session_id(session_id);
+ if (dev) {
+ usbi_dbg("using existing device for %d/%d (session %ld)",
+ busnum, devaddr, session_id);
+ } else {
+ usbi_dbg("allocating new device for %d/%d (session %ld)",
+ busnum, devaddr, session_id);
+ dev = usbi_alloc_device(session_id);
+ if (!dev) {
+ r = -ENOMEM;
+ goto out;
+ }
+ need_unref = 1;
+ r = initialize_device(dev, busnum, devaddr);
+ if (r < 0)
+ goto out;
+ }
+
+ discdevs = discovered_devs_append(*_discdevs, dev);
+ if (!discdevs)
+ r = -ENOMEM;
+ else
+ *_discdevs = discdevs;
+
+out:
+ if (need_unref)
+ libusb_device_unref(dev);
+ return r;
+}
+
+/* open a bus directory and adds all discovered devices to discdevs. on
+ * failure (non-zero return) the pre-existing discdevs should be destroyed
+ * (and devices freed). on success, the new discdevs pointer should be used
+ * as it may have been moved. */
+static int scan_busdir(struct discovered_devs **_discdevs, uint8_t busnum)
+{
+ DIR *dir;
+ char dirpath[PATH_MAX + 1];
+ struct dirent *entry;
+ struct discovered_devs *discdevs = *_discdevs;
+ int r = 0;
+
+ snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum);
+ usbi_dbg("%s", dirpath);
+ dir = opendir(dirpath);
+ if (!dir) {
+ usbi_err("opendir '%s' failed, errno=%d", dirpath, errno);
+ /* FIXME: should handle valid race conditions like hub unplugged
+ * during directory iteration - this is not an error */
+ return -1;
+ }
+
+ while ((entry = readdir(dir))) {
+ int devaddr;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ devaddr = atoi(entry->d_name);
+ if (devaddr == 0) {
+ usbi_dbg("unknown dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = scan_device(&discdevs, busnum, (uint8_t) devaddr);
+ if (r < 0)
+ goto out;
+ }
+
+ *_discdevs = discdevs;
+out:
+ closedir(dir);
+ return r;
+}
+
+static int op_get_device_list(struct discovered_devs *discdevs)
+{
+ struct dirent *entry;
+ int r = 0;
+ DIR *buses = opendir(usbfs_path);
+ if (!buses) {
+ usbi_err("opendir buses failed errno=%d", errno);
+ return -1;
+ }
+
+ while ((entry = readdir(buses))) {
+ struct discovered_devs *discdevs_new = discdevs;
+ int busnum;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ busnum = atoi(entry->d_name);
+ if (busnum == 0) {
+ usbi_dbg("unknown dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = scan_busdir(&discdevs_new, busnum);
+ if (r < 0)
+ goto out;
+ discdevs = discdevs_new;
+ }
+
+out:
+ closedir(buses);
+ return r;
+}
+
+static int op_open(struct libusb_device_handle *handle)
+{
+ struct linux_device_priv *dpriv = __device_priv(handle->dev);
+ struct linux_device_handle_priv *hpriv = __device_handle_priv(handle);
+
+ hpriv->fd = open(dpriv->nodepath, O_RDWR);
+ if (hpriv->fd < 0) {
+ usbi_err("open failed, code %d errno %d", hpriv->fd, errno);
+ return -EIO;
+ }
+
+ return usbi_add_pollfd(hpriv->fd, POLLOUT);
+}
+
+static void op_close(struct libusb_device_handle *dev_handle)
+{
+ int fd = __device_handle_priv(dev_handle)->fd;
+ usbi_remove_pollfd(fd);
+ close(fd);
+}
+
+static int op_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ int fd = __device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface);
+ if (r < 0)
+ usbi_err("claim interface failed, error %d", r);
+ return r;
+}
+
+static int op_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ int fd = __device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface);
+ if (r < 0)
+ usbi_err("release interface failed, error %d", r);
+ return r;
+}
+
+static void op_destroy_device(struct libusb_device *dev)
+{
+ unsigned char *nodepath = __device_priv(dev)->nodepath;
+ if (nodepath)
+ free(nodepath);
+}
+
+static int submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = &itransfer->pub;
+ struct usbfs_urb *urb = &__transfer_priv(itransfer)->urb;
+ struct linux_device_handle_priv *dpriv =
+ __device_handle_priv(transfer->dev_handle);
+ int to_be_transferred = transfer->length - itransfer->transferred;
+ int r;
+
+ 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
+ * submit all the URBs instantly: submit, submit, submit, reap, reap, reap
+ * rather than: submit, reap, submit, reap, submit, reap
+ * this will improve performance and fix bugs concerning behaviour when
+ * the user submits two similar multiple-urb requests */
+ usbi_dbg("transferring %d from %d bytes", urb->buffer_length,
+ to_be_transferred);
+
+ r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
+ if (r < 0)
+ usbi_err("submiturb failed error %d errno=%d", r, errno);
+
+ return r;
+}
+
+static int op_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct usbfs_urb *urb = &__transfer_priv(itransfer)->urb;
+ struct libusb_transfer *transfer = &itransfer->pub;
+
+ memset(urb, 0, sizeof(*urb));
+ switch (transfer->endpoint_type) {
+ case LIBUSB_ENDPOINT_TYPE_CONTROL:
+ urb->type = USBFS_URB_TYPE_CONTROL;
+ break;
+ case LIBUSB_ENDPOINT_TYPE_BULK:
+ urb->type = USBFS_URB_TYPE_BULK;
+ break;
+ case LIBUSB_ENDPOINT_TYPE_INTERRUPT:
+ urb->type = USBFS_URB_TYPE_INTERRUPT;
+ break;
+ default:
+ usbi_err("unknown endpoint type %d", transfer->endpoint_type);
+ return -EINVAL;
+ }
+
+ urb->endpoint = transfer->endpoint;
+ return submit_transfer(itransfer);
+}
+
+static int op_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct usbfs_urb *urb = &__transfer_priv(itransfer)->urb;
+ struct libusb_transfer *transfer = &itransfer->pub;
+ struct linux_device_handle_priv *dpriv =
+ __device_handle_priv(transfer->dev_handle);
+
+ return ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urb);
+}
+
+static int reap_for_handle(struct libusb_device_handle *handle)
+{
+ struct linux_device_handle_priv *hpriv = __device_handle_priv(handle);
+ int r;
+ struct usbfs_urb *urb;
+ struct linux_transfer_priv *tpriv;
+ struct usbi_transfer *itransfer;
+ struct libusb_transfer *transfer;
+ int trf_requested;
+ int length;
+
+ r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb);
+ if (r == -1 && errno == EAGAIN)
+ return r;
+ if (r < 0) {
+ usbi_err("reap failed error %d errno=%d", r, errno);
+ return r;
+ }
+
+ tpriv = container_of(urb, struct linux_transfer_priv, urb);
+ itransfer = TRANSFER_PRIV_GET_ITRANSFER(tpriv);
+ transfer = &itransfer->pub;
+
+ usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status,
+ urb->actual_length);
+ list_del(&itransfer->list);
+
+ if (urb->status == -2) {
+ usbi_handle_transfer_cancellation(itransfer);
+ return 0;
+ }
+
+ /* 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 */
+ 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);
+
+ 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);
+ usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
+ return 0;
+ }
+
+ /* if we've transferred all data, we're done */
+ if (itransfer->transferred == length) {
+ usbi_dbg("transfer complete --> all done");
+ usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
+ return 0;
+ }
+
+ /* otherwise, we have more data to transfer */
+ usbi_dbg("more data to transfer...");
+ return submit_transfer(itransfer);
+}
+
+static int op_handle_events(fd_set *readfds, fd_set *writefds)
+{
+ struct libusb_device_handle *handle;
+ int r;
+
+ list_for_each_entry(handle, &usbi_open_devs, list) {
+ struct linux_device_handle_priv *hpriv = __device_handle_priv(handle);
+ if (!FD_ISSET(hpriv->fd, writefds))
+ continue;
+ r = reap_for_handle(handle);
+ if (r == -1 && errno == EAGAIN)
+ continue;
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+const struct usbi_os_backend linux_usbfs_backend = {
+ .name = "Linux usbfs",
+ .init = op_init,
+ .exit = NULL,
+ .get_device_list = op_get_device_list,
+ .open = op_open,
+ .close = op_close,
+ .claim_interface = op_claim_interface,
+ .release_interface = op_release_interface,
+
+ .destroy_device = op_destroy_device,
+
+ .submit_transfer = op_submit_transfer,
+ .cancel_transfer = op_cancel_transfer,
+
+ .handle_events = op_handle_events,
+
+ .device_priv_size = sizeof(struct linux_device_priv),
+ .device_handle_priv_size = sizeof(struct linux_device_handle_priv),
+ .transfer_priv_size = sizeof(struct linux_transfer_priv),
+};
+
diff --git a/libusb/usbfs.h b/libusb/os/linux_usbfs.h
index 9daf0cc..1642959 100644
--- a/libusb/usbfs.h
+++ b/libusb/os/linux_usbfs.h
@@ -21,7 +21,7 @@
#ifndef __LIBUSB_USBFS_H__
#define __LIBUSB_USBFS_H__
-struct usb_ctrltransfer {
+struct usbfs_ctrltransfer {
/* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */
uint8_t bRequestType;
uint8_t bRequest;
@@ -35,7 +35,7 @@ struct usb_ctrltransfer {
void *data;
};
-struct usb_bulktransfer {
+struct usbfs_bulktransfer {
/* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */
unsigned int ep;
unsigned int len;
@@ -45,31 +45,31 @@ struct usb_bulktransfer {
void *data;
};
-struct usb_setinterface {
+struct usbfs_setinterface {
/* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */
unsigned int interface;
unsigned int altsetting;
};
-#define USB_MAXDRIVERNAME 255
+#define USBFS_MAXDRIVERNAME 255
-struct usb_getdriver {
+struct usbfs_getdriver {
unsigned int interface;
- char driver[USB_MAXDRIVERNAME + 1];
+ char driver[USBFS_MAXDRIVERNAME + 1];
};
-#define USB_URB_DISABLE_SPD 1
-#define USB_URB_ISO_ASAP 2
-#define USB_URB_QUEUE_BULK 0x10
+#define USBFS_URB_DISABLE_SPD 1
+#define USBFS_URB_ISO_ASAP 2
+#define USBFS_URB_QUEUE_BULK 0x10
-enum usb_urb_type {
- USB_URB_TYPE_ISO = 0,
- USB_URB_TYPE_INTERRUPT = 1,
- USB_URB_TYPE_CONTROL = 2,
- USB_URB_TYPE_BULK = 3,
+enum usbfs_urb_type {
+ USBFS_URB_TYPE_ISO = 0,
+ USBFS_URB_TYPE_INTERRUPT = 1,
+ USBFS_URB_TYPE_CONTROL = 2,
+ USBFS_URB_TYPE_BULK = 3,
};
-struct usb_iso_packet_desc {
+struct usbfs_iso_packet_desc {
unsigned int length;
unsigned int actual_length;
unsigned int status;
@@ -77,7 +77,7 @@ struct usb_iso_packet_desc {
#define MAX_URB_BUFFER_LENGTH 16384
-struct usb_urb {
+struct usbfs_urb {
unsigned char type;
unsigned char endpoint;
int status;
@@ -90,44 +90,44 @@ struct usb_urb {
int error_count;
unsigned int signr;
void *usercontext;
- struct usb_iso_packet_desc iso_frame_desc[0];
+ struct usbfs_iso_packet_desc iso_frame_desc[0];
};
-struct usb_connectinfo {
+struct usbfs_connectinfo {
unsigned int devnum;
unsigned char slow;
};
-struct usb_ioctl {
+struct usbfs_ioctl {
int ifno; /* interface 0..N ; negative numbers reserved */
int ioctl_code; /* MUST encode size + direction of data so the
* macros in <asm/ioctl.h> give correct values */
void *data; /* param buffer (in, or out) */
};
-struct usb_hub_portinfo {
+struct usbfs_hub_portinfo {
unsigned char numports;
unsigned char port[127]; /* port to device num mapping */
};
-#define IOCTL_USB_CONTROL _IOWR('U', 0, struct usb_ctrltransfer)
-#define IOCTL_USB_BULK _IOWR('U', 2, struct usb_bulktransfer)
-#define IOCTL_USB_RESETEP _IOR('U', 3, unsigned int)
-#define IOCTL_USB_SETINTF _IOR('U', 4, struct usb_setinterface)
-#define IOCTL_USB_SETCONFIG _IOR('U', 5, unsigned int)
-#define IOCTL_USB_GETDRIVER _IOW('U', 8, struct usb_getdriver)
-#define IOCTL_USB_SUBMITURB _IOR('U', 10, struct usb_urb)
-#define IOCTL_USB_DISCARDURB _IO('U', 11)
-#define IOCTL_USB_REAPURB _IOW('U', 12, void *)
-#define IOCTL_USB_REAPURBNDELAY _IOW('U', 13, void *)
-#define IOCTL_USB_CLAIMINTF _IOR('U', 15, unsigned int)
-#define IOCTL_USB_RELEASEINTF _IOR('U', 16, unsigned int)
-#define IOCTL_USB_CONNECTINFO _IOW('U', 17, struct usb_connectinfo)
-#define IOCTL_USB_IOCTL _IOWR('U', 18, struct usb_ioctl)
-#define IOCTL_USB_HUB_PORTINFO _IOR('U', 19, struct usb_hub_portinfo)
-#define IOCTL_USB_RESET _IO('U', 20)
-#define IOCTL_USB_CLEAR_HALT _IOR('U', 21, unsigned int)
-#define IOCTL_USB_DISCONNECT _IO('U', 22)
-#define IOCTL_USB_CONNECT _IO('U', 23)
+#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer)
+#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer)
+#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int)
+#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface)
+#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int)
+#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver)
+#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb)
+#define IOCTL_USBFS_DISCARDURB _IO('U', 11)
+#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *)
+#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *)
+#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int)
+#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int)
+#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo)
+#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl)
+#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo)
+#define IOCTL_USBFS_RESET _IO('U', 20)
+#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int)
+#define IOCTL_USBFS_DISCONNECT _IO('U', 22)
+#define IOCTL_USBFS_CONNECT _IO('U', 23)
#endif