usb: gadget: add super speed support
authorLi Jun <jun.li@nxp.com>
Mon, 25 Jan 2021 13:43:54 +0000 (21:43 +0800)
committerMarek Vasut <marex@denx.de>
Sun, 31 Jan 2021 13:08:56 +0000 (14:08 +0100)
This patch is to add usb gadget super speed support in common
driver, including BOS descriptor and select the super speed
descriptor from function driver.

Reviewed-by: Ye Li <ye.li@nxp.com>
Reviewed-by: Peter Chen <peter.chen@nxp.com>
Tested-by: faqiang.zhu <faqiang.zhu@nxp.com>
Signed-off-by: Li Jun <jun.li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
drivers/usb/gadget/composite.c
include/linux/usb/composite.h
include/linux/usb/gadget.h

index 404da12..1063c57 100644 (file)
@@ -88,6 +88,8 @@ int usb_add_function(struct usb_configuration *config,
                config->fullspeed = 1;
        if (!config->highspeed && function->hs_descriptors)
                config->highspeed = 1;
+       if (!config->superspeed && function->ss_descriptors)
+               config->superspeed = 1;
 
 done:
        if (value)
@@ -223,7 +225,9 @@ static int config_buf(struct usb_configuration *config,
 
        /* add each function's descriptors */
        list_for_each_entry(f, &config->functions, list) {
-               if (speed == USB_SPEED_HIGH)
+               if (speed == USB_SPEED_SUPER)
+                       descriptors = f->ss_descriptors;
+               else if (speed == USB_SPEED_HIGH)
                        descriptors = f->hs_descriptors;
                else
                        descriptors = f->descriptors;
@@ -251,7 +255,9 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
        struct usb_configuration        *c;
        struct list_head                *pos;
 
-       if (gadget_is_dualspeed(gadget)) {
+       if (gadget_is_superspeed(gadget)) {
+               speed = gadget->speed;
+       } else if (gadget_is_dualspeed(gadget)) {
                if (gadget->speed == USB_SPEED_HIGH)
                        hs = 1;
                if (type == USB_DT_OTHER_SPEED_CONFIG)
@@ -275,7 +281,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
                        continue;
 
 check_config:
-               if (speed == USB_SPEED_HIGH) {
+               if (speed == USB_SPEED_SUPER) {
+                       if (!c->superspeed)
+                               continue;
+               } else if (speed == USB_SPEED_HIGH) {
                        if (!c->highspeed)
                                continue;
                } else {
@@ -294,8 +303,12 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
        struct usb_gadget               *gadget = cdev->gadget;
        unsigned                        count = 0;
        int                             hs = 0;
+       int                             ss = 0;
        struct usb_configuration        *c;
 
+       if (gadget->speed == USB_SPEED_SUPER)
+               ss = 1;
+
        if (gadget_is_dualspeed(gadget)) {
                if (gadget->speed == USB_SPEED_HIGH)
                        hs = 1;
@@ -304,7 +317,10 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
        }
        list_for_each_entry(c, &cdev->configs, list) {
                /* ignore configs that won't work at this speed */
-               if (hs) {
+               if (ss) {
+                       if (!c->superspeed)
+                               continue;
+               } else if (hs) {
                        if (!c->highspeed)
                                continue;
                } else {
@@ -388,6 +404,9 @@ static int set_config(struct usb_composite_dev *cdev,
                     case USB_SPEED_HIGH:
                             speed = "high";
                             break;
+                    case USB_SPEED_SUPER:
+                            speed = "super";
+                            break;
                     default:
                             speed = "?";
                             break;
@@ -412,7 +431,9 @@ static int set_config(struct usb_composite_dev *cdev,
                 * function's setup callback instead of the current
                 * configuration's setup callback.
                 */
-               if (gadget->speed == USB_SPEED_HIGH)
+               if (gadget->speed == USB_SPEED_SUPER)
+                       descriptors = f->ss_descriptors;
+               else if (gadget->speed == USB_SPEED_HIGH)
                        descriptors = f->hs_descriptors;
                else
                        descriptors = f->descriptors;
@@ -492,8 +513,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
                list_del(&config->list);
                config->cdev = NULL;
        } else {
-               debug("cfg %d/%p speeds:%s%s\n",
+               debug("cfg %d/%p speeds:%s%s%s\n",
                        config->bConfigurationValue, config,
+                       config->superspeed ? " super" : "",
                        config->highspeed ? " high" : "",
                        config->fullspeed
                                ? (gadget_is_dualspeed(cdev->gadget)
@@ -751,6 +773,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
 static int bos_desc(struct usb_composite_dev *cdev)
 {
        struct usb_ext_cap_descriptor   *usb_ext;
+       struct usb_dcd_config_params    dcd_config_params;
        struct usb_bos_descriptor       *bos = cdev->req->buf;
 
        bos->bLength = USB_DT_BOS_SIZE;
@@ -794,9 +817,19 @@ static int bos_desc(struct usb_composite_dev *cdev)
                                    USB_HIGH_SPEED_OPERATION |
                                    USB_5GBPS_OPERATION);
                ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
-               ss_cap->bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
-               ss_cap->bU2DevExitLat =
-                       cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+
+               /* Get Controller configuration */
+               if (cdev->gadget->ops->get_config_params) {
+                       cdev->gadget->ops->get_config_params(
+                               &dcd_config_params);
+               } else {
+                       dcd_config_params.bU1devExitLat =
+                               USB_DEFAULT_U1_DEV_EXIT_LAT;
+                       dcd_config_params.bU2DevExitLat =
+                               cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+               }
+               ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+               ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
        }
        return le16_to_cpu(bos->wTotalLength);
 }
@@ -999,32 +1032,28 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                        cdev->desc.bNumConfigurations =
                                count_configs(cdev, USB_DT_DEVICE);
 
-                       /*
-                        * If the speed is Super speed, then the supported
-                        * max packet size is 512 and it should be sent as
-                        * exponent of 2. So, 9(2^9=512) should be filled in
-                        * bMaxPacketSize0. Also fill USB version as 3.0
-                        * if speed is Super speed.
-                        */
-                       if (cdev->gadget->speed == USB_SPEED_SUPER) {
+                       cdev->desc.bMaxPacketSize0 =
+                               cdev->gadget->ep0->maxpacket;
+                       if (gadget->speed >= USB_SPEED_SUPER) {
+                               cdev->desc.bcdUSB = cpu_to_le16(0x0310);
                                cdev->desc.bMaxPacketSize0 = 9;
-                               cdev->desc.bcdUSB = cpu_to_le16(0x0300);
                        } else {
-                               cdev->desc.bMaxPacketSize0 =
-                                       cdev->gadget->ep0->maxpacket;
+                               cdev->desc.bcdUSB = cpu_to_le16(0x0200);
                        }
                        value = min(w_length, (u16) sizeof cdev->desc);
                        memcpy(req->buf, &cdev->desc, value);
                        break;
                case USB_DT_DEVICE_QUALIFIER:
-                       if (!gadget_is_dualspeed(gadget))
+                       if (!gadget_is_dualspeed(gadget) ||
+                           gadget->speed >= USB_SPEED_SUPER)
                                break;
                        device_qual(cdev);
                        value = min_t(int, w_length,
                                      sizeof(struct usb_qualifier_descriptor));
                        break;
                case USB_DT_OTHER_SPEED_CONFIG:
-                       if (!gadget_is_dualspeed(gadget))
+                       if (!gadget_is_dualspeed(gadget) ||
+                           gadget->speed >= USB_SPEED_SUPER)
                                break;
 
                case USB_DT_CONFIG:
@@ -1039,10 +1068,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                                value = min(w_length, (u16) value);
                        break;
                case USB_DT_BOS:
-                       if (gadget_is_superspeed(cdev->gadget))
+                       /*
+                        * Super speed connection should support BOS, and
+                        * USB compliance test (USB 2.0 Command Verifier)
+                        * also issues this request, return for now for
+                        * USB 2.0 connection.
+                        */
+                       if (gadget->speed >= USB_SPEED_SUPER) {
                                value = bos_desc(cdev);
-                       if (value >= 0)
                                value = min(w_length, (u16)value);
+                       }
                        break;
                default:
                        goto unknown;
@@ -1421,7 +1456,7 @@ composite_resume(struct usb_gadget *gadget)
 }
 
 static struct usb_gadget_driver composite_driver = {
-       .speed          = USB_SPEED_HIGH,
+       .speed          = USB_SPEED_SUPER,
 
        .bind           = composite_bind,
        .unbind         = composite_unbind,
index d75a0bc..935e5c0 100644 (file)
@@ -146,6 +146,7 @@ struct usb_function {
        struct usb_gadget_strings       **strings;
        struct usb_descriptor_header    **descriptors;
        struct usb_descriptor_header    **hs_descriptors;
+       struct usb_descriptor_header    **ss_descriptors;
 
        struct usb_configuration        *config;
 
@@ -279,6 +280,7 @@ struct usb_configuration {
        u8                      next_interface_id;
        unsigned                highspeed:1;
        unsigned                fullspeed:1;
+       unsigned                superspeed:1;
        struct usb_function     *interface[MAX_CONFIG_INTERFACES];
 };
 
@@ -292,6 +294,7 @@ int usb_add_config(struct usb_composite_dev *,
  *     identifiers.
  * @strings: tables of strings, keyed by identifiers assigned during bind()
  *     and language IDs provided in control requests
+ * @max_speed: Highest speed the driver supports.
  * @bind: (REQUIRED) Used to allocate resources that are shared across the
  *     whole device, such as string IDs, and add its configurations using
  *     @usb_add_config().  This may fail by returning a negative errno
@@ -319,6 +322,7 @@ struct usb_composite_driver {
        const char                              *name;
        const struct usb_device_descriptor      *dev;
        struct usb_gadget_strings               **strings;
+       enum usb_device_speed                   max_speed;
 
        /* REVISIT:  bind() functions can be marked __init, which
         * makes trouble for section mismatch analysis.  See if
index 8d54b91..7e6d329 100644 (file)
@@ -449,6 +449,11 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep)
 
 /*-------------------------------------------------------------------------*/
 
+struct usb_dcd_config_params {
+       __u8  bU1devExitLat;    /* U1 Device exit Latency */
+       __le16 bU2DevExitLat;   /* U2 Device exit Latency */
+};
+
 struct usb_gadget;
 struct usb_gadget_driver;
 
@@ -464,6 +469,7 @@ struct usb_gadget_ops {
        int     (*pullup) (struct usb_gadget *, int is_on);
        int     (*ioctl)(struct usb_gadget *,
                                unsigned code, unsigned long param);
+       void    (*get_config_params)(struct usb_dcd_config_params *);
        int     (*udc_start)(struct usb_gadget *,
                             struct usb_gadget_driver *);
        int     (*udc_stop)(struct usb_gadget *);