The mass storage function only provides full and high speed
descriptors, so a SuperSpeed-capable UDC that comes up at SuperSpeed
reports a device descriptor with no configurations: count_configs()
skips every configuration without SuperSpeed support. The host then
gives up with "no configurations" and keeps power-cycling the port,
which makes `ums` unusable on such controllers unless their DT caps
maximum-speed at high-speed.

Backport the SuperSpeed descriptors from the Linux mass storage
function, adapted to the U-Boot copy, so that `ums` enumerates and
runs at SuperSpeed. Leave out the BOS and device capability
descriptors that came with it, as nothing references them.

U-Boot has no config_ep_by_speed(), so set the bulk endpoints'
companion descriptor and maxburst by hand before enabling them.
Otherwise the UDC sees a maxburst of 0, which DWC3 turns into an
underflowed burst size instead of the bMaxBurst the host was given.

Based on Linux commit 4bb99b7c82ba ("usb: gadget: storage: add
superspeed support").

Signed-off-by: Alexey Charkov <[email protected]>
---
CI for this patch: 
https://git.u-boot-project.org/u-boot/contributors/alchark/u-boot/-/pipelines/1872
---
 drivers/usb/gadget/f_mass_storage.c | 40 ++++++++++++++++++-
 drivers/usb/gadget/storage_common.c | 77 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 114 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/f_mass_storage.c 
b/drivers/usb/gadget/f_mass_storage.c
index 20c094b0d96f..7cc5998cec7c 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2219,16 +2219,29 @@ reset:
        common->fsg = new_fsg;
        fsg = common->fsg;
 
+       /*
+        * The UDC programs the SuperSpeed burst from the endpoint, so
+        * hand it the companion descriptors before enabling.
+        */
+       if (common->gadget->speed >= USB_SPEED_SUPER) {
+               fsg->bulk_in->comp_desc = &fsg_ss_bulk_in_comp_desc;
+               fsg->bulk_in->maxburst = fsg_ss_bulk_in_comp_desc.bMaxBurst + 1;
+               fsg->bulk_out->comp_desc = &fsg_ss_bulk_out_comp_desc;
+               fsg->bulk_out->maxburst = fsg_ss_bulk_out_comp_desc.bMaxBurst + 
1;
+       }
+
        /* Enable the endpoints */
        d = fsg_ep_desc(common->gadget,
-                       &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
+                       &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc,
+                       &fsg_ss_bulk_in_desc);
        rc = enable_endpoint(common, fsg->bulk_in, d);
        if (rc)
                goto reset;
        fsg->bulk_in_enabled = 1;
 
        d = fsg_ep_desc(common->gadget,
-                       &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
+                       &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc,
+                       &fsg_ss_bulk_out_desc);
        rc = enable_endpoint(common, fsg->bulk_out, d);
        if (rc)
                goto reset;
@@ -2649,6 +2662,7 @@ static void fsg_unbind(struct usb_configuration *c, 
struct usb_function *f)
        fsg_common_release(fsg->common);
        free(fsg->function.descriptors);
        free(fsg->function.hs_descriptors);
+       free(fsg->function.ss_descriptors);
        kfree(fsg);
 }
 
@@ -2704,6 +2718,28 @@ static int fsg_bind(struct usb_configuration *c, struct 
usb_function *f)
                        return -ENOMEM;
                }
        }
+
+       if (gadget_is_superspeed(gadget)) {
+               unsigned int max_burst;
+
+               /* Calculate bMaxBurst, we know packet size is 1024 */
+               max_burst = min_t(unsigned int, FSG_BUFLEN / 1024, 15);
+
+               fsg_ss_bulk_in_desc.bEndpointAddress =
+                       fsg_fs_bulk_in_desc.bEndpointAddress;
+               fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+               fsg_ss_bulk_out_desc.bEndpointAddress =
+                       fsg_fs_bulk_out_desc.bEndpointAddress;
+               fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+
+               f->ss_descriptors = usb_copy_descriptors(fsg_ss_function);
+               if (unlikely(!f->ss_descriptors)) {
+                       free(f->hs_descriptors);
+                       free(f->descriptors);
+                       return -ENOMEM;
+               }
+       }
        return 0;
 
 autoconf_fail:
diff --git a/drivers/usb/gadget/storage_common.c 
b/drivers/usb/gadget/storage_common.c
index 7e4b542f7ce5..a89c7ea3e532 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -531,11 +531,86 @@ static struct usb_descriptor_header *fsg_hs_function[] = {
        NULL,
 };
 
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+       .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /*.bMaxBurst =          DYNAMIC, */
+};
+
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+       .bLength =              sizeof(fsg_ss_bulk_out_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /*.bMaxBurst =          DYNAMIC, */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_ss_intr_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(2),
+       .bInterval =            9,      /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = {
+       .bLength =              sizeof(fsg_ss_intr_in_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .wBytesPerInterval =    cpu_to_le16(2),
+};
+
+#endif
+
+static struct usb_descriptor_header *fsg_ss_function[] = {
+#ifndef FSG_NO_OTG
+       (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+       (struct usb_descriptor_header *) &fsg_intf_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+#ifndef FSG_NO_INTR_EP
+       (struct usb_descriptor_header *) &fsg_ss_intr_in_desc,
+       (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc,
+#endif
+       NULL,
+};
+
 /* Maxpacket and other transfer characteristics vary by speed. */
 static struct usb_endpoint_descriptor *
 fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
-               struct usb_endpoint_descriptor *hs)
+               struct usb_endpoint_descriptor *hs,
+               struct usb_endpoint_descriptor *ss)
 {
+       if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER)
+               return ss;
        if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
                return hs;
        return fs;

---
base-commit: 709a97a05df66e394f40d249105d0baade46f107
change-id: 20261106-ums-ss-2980a315e132

Best regards,
--  
Alexey Charkov <[email protected]>

Reply via email to