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:
		
							parent
							
								
									42a594de14
								
							
						
					
					
						commit
						8745b9ebcc
					
				| 
						 | 
					@ -88,6 +88,8 @@ int usb_add_function(struct usb_configuration *config,
 | 
				
			||||||
		config->fullspeed = 1;
 | 
							config->fullspeed = 1;
 | 
				
			||||||
	if (!config->highspeed && function->hs_descriptors)
 | 
						if (!config->highspeed && function->hs_descriptors)
 | 
				
			||||||
		config->highspeed = 1;
 | 
							config->highspeed = 1;
 | 
				
			||||||
 | 
						if (!config->superspeed && function->ss_descriptors)
 | 
				
			||||||
 | 
							config->superspeed = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
done:
 | 
					done:
 | 
				
			||||||
	if (value)
 | 
						if (value)
 | 
				
			||||||
| 
						 | 
					@ -223,7 +225,9 @@ static int config_buf(struct usb_configuration *config,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* add each function's descriptors */
 | 
						/* add each function's descriptors */
 | 
				
			||||||
	list_for_each_entry(f, &config->functions, list) {
 | 
						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;
 | 
								descriptors = f->hs_descriptors;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			descriptors = f->descriptors;
 | 
								descriptors = f->descriptors;
 | 
				
			||||||
| 
						 | 
					@ -251,7 +255,9 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
 | 
				
			||||||
	struct usb_configuration	*c;
 | 
						struct usb_configuration	*c;
 | 
				
			||||||
	struct list_head		*pos;
 | 
						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)
 | 
							if (gadget->speed == USB_SPEED_HIGH)
 | 
				
			||||||
			hs = 1;
 | 
								hs = 1;
 | 
				
			||||||
		if (type == USB_DT_OTHER_SPEED_CONFIG)
 | 
							if (type == USB_DT_OTHER_SPEED_CONFIG)
 | 
				
			||||||
| 
						 | 
					@ -275,7 +281,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
check_config:
 | 
					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)
 | 
								if (!c->highspeed)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					@ -294,8 +303,12 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
 | 
				
			||||||
	struct usb_gadget		*gadget = cdev->gadget;
 | 
						struct usb_gadget		*gadget = cdev->gadget;
 | 
				
			||||||
	unsigned			count = 0;
 | 
						unsigned			count = 0;
 | 
				
			||||||
	int				hs = 0;
 | 
						int				hs = 0;
 | 
				
			||||||
 | 
						int				ss = 0;
 | 
				
			||||||
	struct usb_configuration	*c;
 | 
						struct usb_configuration	*c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gadget->speed == USB_SPEED_SUPER)
 | 
				
			||||||
 | 
							ss = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gadget_is_dualspeed(gadget)) {
 | 
						if (gadget_is_dualspeed(gadget)) {
 | 
				
			||||||
		if (gadget->speed == USB_SPEED_HIGH)
 | 
							if (gadget->speed == USB_SPEED_HIGH)
 | 
				
			||||||
			hs = 1;
 | 
								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) {
 | 
						list_for_each_entry(c, &cdev->configs, list) {
 | 
				
			||||||
		/* ignore configs that won't work at this speed */
 | 
							/* ignore configs that won't work at this speed */
 | 
				
			||||||
		if (hs) {
 | 
							if (ss) {
 | 
				
			||||||
 | 
								if (!c->superspeed)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
							} else if (hs) {
 | 
				
			||||||
			if (!c->highspeed)
 | 
								if (!c->highspeed)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					@ -388,6 +404,9 @@ static int set_config(struct usb_composite_dev *cdev,
 | 
				
			||||||
		     case USB_SPEED_HIGH:
 | 
							     case USB_SPEED_HIGH:
 | 
				
			||||||
			     speed = "high";
 | 
								     speed = "high";
 | 
				
			||||||
			     break;
 | 
								     break;
 | 
				
			||||||
 | 
							     case USB_SPEED_SUPER:
 | 
				
			||||||
 | 
								     speed = "super";
 | 
				
			||||||
 | 
								     break;
 | 
				
			||||||
		     default:
 | 
							     default:
 | 
				
			||||||
			     speed = "?";
 | 
								     speed = "?";
 | 
				
			||||||
			     break;
 | 
								     break;
 | 
				
			||||||
| 
						 | 
					@ -412,7 +431,9 @@ static int set_config(struct usb_composite_dev *cdev,
 | 
				
			||||||
		 * function's setup callback instead of the current
 | 
							 * function's setup callback instead of the current
 | 
				
			||||||
		 * configuration's setup callback.
 | 
							 * 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;
 | 
								descriptors = f->hs_descriptors;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			descriptors = f->descriptors;
 | 
								descriptors = f->descriptors;
 | 
				
			||||||
| 
						 | 
					@ -492,8 +513,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
 | 
				
			||||||
		list_del(&config->list);
 | 
							list_del(&config->list);
 | 
				
			||||||
		config->cdev = NULL;
 | 
							config->cdev = NULL;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		debug("cfg %d/%p speeds:%s%s\n",
 | 
							debug("cfg %d/%p speeds:%s%s%s\n",
 | 
				
			||||||
			config->bConfigurationValue, config,
 | 
								config->bConfigurationValue, config,
 | 
				
			||||||
 | 
								config->superspeed ? " super" : "",
 | 
				
			||||||
			config->highspeed ? " high" : "",
 | 
								config->highspeed ? " high" : "",
 | 
				
			||||||
			config->fullspeed
 | 
								config->fullspeed
 | 
				
			||||||
				? (gadget_is_dualspeed(cdev->gadget)
 | 
									? (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)
 | 
					static int bos_desc(struct usb_composite_dev *cdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct usb_ext_cap_descriptor   *usb_ext;
 | 
						struct usb_ext_cap_descriptor   *usb_ext;
 | 
				
			||||||
 | 
						struct usb_dcd_config_params	dcd_config_params;
 | 
				
			||||||
	struct usb_bos_descriptor       *bos = cdev->req->buf;
 | 
						struct usb_bos_descriptor       *bos = cdev->req->buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bos->bLength = USB_DT_BOS_SIZE;
 | 
						bos->bLength = USB_DT_BOS_SIZE;
 | 
				
			||||||
| 
						 | 
					@ -794,10 +817,20 @@ static int bos_desc(struct usb_composite_dev *cdev)
 | 
				
			||||||
				    USB_HIGH_SPEED_OPERATION |
 | 
									    USB_HIGH_SPEED_OPERATION |
 | 
				
			||||||
				    USB_5GBPS_OPERATION);
 | 
									    USB_5GBPS_OPERATION);
 | 
				
			||||||
		ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
 | 
							ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
 | 
				
			||||||
		ss_cap->bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
 | 
					
 | 
				
			||||||
		ss_cap->bU2DevExitLat =
 | 
							/* 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);
 | 
									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);
 | 
						return le16_to_cpu(bos->wTotalLength);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -999,32 +1032,28 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 | 
				
			||||||
			cdev->desc.bNumConfigurations =
 | 
								cdev->desc.bNumConfigurations =
 | 
				
			||||||
				count_configs(cdev, USB_DT_DEVICE);
 | 
									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 = 9;
 | 
					 | 
				
			||||||
				cdev->desc.bcdUSB = cpu_to_le16(0x0300);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
			cdev->desc.bMaxPacketSize0 =
 | 
								cdev->desc.bMaxPacketSize0 =
 | 
				
			||||||
				cdev->gadget->ep0->maxpacket;
 | 
									cdev->gadget->ep0->maxpacket;
 | 
				
			||||||
 | 
								if (gadget->speed >= USB_SPEED_SUPER) {
 | 
				
			||||||
 | 
									cdev->desc.bcdUSB = cpu_to_le16(0x0310);
 | 
				
			||||||
 | 
									cdev->desc.bMaxPacketSize0 = 9;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									cdev->desc.bcdUSB = cpu_to_le16(0x0200);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			value = min(w_length, (u16) sizeof cdev->desc);
 | 
								value = min(w_length, (u16) sizeof cdev->desc);
 | 
				
			||||||
			memcpy(req->buf, &cdev->desc, value);
 | 
								memcpy(req->buf, &cdev->desc, value);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case USB_DT_DEVICE_QUALIFIER:
 | 
							case USB_DT_DEVICE_QUALIFIER:
 | 
				
			||||||
			if (!gadget_is_dualspeed(gadget))
 | 
								if (!gadget_is_dualspeed(gadget) ||
 | 
				
			||||||
 | 
								    gadget->speed >= USB_SPEED_SUPER)
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			device_qual(cdev);
 | 
								device_qual(cdev);
 | 
				
			||||||
			value = min_t(int, w_length,
 | 
								value = min_t(int, w_length,
 | 
				
			||||||
				      sizeof(struct usb_qualifier_descriptor));
 | 
									      sizeof(struct usb_qualifier_descriptor));
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case USB_DT_OTHER_SPEED_CONFIG:
 | 
							case USB_DT_OTHER_SPEED_CONFIG:
 | 
				
			||||||
			if (!gadget_is_dualspeed(gadget))
 | 
								if (!gadget_is_dualspeed(gadget) ||
 | 
				
			||||||
 | 
								    gadget->speed >= USB_SPEED_SUPER)
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USB_DT_CONFIG:
 | 
							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);
 | 
									value = min(w_length, (u16) value);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case USB_DT_BOS:
 | 
							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);
 | 
									value = bos_desc(cdev);
 | 
				
			||||||
			if (value >= 0)
 | 
					 | 
				
			||||||
				value = min(w_length, (u16)value);
 | 
									value = min(w_length, (u16)value);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			goto unknown;
 | 
								goto unknown;
 | 
				
			||||||
| 
						 | 
					@ -1421,7 +1456,7 @@ composite_resume(struct usb_gadget *gadget)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct usb_gadget_driver composite_driver = {
 | 
					static struct usb_gadget_driver composite_driver = {
 | 
				
			||||||
	.speed		= USB_SPEED_HIGH,
 | 
						.speed		= USB_SPEED_SUPER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	.bind		= composite_bind,
 | 
						.bind		= composite_bind,
 | 
				
			||||||
	.unbind         = composite_unbind,
 | 
						.unbind         = composite_unbind,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,6 +146,7 @@ struct usb_function {
 | 
				
			||||||
	struct usb_gadget_strings	**strings;
 | 
						struct usb_gadget_strings	**strings;
 | 
				
			||||||
	struct usb_descriptor_header	**descriptors;
 | 
						struct usb_descriptor_header	**descriptors;
 | 
				
			||||||
	struct usb_descriptor_header	**hs_descriptors;
 | 
						struct usb_descriptor_header	**hs_descriptors;
 | 
				
			||||||
 | 
						struct usb_descriptor_header    **ss_descriptors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct usb_configuration	*config;
 | 
						struct usb_configuration	*config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -279,6 +280,7 @@ struct usb_configuration {
 | 
				
			||||||
	u8			next_interface_id;
 | 
						u8			next_interface_id;
 | 
				
			||||||
	unsigned		highspeed:1;
 | 
						unsigned		highspeed:1;
 | 
				
			||||||
	unsigned		fullspeed:1;
 | 
						unsigned		fullspeed:1;
 | 
				
			||||||
 | 
						unsigned		superspeed:1;
 | 
				
			||||||
	struct usb_function	*interface[MAX_CONFIG_INTERFACES];
 | 
						struct usb_function	*interface[MAX_CONFIG_INTERFACES];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -292,6 +294,7 @@ int usb_add_config(struct usb_composite_dev *,
 | 
				
			||||||
 *	identifiers.
 | 
					 *	identifiers.
 | 
				
			||||||
 * @strings: tables of strings, keyed by identifiers assigned during bind()
 | 
					 * @strings: tables of strings, keyed by identifiers assigned during bind()
 | 
				
			||||||
 *	and language IDs provided in control requests
 | 
					 *	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
 | 
					 * @bind: (REQUIRED) Used to allocate resources that are shared across the
 | 
				
			||||||
 *	whole device, such as string IDs, and add its configurations using
 | 
					 *	whole device, such as string IDs, and add its configurations using
 | 
				
			||||||
 *	@usb_add_config().  This may fail by returning a negative errno
 | 
					 *	@usb_add_config().  This may fail by returning a negative errno
 | 
				
			||||||
| 
						 | 
					@ -319,6 +322,7 @@ struct usb_composite_driver {
 | 
				
			||||||
	const char				*name;
 | 
						const char				*name;
 | 
				
			||||||
	const struct usb_device_descriptor	*dev;
 | 
						const struct usb_device_descriptor	*dev;
 | 
				
			||||||
	struct usb_gadget_strings		**strings;
 | 
						struct usb_gadget_strings		**strings;
 | 
				
			||||||
 | 
						enum usb_device_speed			max_speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* REVISIT:  bind() functions can be marked __init, which
 | 
						/* REVISIT:  bind() functions can be marked __init, which
 | 
				
			||||||
	 * makes trouble for section mismatch analysis.  See if
 | 
						 * makes trouble for section mismatch analysis.  See if
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
struct usb_gadget_driver;
 | 
					struct usb_gadget_driver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -464,6 +469,7 @@ struct usb_gadget_ops {
 | 
				
			||||||
	int	(*pullup) (struct usb_gadget *, int is_on);
 | 
						int	(*pullup) (struct usb_gadget *, int is_on);
 | 
				
			||||||
	int	(*ioctl)(struct usb_gadget *,
 | 
						int	(*ioctl)(struct usb_gadget *,
 | 
				
			||||||
				unsigned code, unsigned long param);
 | 
									unsigned code, unsigned long param);
 | 
				
			||||||
 | 
						void	(*get_config_params)(struct usb_dcd_config_params *);
 | 
				
			||||||
	int	(*udc_start)(struct usb_gadget *,
 | 
						int	(*udc_start)(struct usb_gadget *,
 | 
				
			||||||
			     struct usb_gadget_driver *);
 | 
								     struct usb_gadget_driver *);
 | 
				
			||||||
	int	(*udc_stop)(struct usb_gadget *);
 | 
						int	(*udc_stop)(struct usb_gadget *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue