diff mbox series

[V2,11/17] usb: gadget: add super speed support

Message ID 20210125134400.780-12-peng.fan@nxp.com
State New
Headers show
Series usb: gadget: update | expand

Commit Message

Peng Fan Jan. 25, 2021, 1:43 p.m. UTC
From: Li Jun <jun.li@nxp.com>


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 | 85 ++++++++++++++++++++++++----------
 include/linux/usb/composite.h  |  4 ++
 include/linux/usb/gadget.h     |  6 +++
 3 files changed, 70 insertions(+), 25 deletions(-)

-- 
2.28.0
diff mbox series

Patch

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 404da12754..1063c571d8 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -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,
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index d75a0bc4c4..935e5c0cbb 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -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
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 8d54b91734..7e6d329e54 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -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 *);