move descriptor parsing into main library
authorDaniel Drake <dsd@gentoo.org>
Mon, 5 May 2008 16:47:49 +0000 (17:47 +0100)
committerDaniel Drake <dsd@gentoo.org>
Mon, 5 May 2008 16:49:52 +0000 (17:49 +0100)
OS modules now provide functionality for fetching device/config
descriptors

libusb/core.c
libusb/libusbi.h
libusb/os/linux_usbfs.c

index 2225106f80653f6130bed6e61cd35b96fe1ed215..0464bd874357a85eb6d8857426a163f1b05c139f 100644 (file)
@@ -232,6 +232,7 @@ static void discovered_devs_free(struct discovered_devs *discdevs)
        free(discdevs);
 }
 
+/* allocate a new device with reference count 1 */
 struct libusb_device *usbi_alloc_device(unsigned long session_id)
 {
        size_t priv_size = usbi_backend->device_priv_size;
@@ -255,6 +256,97 @@ struct libusb_device *usbi_alloc_device(unsigned long session_id)
        return dev;
 }
 
+/* call the OS discovery routines to populate descriptors etc */
+int usbi_discover_device(struct libusb_device *dev)
+{
+       int r;
+       int i;
+       void *user_data;
+       unsigned char raw_desc[DEVICE_DESC_LENGTH];
+       size_t alloc_size;
+
+       dev->config = NULL;
+
+       r = usbi_backend->begin_discovery(dev, &user_data);
+       if (r < 0)
+               return r;
+       
+       r = usbi_backend->get_device_descriptor(dev, raw_desc, user_data);
+       if (r < 0)
+               goto err;
+
+       usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", &dev->desc);
+
+       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;
+       }
+
+       alloc_size = dev->desc.bNumConfigurations
+               * sizeof(struct libusb_config_descriptor);
+       dev->config = malloc(alloc_size);
+       if (!dev->config) {
+               r = LIBUSB_ERROR_NO_MEM;
+               goto err;
+       }
+
+       memset(dev->config, 0, alloc_size);
+       for (i = 0; i < dev->desc.bNumConfigurations; i++) {
+               unsigned char tmp[8];
+               unsigned char *bigbuffer;
+               struct libusb_config_descriptor config;
+
+               r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp),
+                       user_data);
+               if (r < 0)
+                       goto err;
+
+               usbi_parse_descriptor(tmp, "bbw", &config);
+
+               bigbuffer = malloc(config.wTotalLength);
+               if (!bigbuffer) {
+                       r = LIBUSB_ERROR_NO_MEM;
+                       goto err;
+               }
+
+               r = usbi_backend->get_config_descriptor(dev, i, bigbuffer,
+                       config.wTotalLength, user_data);
+               if (r < 0) {
+                       free(bigbuffer);
+                       goto err;
+               }
+
+               r = usbi_parse_configuration(&dev->config[i], bigbuffer);
+               free(bigbuffer);
+               if (r < 0) {
+                       usbi_err("parse_configuration failed with code %d", r);
+                       goto err;
+               } else if (r > 0) {
+                       usbi_warn("descriptor data still left\n");
+               }
+       }
+
+       usbi_backend->end_discovery(dev, user_data);
+       return 0;
+
+err:
+       if (dev->config) {
+               usbi_clear_configurations(dev);
+               free(dev->config);
+               dev->config = NULL;
+       }
+
+       usbi_backend->end_discovery(dev, user_data);
+       return r;
+}
+
 struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id)
 {
        struct libusb_device *dev;
index 2f70053206057b70644fecb3e979c8a22654db12..ca44512838e44741dc1bc203d376f3ff8827c545 100644 (file)
@@ -223,6 +223,7 @@ void usbi_io_init(void);
 
 struct libusb_device *usbi_alloc_device(unsigned long session_id);
 struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id);
+int usbi_discover_device(struct libusb_device *dev);
 
 void usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
        enum libusb_transfer_status status);
