usb: gadget: add SS descriptors to Ethernet gadget
authorPaul Zimmerman <Paul.Zimmerman@synopsys.com>
Mon, 27 Jun 2011 21:13:18 +0000 (14:13 -0700)
committerFelipe Balbi <balbi@ti.com>
Fri, 8 Jul 2011 10:55:30 +0000 (13:55 +0300)
Add SuperSpeed descriptors to the Network USB
function drivers.

This has been lightly tested using a Linux host.
I was able to ssh from device to host and host to
device, no obvious problems seen.

Signed-off-by: Paul Zimmerman <paulz@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/ether.c
drivers/usb/gadget/f_ecm.c
drivers/usb/gadget/f_eem.c
drivers/usb/gadget/f_rndis.c
drivers/usb/gadget/f_subset.c
drivers/usb/gadget/u_ether.c

index ac41858..aafc84f 100644 (file)
@@ -401,7 +401,7 @@ static struct usb_composite_driver eth_driver = {
        .name           = "g_ether",
        .dev            = &device_desc,
        .strings        = dev_strings,
-       .max_speed      = USB_SPEED_HIGH,
+       .max_speed      = USB_SPEED_SUPER,
        .unbind         = __exit_p(eth_unbind),
 };
 
index ddedbc8..3691a0c 100644 (file)
@@ -77,10 +77,12 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f)
 /* peak (theoretical) bulk transfer rate in bits-per-second */
 static inline unsigned ecm_bitrate(struct usb_gadget *g)
 {
-       if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+               return 13 * 1024 * 8 * 1000 * 8;
+       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
                return 13 * 512 * 8 * 1000 * 8;
        else
-               return 19 *  64 * 1 * 1000 * 8;
+               return 19 * 64 * 1 * 1000 * 8;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -210,8 +212,10 @@ static struct usb_descriptor_header *ecm_fs_function[] = {
        (struct usb_descriptor_header *) &ecm_header_desc,
        (struct usb_descriptor_header *) &ecm_union_desc,
        (struct usb_descriptor_header *) &ecm_desc,
+
        /* NOTE: status endpoint might need to be removed */
        (struct usb_descriptor_header *) &fs_ecm_notify_desc,
+
        /* data interface, altsettings 0 and 1 */
        (struct usb_descriptor_header *) &ecm_data_nop_intf,
        (struct usb_descriptor_header *) &ecm_data_intf,
@@ -231,6 +235,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
        .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
        .bInterval =            LOG2_STATUS_INTERVAL_MSEC + 4,
 };
+
 static struct usb_endpoint_descriptor hs_ecm_in_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
@@ -255,8 +260,10 @@ static struct usb_descriptor_header *ecm_hs_function[] = {
        (struct usb_descriptor_header *) &ecm_header_desc,
        (struct usb_descriptor_header *) &ecm_union_desc,
        (struct usb_descriptor_header *) &ecm_desc,
+
        /* NOTE: status endpoint might need to be removed */
        (struct usb_descriptor_header *) &hs_ecm_notify_desc,
+
        /* data interface, altsettings 0 and 1 */
        (struct usb_descriptor_header *) &ecm_data_nop_intf,
        (struct usb_descriptor_header *) &ecm_data_intf,
@@ -265,6 +272,76 @@ static struct usb_descriptor_header *ecm_hs_function[] = {
        NULL,
 };
 
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
+       .bInterval =            LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
+       .bLength =              sizeof ss_ecm_intr_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 3 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+       .wBytesPerInterval =    cpu_to_le16(ECM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_ecm_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ecm_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
+       .bLength =              sizeof ss_ecm_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *ecm_ss_function[] = {
+       /* CDC ECM control descriptors */
+       (struct usb_descriptor_header *) &ecm_control_intf,
+       (struct usb_descriptor_header *) &ecm_header_desc,
+       (struct usb_descriptor_header *) &ecm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+
+       /* NOTE: status endpoint might need to be removed */
+       (struct usb_descriptor_header *) &ss_ecm_notify_desc,
+       (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
+
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ecm_data_nop_intf,
+       (struct usb_descriptor_header *) &ecm_data_intf,
+       (struct usb_descriptor_header *) &ss_ecm_in_desc,
+       (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
+       (struct usb_descriptor_header *) &ss_ecm_out_desc,
+       (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
+       NULL,
+};
+
 /* string descriptors: */
 
 static struct usb_string ecm_string_defs[] = {
@@ -679,6 +756,20 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
                        goto fail;
        }
 
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               ss_ecm_in_desc.bEndpointAddress =
+                               fs_ecm_in_desc.bEndpointAddress;
+               ss_ecm_out_desc.bEndpointAddress =
+                               fs_ecm_out_desc.bEndpointAddress;
+               ss_ecm_notify_desc.bEndpointAddress =
+                               fs_ecm_notify_desc.bEndpointAddress;
+
+               /* copy descriptors, and track endpoint copies */
+               f->ss_descriptors = usb_copy_descriptors(ecm_ss_function);
+               if (!f->ss_descriptors)
+                       goto fail;
+       }
+
        /* NOTE:  all that is done without knowing or caring about
         * the network link ... which is unavailable to this code
         * until we're activated via set_alt().
@@ -688,6 +779,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
        ecm->port.close = ecm_close;
 
        DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
                        gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                        ecm->port.in_ep->name, ecm->port.out_ep->name,
                        ecm->notify->name);
@@ -696,6 +788,8 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
 fail:
        if (f->descriptors)
                usb_free_descriptors(f->descriptors);
+       if (f->hs_descriptors)
+               usb_free_descriptors(f->hs_descriptors);
 
        if (ecm->notify_req) {
                kfree(ecm->notify_req->buf);
@@ -722,6 +816,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
 
        DBG(c->cdev, "ecm unbind\n");
 
+       if (gadget_is_superspeed(c->cdev->gadget))
+               usb_free_descriptors(f->ss_descriptors);
        if (gadget_is_dualspeed(c->cdev->gadget))
                usb_free_descriptors(f->hs_descriptors);
        usb_free_descriptors(f->descriptors);
index 3e41274..046c6d0 100644 (file)
@@ -115,6 +115,45 @@ static struct usb_descriptor_header *eem_hs_function[] __initdata = {
        NULL,
 };
 
+/* super speed support: */
+
+static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = {
+       .bLength =              sizeof eem_ss_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *eem_ss_function[] __initdata = {
+       /* CDC EEM control descriptors */
+       (struct usb_descriptor_header *) &eem_intf,
+       (struct usb_descriptor_header *) &eem_ss_in_desc,
+       (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &eem_ss_out_desc,
+       (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
+       NULL,
+};
+
 /* string descriptors: */
 
 static struct usb_string eem_string_defs[] = {
@@ -265,7 +304,20 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
                        goto fail;
        }
 
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               eem_ss_in_desc.bEndpointAddress =
+                               eem_fs_in_desc.bEndpointAddress;
+               eem_ss_out_desc.bEndpointAddress =
+                               eem_fs_out_desc.bEndpointAddress;
+
+               /* copy descriptors, and track endpoint copies */
+               f->ss_descriptors = usb_copy_descriptors(eem_ss_function);
+               if (!f->ss_descriptors)
+                       goto fail;
+       }
+
        DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
                        gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                        eem->port.in_ep->name, eem->port.out_ep->name);
        return 0;
@@ -273,6 +325,8 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
 fail:
        if (f->descriptors)
                usb_free_descriptors(f->descriptors);
+       if (f->hs_descriptors)
+               usb_free_descriptors(f->hs_descriptors);
 
        /* we might as well release our claims on endpoints */
        if (eem->port.out_ep->desc)
@@ -292,6 +346,8 @@ eem_unbind(struct usb_configuration *c, struct usb_function *f)
 
        DBG(c->cdev, "eem unbind\n");
 
+       if (gadget_is_superspeed(c->cdev->gadget))
+               usb_free_descriptors(f->ss_descriptors);
        if (gadget_is_dualspeed(c->cdev->gadget))
                usb_free_descriptors(f->hs_descriptors);
        usb_free_descriptors(f->descriptors);
index b324efa..8f3eae9 100644 (file)
@@ -95,10 +95,12 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f)
 /* peak (theoretical) bulk transfer rate in bits-per-second */
 static unsigned int bitrate(struct usb_gadget *g)
 {
-       if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+               return 13 * 1024 * 8 * 1000 * 8;
+       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
                return 13 * 512 * 8 * 1000 * 8;
        else
-               return 19 *  64 * 1 * 1000 * 8;
+               return 19 * 64 * 1 * 1000 * 8;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -216,6 +218,7 @@ static struct usb_endpoint_descriptor fs_out_desc = {
 
 static struct usb_descriptor_header *eth_fs_function[] = {
        (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
        /* control interface matches ACM, not Ethernet */
        (struct usb_descriptor_header *) &rndis_control_intf,
        (struct usb_descriptor_header *) &header_desc,
@@ -223,6 +226,7 @@ static struct usb_descriptor_header *eth_fs_function[] = {
        (struct usb_descriptor_header *) &rndis_acm_descriptor,
        (struct usb_descriptor_header *) &rndis_union_desc,
        (struct usb_descriptor_header *) &fs_notify_desc,
+
        /* data interface has no altsetting */
        (struct usb_descriptor_header *) &rndis_data_intf,
        (struct usb_descriptor_header *) &fs_in_desc,
@@ -241,6 +245,7 @@ static struct usb_endpoint_descriptor hs_notify_desc = {
        .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
        .bInterval =            LOG2_STATUS_INTERVAL_MSEC + 4,
 };
+
 static struct usb_endpoint_descriptor hs_in_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
@@ -261,6 +266,7 @@ static struct usb_endpoint_descriptor hs_out_desc = {
 
 static struct usb_descriptor_header *eth_hs_function[] = {
        (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
        /* control interface matches ACM, not Ethernet */
        (struct usb_descriptor_header *) &rndis_control_intf,
        (struct usb_descriptor_header *) &header_desc,
@@ -268,6 +274,7 @@ static struct usb_descriptor_header *eth_hs_function[] = {
        (struct usb_descriptor_header *) &rndis_acm_descriptor,
        (struct usb_descriptor_header *) &rndis_union_desc,
        (struct usb_descriptor_header *) &hs_notify_desc,
+
        /* data interface has no altsetting */
        (struct usb_descriptor_header *) &rndis_data_intf,
        (struct usb_descriptor_header *) &hs_in_desc,
@@ -275,6 +282,76 @@ static struct usb_descriptor_header *eth_hs_function[] = {
        NULL,
 };
 
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
+       .bInterval =            LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+       .bLength =              sizeof ss_intr_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 3 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+       .wBytesPerInterval =    cpu_to_le16(STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+       .bLength =              sizeof ss_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *eth_ss_function[] = {
+       (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+       /* control interface matches ACM, not Ethernet */
+       (struct usb_descriptor_header *) &rndis_control_intf,
+       (struct usb_descriptor_header *) &header_desc,
+       (struct usb_descriptor_header *) &call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &rndis_acm_descriptor,
+       (struct usb_descriptor_header *) &rndis_union_desc,
+       (struct usb_descriptor_header *) &ss_notify_desc,
+       (struct usb_descriptor_header *) &ss_intr_comp_desc,
+
+       /* data interface has no altsetting */
+       (struct usb_descriptor_header *) &rndis_data_intf,
+       (struct usb_descriptor_header *) &ss_in_desc,
+       (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &ss_out_desc,
+       (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+       NULL,
+};
+
 /* string descriptors: */
 
 static struct usb_string rndis_string_defs[] = {
@@ -670,11 +747,24 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
 
                /* copy descriptors, and track endpoint copies */
                f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
-
                if (!f->hs_descriptors)
                        goto fail;
        }
 
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               ss_in_desc.bEndpointAddress =
+                               fs_in_desc.bEndpointAddress;
+               ss_out_desc.bEndpointAddress =
+                               fs_out_desc.bEndpointAddress;
+               ss_notify_desc.bEndpointAddress =
+                               fs_notify_desc.bEndpointAddress;
+
+               /* copy descriptors, and track endpoint copies */
+               f->ss_descriptors = usb_copy_descriptors(eth_ss_function);
+               if (!f->ss_descriptors)
+                       goto fail;
+       }
+
        rndis->port.open = rndis_open;
        rndis->port.close = rndis_close;
 
@@ -699,12 +789,15 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
         */
 
        DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
                        gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                        rndis->port.in_ep->name, rndis->port.out_ep->name,
                        rndis->notify->name);
        return 0;
 
 fail:
+       if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors)
+               usb_free_descriptors(f->ss_descriptors);
        if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
                usb_free_descriptors(f->hs_descriptors);
        if (f->descriptors)
@@ -736,6 +829,8 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f)
        rndis_deregister(rndis->config);
        rndis_exit();
 
+       if (gadget_is_superspeed(c->cdev->gadget))
+               usb_free_descriptors(f->ss_descriptors);
        if (gadget_is_dualspeed(c->cdev->gadget))
                usb_free_descriptors(f->hs_descriptors);
        usb_free_descriptors(f->descriptors);
index 93bf676..3dc5375 100644 (file)
@@ -201,6 +201,46 @@ static struct usb_descriptor_header *hs_eth_function[] __initdata = {
        NULL,
 };
 
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_subset_in_desc __initdata = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_subset_out_desc __initdata = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc __initdata = {
+       .bLength =              sizeof ss_subset_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *ss_eth_function[] __initdata = {
+       (struct usb_descriptor_header *) &subset_data_intf,
+       (struct usb_descriptor_header *) &mdlm_header_desc,
+       (struct usb_descriptor_header *) &mdlm_desc,
+       (struct usb_descriptor_header *) &mdlm_detail_desc,
+       (struct usb_descriptor_header *) &ether_desc,
+       (struct usb_descriptor_header *) &ss_subset_in_desc,
+       (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
+       (struct usb_descriptor_header *) &ss_subset_out_desc,
+       (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
+       NULL,
+};
+
 /* string descriptors: */
 
 static struct usb_string geth_string_defs[] = {
@@ -290,6 +330,8 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
 
        /* copy descriptors, and track endpoint copies */
        f->descriptors = usb_copy_descriptors(fs_eth_function);
+       if (!f->descriptors)
+               goto fail;
 
        /* support all relevant hardware speeds... we expect that when
         * hardware is dual speed, all bulk-capable endpoints work at
@@ -303,6 +345,20 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
 
                /* copy descriptors, and track endpoint copies */
                f->hs_descriptors = usb_copy_descriptors(hs_eth_function);
+               if (!f->hs_descriptors)
+                       goto fail;
+       }
+
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               ss_subset_in_desc.bEndpointAddress =
+                               fs_subset_in_desc.bEndpointAddress;
+               ss_subset_out_desc.bEndpointAddress =
+                               fs_subset_out_desc.bEndpointAddress;
+
+               /* copy descriptors, and track endpoint copies */
+               f->ss_descriptors = usb_copy_descriptors(ss_eth_function);
+               if (!f->ss_descriptors)
+                       goto fail;
        }
 
        /* NOTE:  all that is done without knowing or caring about
@@ -311,11 +367,17 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
         */
 
        DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
                        gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                        geth->port.in_ep->name, geth->port.out_ep->name);
        return 0;
 
 fail:
+       if (f->descriptors)
+               usb_free_descriptors(f->descriptors);
+       if (f->hs_descriptors)
+               usb_free_descriptors(f->hs_descriptors);
+
        /* we might as well release our claims on endpoints */
        if (geth->port.out_ep->desc)
                geth->port.out_ep->driver_data = NULL;
@@ -330,6 +392,8 @@ fail:
 static void
 geth_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+       if (gadget_is_superspeed(c->cdev->gadget))
+               usb_free_descriptors(f->ss_descriptors);
        if (gadget_is_dualspeed(c->cdev->gadget))
                usb_free_descriptors(f->hs_descriptors);
        usb_free_descriptors(f->descriptors);
index b91363e..dfed4c1 100644 (file)
@@ -97,16 +97,17 @@ struct eth_dev {
 
 static unsigned qmult = 5;
 module_param(qmult, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(qmult, "queue length multiplier at high speed");
+MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
 
 #else  /* full speed (low speed doesn't do bulk) */
 #define qmult          1
 #endif
 
-/* for dual-speed hardware, use deeper queues at highspeed */
+/* for dual-speed hardware, use deeper queues at high/super speed */
 static inline int qlen(struct usb_gadget *gadget)
 {
-       if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH)
+       if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
+                                           gadget->speed == USB_SPEED_SUPER))
                return qmult * DEFAULT_QLEN;
        else
                return DEFAULT_QLEN;
@@ -598,9 +599,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
 
        req->length = length;
 
-       /* throttle highspeed IRQ rate back slightly */
+       /* throttle high/super speed IRQ rate back slightly */
        if (gadget_is_dualspeed(dev->gadget))
-               req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+               req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
+                                    dev->gadget->speed == USB_SPEED_SUPER)
                        ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
                        : 0;