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) {
