Beginnings of cross-platform abstraction
authorDaniel Drake <dsd@gentoo.org>
Thu, 13 Mar 2008 12:36:56 +0000 (12:36 +0000)
committerDaniel Drake <dsd@gentoo.org>
Thu, 13 Mar 2008 12:46:08 +0000 (12:46 +0000)
This also includes a libusb_get_pollfds API change

TODO
configure.ac
examples/dpfp.c
libusb/Makefile.am
libusb/core.c
libusb/io.c
libusb/libusb.h
libusb/libusbi.h
libusb/os/linux_usbfs.c [new file with mode: 0644]
libusb/os/linux_usbfs.h [moved from libusb/usbfs.h with 57% similarity]

diff --git a/TODO b/TODO
index 8506952..de81ef9 100644 (file)
--- 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
index 58b4aa2..e8ae187 100644 (file)
@@ -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"
index 70944f8..47d9fc6 100644 (file)
@@ -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;
        }
 
index 462bd2f..fb5d033 100644 (file)
@@ -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
index 8d7abcf..68d32ce 100644 (file)
 
 #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,
index 118c8b0..36f5ceb 100644 (file)
@@ -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;
+}
+
index 4f38ddf..414803c 100644 (file)
@@ -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);
index 2ef2573..2d56d64 100644 (file)
 
 #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 (file)
index 0000000..bfd526b
--- /dev/null
@@ -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),
+};
+
similarity index 57%
rename from libusb/usbfs.h
rename to libusb/os/linux_usbfs.h
index 9daf0cc..1642959 100644 (file)
@@ -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