@@ -273,6 +274,13 @@ struct usbi_os_backend {
        int (*open)(struct libusb_device_handle *handle);
        void (*close)(struct libusb_device_handle *handle);
 
+       int (*begin_discovery)(struct libusb_device *device, void **user_data);
+       int (*get_device_descriptor)(struct libusb_device *device,
+               unsigned char *buffer, void *user_data);
+       int (*get_config_descriptor)(struct libusb_device *device, int index,
+               unsigned char *buffer, size_t len, void *user_data);
+       void (*end_discovery)(struct libusb_device *device, void *user_data);
+
        int (*set_configuration)(struct libusb_device_handle *handle, int config);
 
        int (*claim_interface)(struct libusb_device_handle *handle, int iface);
index 22d6b6a8fbc1d1f70d23da9ac5b2ae72cbf3fbd3..4e1cae1b2ae85dcd88add7059aa5b10f4e0e6274 100644 (file)
@@ -133,132 +133,125 @@ static int op_init(void)
        return 0;
 }
 
-static int initialize_device(struct libusb_device *dev, uint8_t busnum,
-       uint8_t devaddr)
+struct discovery_data {
+       int fd;
+};
+
+static int op_begin_discovery(struct libusb_device *dev, void **user_data)
 {
        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;
-       dev->bus_number = busnum;
-       dev->device_address = devaddr;
+       struct discovery_data *ddata = malloc(sizeof(*ddata));
+       if (!ddata)
+               return LIBUSB_ERROR_NO_MEM;
 
-       snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, busnum, devaddr);
-       usbi_dbg("%s", path);
-       fd = open(path, O_RDONLY);
-       if (fd < 0) {
-               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;
+       ddata->fd = open(priv->nodepath, O_RDONLY);
+       if (ddata->fd < 0) {
+               usbi_dbg("open '%s' failed, ret=%d errno=%d", priv->nodepath,
+                       ddata->fd, errno);
+               free(ddata);
+               return LIBUSB_ERROR_IO;
        }
 
-       /* FIXME: move config parsing into main lib */
-       r = read(fd, raw_desc, DEVICE_DESC_LENGTH);
+       *user_data = ddata;
+       return 0;
+}
+
+static int op_get_device_descriptor(struct libusb_device *device,
+       unsigned char *buffer, void *user_data)
+{
+       struct discovery_data *ddata = user_data;
+       int r = read(ddata->fd, buffer, DEVICE_DESC_LENGTH);
        if (r < 0) {
                usbi_err("read failed ret=%d errno=%d", r, errno);
-               goto err;
-       }
-       if (r < DEVICE_DESC_LENGTH) {
+               return LIBUSB_ERROR_IO;
+       } else if (r < DEVICE_DESC_LENGTH) {
                usbi_err("short descriptor read %d/%d", r, DEVICE_DESC_LENGTH);
-               r = -EIO;
-               goto err;
-       }
-
-       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;
+               return LIBUSB_ERROR_IO;
        }
 
-       if (dev->desc.bNumConfigurations < 1) {
-               usbi_dbg("no configurations?");
-               r = -EINVAL;
-               goto err;
-       }
+       return 0;
+}
 
