usb: gadget: add super speed support

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>
This commit is contained in:
Li Jun 2021-01-25 21:43:54 +08:00 committed by Marek Vasut
parent 42a594de14
commit 8745b9ebcc
3 changed files with 70 additions and 25 deletions

View 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,

View 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

View 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 *);