From: Ana Bobillo <[email protected]>

Endpoint zero is the default control endpoint, which every device has
and no interface describes. usb_parse_config() still stores an endpoint
descriptor numbered zero like any other, so it reaches the users of
ep_desc[]: usb_set_maxpacket() overwrites endpoint zero's packet sizes
with it, and on xHCI, xhci_get_ep_index() gives an OUT endpoint zero
the index -1, which xhci_init_ep_contexts_if() then uses for the
device's endpoint contexts and rings.

Skip such a descriptor, and the companion descriptor after it, as Linux
does in usb_parse_endpoint(). Add a test with a device that has one,
which also checks that endpoint zero keeps its packet size.

Signed-off-by: Ana Bobillo <[email protected]>
---
 common/usb.c  |  7 +++++++
 test/dm/usb.c | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)

diff --git a/common/usb.c b/common/usb.c
index 05502da6b30..619132512dd 100644
--- a/common/usb.c
+++ b/common/usb.c
@@ -466,6 +466,13 @@ static int usb_parse_config(struct usb_device *dev,
                                break;
                        }
                        if_desc = &dev->config.if_desc[ifno];
+                       /* endpoint zero is the default control endpoint */
+                       if (usb_endpoint_num((struct usb_endpoint_descriptor *)
+                                            head) == 0) {
+                               printf("Interface %d has a descriptor for 
endpoint zero, skipping\n",
+                                      if_desc->desc.bInterfaceNumber);
+                               break;
+                       }
                        if (if_desc->no_of_ep >= USB_MAXENDPOINTS) {
                                printf("Interface %d has too many endpoints!\n",
                                        if_desc->desc.bInterfaceNumber);
diff --git a/test/dm/usb.c b/test/dm/usb.c
index 145ce79dfec..4975a952d87 100644
--- a/test/dm/usb.c
+++ b/test/dm/usb.c
@@ -574,6 +574,42 @@ static int scan_port1(struct unit_test_state *uts, void 
**desc_list,
        return 0;
 }
 
+/* test that a descriptor for endpoint zero is ignored */
+static int dm_test_usb_config_ep0(struct unit_test_state *uts)
+{
+       static struct usb_interface_descriptor interface0;
+       static struct usb_endpoint_descriptor endpoint0 = {
+               .bLength                = USB_DT_ENDPOINT_SIZE,
+               .bDescriptorType        = USB_DT_ENDPOINT,
+               .bEndpointAddress       = 0,
+               .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+               .wMaxPacketSize         = cpu_to_le16(512),
+       };
+       static void *desc_list[] = {
+               &vendor_device_desc,
+               &vendor_config0,
+               &interface0,
+               &endpoint0,
+               &vendor_endpoint1_in,
+               NULL,
+       };
+       struct usb_device *udev;
+
+       interface0 = vendor_interface0;
+       interface0.bNumEndpoints = 2;
+       ut_assertok(scan_port1(uts, desc_list, &udev));
+       ut_assertnonnull(udev);
+       ut_asserteq(1, udev->config.if_desc[0].no_of_ep);
+       ut_asserteq(1 | USB_DIR_IN,
+                   udev->config.if_desc[0].ep_desc[0].bEndpointAddress);
+       ut_asserteq(64, udev->epmaxpacketin[0]);
+       ut_asserteq(64, udev->epmaxpacketout[0]);
+       ut_assertok(usb_stop());
+
+       return 0;
+}
+DM_TEST(dm_test_usb_config_ep0, UTF_SCAN_PDATA | UTF_SCAN_FDT);
+
 /* test that interface and endpoint counts are limited to those described */
 static int dm_test_usb_config_counts(struct unit_test_state *uts)
 {

Reply via email to