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@xxxxxxxxxxxx> --- Hi Felipe, In the spirit of the other gadget patches that have appeared lately, here is one to add SS descriptors to the Ethernet gadget. Hope you find it useful. This version adds changes to ether.c and u_ether.c, and removes a change to f_ncm.c that shouldn't have been included in the previous version. -Paul drivers/usb/gadget/ether.c | 4 ++ drivers/usb/gadget/f_ecm.c | 92 ++++++++++++++++++++++++++++++++++++++-- drivers/usb/gadget/f_eem.c | 54 +++++++++++++++++++++++- drivers/usb/gadget/f_rndis.c | 93 ++++++++++++++++++++++++++++++++++++++-- drivers/usb/gadget/f_subset.c | 62 +++++++++++++++++++++++++++- drivers/usb/gadget/u_ether.c | 12 +++-- 6 files changed, 301 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index fa7c5ab..357233c 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -402,7 +402,11 @@ static struct usb_composite_driver eth_driver = { .dev = &device_desc, .strings = dev_strings, #ifdef CONFIG_USB_GADGET_DUALSPEED +# ifdef CONFIG_USB_GADGET_SUPERSPEED + .max_speed = USB_SPEED_SUPER, +# else .max_speed = USB_SPEED_HIGH, +# endif #else .max_speed = USB_SPEED_FULL, #endif /* CONFIG_USB_GADGET_DUALSPEED */ diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index ddedbc8..b54e2ac 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -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) - return 13 * 512 * 8 * 1000 * 8; + if (gadget_is_dualspeed(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; } /*-------------------------------------------------------------------------*/ @@ -231,6 +233,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, @@ -265,6 +268,71 @@ 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 = 6, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .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 = 6, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 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[] = { @@ -677,6 +745,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = usb_copy_descriptors(ecm_hs_function); if (!f->hs_descriptors) goto fail; + + ss_ecm_in_desc.bEndpointAddress = + hs_ecm_in_desc.bEndpointAddress; + ss_ecm_out_desc.bEndpointAddress = + hs_ecm_out_desc.bEndpointAddress; + ss_ecm_notify_desc.bEndpointAddress = + hs_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 @@ -696,6 +776,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,8 +804,10 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ecm unbind\n"); - if (gadget_is_dualspeed(c->cdev->gadget)) + if (gadget_is_dualspeed(c->cdev->gadget)) { usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->ss_descriptors); + } usb_free_descriptors(f->descriptors); kfree(ecm->notify_req->buf); diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 3e41274..a22a3a8 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -115,6 +115,44 @@ 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 = 6, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 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[] = { @@ -263,6 +301,16 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = usb_copy_descriptors(eem_hs_function); if (!f->hs_descriptors) goto fail; + + eem_ss_in_desc.bEndpointAddress = + eem_hs_in_desc.bEndpointAddress; + eem_ss_out_desc.bEndpointAddress = + eem_hs_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", @@ -273,6 +321,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,8 +342,10 @@ eem_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "eem unbind\n"); - if (gadget_is_dualspeed(c->cdev->gadget)) + if (gadget_is_dualspeed(c->cdev->gadget)) { usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->ss_descriptors); + } usb_free_descriptors(f->descriptors); kfree(eem); } diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index b324efa..8e8b1f9 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -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) - return 13 * 512 * 8 * 1000 * 8; + if (gadget_is_dualspeed(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; } /*-------------------------------------------------------------------------*/ @@ -241,6 +243,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, @@ -275,6 +278,71 @@ 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 = 6, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .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 = 6, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 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,9 +738,20 @@ 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; + + ss_in_desc.bEndpointAddress = + hs_in_desc.bEndpointAddress; + ss_out_desc.bEndpointAddress = + hs_out_desc.bEndpointAddress; + ss_notify_desc.bEndpointAddress = + hs_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; @@ -705,6 +784,8 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + if (gadget_is_dualspeed(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,8 +817,10 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) rndis_deregister(rndis->config); rndis_exit(); - if (gadget_is_dualspeed(c->cdev->gadget)) + if (gadget_is_dualspeed(c->cdev->gadget)) { + usb_free_descriptors(f->ss_descriptors); usb_free_descriptors(f->hs_descriptors); + } usb_free_descriptors(f->descriptors); kfree(rndis->notify_req->buf); diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 93bf676..2bee58f 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -201,6 +201,45 @@ 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 = 6, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 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 *) ðer_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 +329,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 +344,18 @@ 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; + + ss_subset_in_desc.bEndpointAddress = + hs_subset_in_desc.bEndpointAddress; + ss_subset_out_desc.bEndpointAddress = + hs_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 @@ -316,6 +369,11 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) 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,8 +388,10 @@ fail: static void geth_unbind(struct usb_configuration *c, struct usb_function *f) { - if (gadget_is_dualspeed(c->cdev->gadget)) + if (gadget_is_dualspeed(c->cdev->gadget)) { usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->ss_descriptors); + } usb_free_descriptors(f->descriptors); geth_string_defs[1].s = NULL; kfree(func_to_geth(f)); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index b91363e..dfed4c1 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -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; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html