[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cdi-devel] [PATCH 06/10] usb: Create provide_virtual_devices()



* new_hub() has been getting rather long. This pulls out the code which
  enumerates and provides the virtual devices to CDI (either the full
  physical device, or single interfaces, depending on the device and
  configuration), making it shorter.
! Also, that stuff was broken. It expected a certain order of
  descriptors without gaps in between; fix it.
! Of course, that is not the only way in which it was broken:
  cdi_usb_device.interface should *not* be set to the interface
  descriptor index, but to the value of that descriptor's
  b_interface_number field.

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 usb/usb.c | 255 ++++++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 155 insertions(+), 100 deletions(-)

diff --git a/usb/usb.c b/usb/usb.c
index c7a56d3..a8366e0 100644
--- a/usb/usb.c
+++ b/usb/usb.c
@@ -405,8 +405,162 @@ static void debug_configuration(usb_device_t *dev)
 #endif
 
 
+static struct cdi_usb_descriptor *next_desc(struct cdi_usb_descriptor *desc)
+{
+    return (struct cdi_usb_descriptor *)((uintptr_t)desc + desc->b_length);
+}
+
 static void new_hub_device(usb_logical_device_t *ldev);
 
+static void provide_virtual_devices(usb_device_t *dev)
+{
+    usb_logical_device_t *ldev = NULL;
+    int ldev_ep_idx = 0;
+    bool interface_level;
+
+    struct cdi_usb_descriptor *opaque_desc;
+    struct cdi_usb_device_descriptor *devd = &dev->dev_desc;
+    struct cdi_usb_configuration_descriptor *config = dev->config;
+    struct cdi_usb_interface_descriptor *ifc;
+    struct cdi_usb_endpoint_descriptor *ep;
+
+    uintptr_t desc_end = (uintptr_t)config + config->w_total_length;
+
+    interface_level = devd->b_device_class == CDI_USB_DEVCLS_NONE;
+    if (!interface_level) {
+        ldev = calloc(1, sizeof(*ldev));
+        ldev->cdi.bus_data.bus_type = CDI_USB;
+        ldev->cdi.interface         = -1;
+        ldev->cdi.endpoint_count    = 1; // EP0
+        ldev->cdi.vendor_id         = devd->id_vendor;
+        ldev->cdi.product_id        = devd->id_product;
+        ldev->cdi.class_id          = devd->b_device_class;
+        ldev->cdi.subclass_id       = devd->b_device_sub_class;
+        ldev->cdi.protocol_id       = devd->b_device_protocol;
+
+        ldev->dev = dev;
+
+        ldev->endpoints = malloc(1 * sizeof(ldev->endpoints[0]));
+        ldev->endpoints[0] = 0; // EP0
+        ldev_ep_idx = 1;
+    }
+
+
+    opaque_desc = next_desc(&config->base);
+
+    int i = 0;
+    while ((uintptr_t)next_desc(opaque_desc) <= desc_end &&
+           i < (int)config->b_num_interfaces)
+    {
+        if (opaque_desc->b_descriptor_type == CDI_USB_DESC_INTERFACE) {
+            i++;
+            ifc = CDI_UPCAST(opaque_desc,
+                             struct cdi_usb_interface_descriptor, base);
+        } else {
+            ifc = NULL;
+        }
+
+        opaque_desc = next_desc(opaque_desc);
+        if (!ifc) {
+            continue;
+        }
+
+        if (interface_level) {
+            ldev = calloc(1, sizeof(*ldev));
+            ldev->cdi.bus_data.bus_type = CDI_USB;
+            ldev->cdi.interface         = ifc->b_interface_number;
+            // Include EP0
+            ldev->cdi.endpoint_count    = ifc->b_num_endpoints + 1;
+            ldev->cdi.vendor_id         = devd->id_vendor;
+            ldev->cdi.product_id        = devd->id_product;
+            ldev->cdi.class_id          = ifc->b_interface_class;
+            ldev->cdi.subclass_id       = ifc->b_interface_sub_class;
+            ldev->cdi.protocol_id       = ifc->b_interface_protocol;
+
+            ldev->dev = dev;
+
+            ldev->endpoints = malloc(ldev->cdi.endpoint_count
+                                     * sizeof(ldev->endpoints[0]));
+            ldev->endpoints[0] = 0; // EP0
+            ldev_ep_idx = 1;
+        } else {
+            ldev->cdi.endpoint_count += ifc->b_num_endpoints;
+            ldev->endpoints = realloc(ldev->endpoints,
+                                      ldev->cdi.endpoint_count
+                                      * sizeof(ldev->endpoints[0]));
+        }
+
+        int j = 0;
+        while ((uintptr_t)next_desc(opaque_desc) <= desc_end &&
+               j < (int)ifc->b_num_endpoints)
+        {
+            if (opaque_desc->b_descriptor_type == CDI_USB_DESC_ENDPOINT) {
+                j++;
+                ep = CDI_UPCAST(opaque_desc,
+                                struct cdi_usb_endpoint_descriptor, base);
+            } else {
+                ep = NULL;
+            }
+
+            opaque_desc = next_desc(opaque_desc);
+            if (!ep) {
+                continue;
+            }
+
+            bool in = ep->b_endpoint_address & CDI_USB_EPDSC_EPADR_DIR;
+            int id  = CDI_USB_EPDSC_EPADR_ID(ep->b_endpoint_address);
+
+            dev->eps[in][id].ep     = id;
+            dev->eps[in][id].mps
+                = CDI_USB_EPDSC_MPS_MPS(ep->w_max_packet_size);
+            dev->eps[in][id].desc   = ep;
+
+            switch (ep->bm_attributes & CDI_USB_EPDSC_ATTR_XFER_TYPE_MASK) {
+                case CDI_USB_EPDSC_ATTR_CONTROL:
+                    dev->eps[in][id].type = CDI_USB_CONTROL;
+                    break;
+                case CDI_USB_EPDSC_ATTR_ISOCHRONOUS:
+                    dev->eps[in][id].type = CDI_USB_ISOCHRONOUS;
+                    break;
+                case CDI_USB_EPDSC_ATTR_BULK:
+                    dev->eps[in][id].type = CDI_USB_BULK;
+                    break;
+                case CDI_USB_EPDSC_ATTR_INTERRUPT:
+                    dev->eps[in][id].type = CDI_USB_INTERRUPT;
+                    break;
+            }
+
+            ldev->endpoints[ldev_ep_idx++] = ((int)in << 4) | id;
+        }
+
+        // FIXME (should be a graceful failure)
+        assert(j == ifc->b_num_endpoints);
+
+        if (interface_level) {
+            if (ldev->cdi.class_id == CDI_USB_DEVCLS_HUB) {
+                new_hub_device(ldev);
+            } else {
+                cdi_provide_device(&ldev->cdi.bus_data);
+            }
+        }
+
+        ifc = (void *)opaque_desc;
+    }
+
+    if (i < config->b_num_interfaces) {
+        printf("[usb] warning: failed to inquire all device interfaces\n");
+    }
+
+    if (!interface_level) {
+        if (ldev->cdi.class_id == CDI_USB_DEVCLS_HUB) {
+            new_hub_device(ldev);
+        } else {
+            cdi_provide_device(&ldev->cdi.bus_data);
+        }
+    }
+}
+
+
 static void new_hub(usb_hub_t *hub)
 {
     for (int i = 0; i < hub->cdi->ports; i++) {
@@ -458,41 +612,14 @@ static void new_hub(usb_hub_t *hub)
         debug_device(dev);
 #endif
 
-        usb_logical_device_t *ldev = NULL;
-        int ldev_ep_idx = 0;
-        bool interface_level;
-
-        interface_level = dev->dev_desc.b_device_class == CDI_USB_DEVCLS_NONE;
-        if (!interface_level) {
-            ldev = calloc(1, sizeof(*ldev));
-            ldev->cdi.bus_data.bus_type = CDI_USB;
-            ldev->cdi.interface         = -1;
-            ldev->cdi.endpoint_count    = 1; // EP0
-            ldev->cdi.vendor_id         = dev->dev_desc.id_vendor;
-            ldev->cdi.product_id        = dev->dev_desc.id_product;
-            ldev->cdi.class_id          = dev->dev_desc.b_device_class;
-            ldev->cdi.subclass_id       = dev->dev_desc.b_device_sub_class;
-            ldev->cdi.protocol_id       = dev->dev_desc.b_device_protocol;
-
-            ldev->dev = dev;
-
-            ldev->endpoints = malloc(1 * sizeof(ldev->endpoints[0]));
-            ldev->endpoints[0] = 0; // EP0
-            ldev_ep_idx = 1;
-        }
-
         if (get_configurations(dev) < 0) {
             printf("[usb] failed to get device configurations\n");
-            free(ldev->endpoints);
-            free(ldev);
             free(dev);
             hub->drv->port_disable(hub->cdi, i);
             continue;
         }
         if (choose_configuration(dev) < 0) {
             printf("[usb] failed to select device configuration\n");
-            free(ldev->endpoints);
-            free(ldev);
             for (int j = 0; j < (int)dev->dev_desc.b_num_configurations; j++) {
                 free(dev->configs[j]);
             }
@@ -506,79 +633,7 @@ static void new_hub(usb_hub_t *hub)
         debug_configuration(dev);
 #endif
 
-        struct cdi_usb_interface_descriptor *ifc = (void *)(dev->config + 1);
-        for (int j = 0; j < (int)dev->config->b_num_interfaces; j++) {
-            if (interface_level) {
-                ldev = calloc(1, sizeof(*ldev));
-                ldev->cdi.bus_data.bus_type = CDI_USB;
-                ldev->cdi.interface         = j;
-                // Include EP0
-                ldev->cdi.endpoint_count    = ifc->b_num_endpoints + 1;
-                ldev->cdi.vendor_id         = dev->dev_desc.id_vendor;
-                ldev->cdi.product_id        = dev->dev_desc.id_product;
-                ldev->cdi.class_id          = ifc->b_interface_class;
-                ldev->cdi.subclass_id       = ifc->b_interface_sub_class;
-                ldev->cdi.protocol_id       = ifc->b_interface_protocol;
-
-                ldev->dev = dev;
-
-                ldev->endpoints = malloc(ldev->cdi.endpoint_count
-                                         * sizeof(ldev->endpoints[0]));
-                ldev->endpoints[0] = 0; // EP0
-                ldev_ep_idx = 1;
-            } else {
-                ldev->cdi.endpoint_count += ifc->b_num_endpoints;
-                ldev->endpoints = realloc(ldev->endpoints,
-                                          ldev->cdi.endpoint_count
-                                          * sizeof(ldev->endpoints[0]));
-            }
-
-            struct cdi_usb_endpoint_descriptor *ep = (void *)(ifc + 1);
-            for (int k = 0; k < (int)ifc->b_num_endpoints; k++, ep++) {
-                bool in = ep->b_endpoint_address & CDI_USB_EPDSC_EPADR_DIR;
-                int id  = CDI_USB_EPDSC_EPADR_ID(ep->b_endpoint_address);
-
-                dev->eps[in][id].ep     = id;
-                dev->eps[in][id].mps
-                    = CDI_USB_EPDSC_MPS_MPS(ep->w_max_packet_size);
-                dev->eps[in][id].desc   = ep;
-
-                switch (ep->bm_attributes & CDI_USB_EPDSC_ATTR_XFER_TYPE_MASK) {
-                    case CDI_USB_EPDSC_ATTR_CONTROL:
-                        dev->eps[in][id].type = CDI_USB_CONTROL;
-                        break;
-                    case CDI_USB_EPDSC_ATTR_ISOCHRONOUS:
-                        dev->eps[in][id].type = CDI_USB_ISOCHRONOUS;
-                        break;
-                    case CDI_USB_EPDSC_ATTR_BULK:
-                        dev->eps[in][id].type = CDI_USB_BULK;
-                        break;
-                    case CDI_USB_EPDSC_ATTR_INTERRUPT:
-                        dev->eps[in][id].type = CDI_USB_INTERRUPT;
-                        break;
-                }
-
-                ldev->endpoints[ldev_ep_idx++] = ((int)in << 4) | id;
-            }
-
-            if (interface_level) {
-                if (ldev->cdi.class_id == CDI_USB_DEVCLS_HUB) {
-                    new_hub_device(ldev);
-                } else {
-                    cdi_provide_device(&ldev->cdi.bus_data);
-                }
-            }
-
-            ifc = (void *)ep;
-        }
-
-        if (!interface_level) {
-            if (ldev->cdi.class_id == CDI_USB_DEVCLS_HUB) {
-                new_hub_device(ldev);
-            } else {
-                cdi_provide_device(&ldev->cdi.bus_data);
-            }
-        }
+        provide_virtual_devices(dev);
     }
 }
 
-- 
2.6.4