-       tmp = dev->desc.bNumConfigurations * sizeof(struct libusb_config_descriptor);
-       dev->config = malloc(tmp);
-       if (!dev->config) {
-               r = -ENOMEM;
-               goto err;
+static int op_get_config_descriptor(struct libusb_device *device,
+       int config_index, unsigned char *buffer, size_t len, void *user_data)
+{
+       struct discovery_data *ddata = user_data;
+       off_t off;
+       ssize_t r;
+       int fd = ddata->fd;
+
+       off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
+       if (off < 0) {
+               usbi_err("seek failed ret=%d errno=%d", off, errno);
+               return LIBUSB_ERROR_IO;
        }
 
-       memset(dev->config, 0, tmp);
-       for (i = 0; i < dev->desc.bNumConfigurations; i++) {
-               unsigned char buffer[8], *bigbuffer;
+       /* might need to skip some configuration descriptors to reach the
+        * requested index */
+       while (config_index > 0) {
+               unsigned char tmp[8];
                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;
+               /* read first 8 bytes of descriptor */
+               r = read(fd, tmp, sizeof(tmp));
+               if (r < 0) {
+                       usbi_err("read failed ret=%d errno=%d", r, errno);
+                       return LIBUSB_ERROR_IO;
+               } else if (r < sizeof(tmp)) {
+                       usbi_err("short descriptor read %d/%d", r, sizeof(tmp));
+                       return LIBUSB_ERROR_IO;
                }
-
+       
                usbi_parse_descriptor(buffer, "bbw", &config);
 
-               bigbuffer = malloc(config.wTotalLength);
-               if (!bigbuffer) {
-                       r = -ENOMEM;
-                       goto err;
+               /* seek forward to end of config */
+               off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
+               if (off < 0) {
+                       usbi_err("seek failed ret=%d errno=%d", off, errno);
+                       return LIBUSB_ERROR_IO;
                }
 
-               /* 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);
-               free(bigbuffer);
-               if (r < 0) {
-                       usbi_err("parse_configuration failed with code %d", r);
-                       goto err;
-               }
-               if (r > 0)
-                       usbi_warn("descriptor data still left\n");
+               config_index--;
        }
 
-       priv->nodepath = strdup(path);
-       if (!priv->nodepath) {
-               r = -ENOMEM;
-               goto err;
+       /* read the actual config */
+       r = read(fd, buffer, len);
+       if (r < 0) {
+               usbi_err("read failed ret=%d errno=%d", r, errno);
+               return LIBUSB_ERROR_IO;
+       } else if (r < len) {
+               usbi_err("short descriptor read %d/%d", r, len);
+               return LIBUSB_ERROR_IO;
        }
 
-       close(fd);
        return 0;
+}
 
-err:
-       if (fd)
-               close(fd);
-       if (dev->config) {
-               usbi_clear_configurations(dev);
-               free(dev->config);
-               dev->config = NULL;
-       }
-       if (priv->nodepath) {
-               free(priv->nodepath);
-               priv->nodepath = NULL;
-       }
-       return r;
+static void op_end_discovery(struct libusb_device *device, void *user_data)
+{
+       struct discovery_data *ddata = user_data;
+       close(ddata->fd);
+       free(ddata);
+}
+
+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];
+
+       priv->nodepath = NULL;
+       dev->bus_number = busnum;
+       dev->device_address = devaddr;
+
+       snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, busnum, devaddr);
+       usbi_dbg("%s", path);
+
+       priv->nodepath = strdup(path);
+       if (!priv->nodepath)
+               return LIBUSB_ERROR_NO_MEM;
+
+       return 0;
 }
 
 /* open a device file, set up the libusb_device structure for it, and add it to
@@ -297,6 +290,9 @@ static int scan_device(struct discovered_devs **_discdevs, uint8_t busnum,
                r = initialize_device(dev, busnum, devaddr);
                if (r < 0)
                        goto out;
+               r = usbi_discover_device(dev);
+               if (r < 0)
+                       goto out;
        }
 
        discdevs = discovered_devs_append(*_discdevs, dev);
@@ -1212,6 +1208,11 @@ const struct usbi_os_backend linux_usbfs_backend = {
        .init = op_init,
        .exit = NULL,
        .get_device_list = op_get_device_list,
+       .begin_discovery = op_begin_discovery,
+       .get_device_descriptor = op_get_device_descriptor,
+       .get_config_descriptor = op_get_config_descriptor,
+       .end_discovery = op_end_discovery,
+
        .open = op_open,
        .close = op_close,
        .set_configuration = op_set_configuration,