linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 1/3] usb: USB3.0 ch11 definitions
       [not found] <1288625887-30303-1-git-send-email-tlinder@codeaurora.org>
@ 2010-11-01 15:38 ` Tatyana Brokhman
  2010-11-01 15:38   ` [PATCH v4 2/3] usb: dummy_hcd code simplification Tatyana Brokhman
  2010-11-01 15:48   ` [PATCH v4 1/3] usb: USB3.0 ch11 definitions Greg KH
  0 siblings, 2 replies; 6+ messages in thread
From: Tatyana Brokhman @ 2010-11-01 15:38 UTC (permalink / raw)
  To: linux-usb
  Cc: linux-arm-msm, Tatyana Brokhman, Greg Kroah-Hartman, Alan Stern,
	Sarah Sharp, Jacob Pan, linux-kernel

Adding hub SuperSpeed usb definitions as defined by ch10 of the USB3.0
spec.

Signed-off-by: tlinder <tlinder@codeaurora.org>
---
 include/linux/usb/ch11.h |   47 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/hcd.h  |    4 +++
 2 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h
index 119194c..10ec069 100644
--- a/include/linux/usb/ch11.h
+++ b/include/linux/usb/ch11.h
@@ -28,6 +28,13 @@
 #define HUB_STOP_TT		11
 
 /*
+ * Hub class additional requests defined by USB 3.0 spec
+ * See USB 3.0 spec Table 10-6
+ */
+#define HUB_SET_DEPTH		12
+#define HUB_GET_PORT_ERR_COUNT	13
+
+/*
  * Hub Class feature numbers
  * See USB 2.0 spec Table 11-17
  */
@@ -56,6 +63,20 @@
 #define USB_PORT_FEAT_C_PORT_L1         23
 
 /*
+ * Port feature selectors added by USB 3.0 spec.
+ * See USB 3.0 spec Table 10-7
+ */
+#define USB_PORT_FEAT_LINK_STATE		5
+#define USB_PORT_FEAT_U1_TIMEOUT		23
+#define USB_PORT_FEAT_U2_TIMEOUT		24
+#define USB_PORT_FEAT_C_LINK_STATE		25
+#define USB_PORT_FEAT_C_CONFIG_ERR		26
+#define USB_PORT_FEAT_REMOTE_WAKE_MASK		27
+#define USB_PORT_FEAT_BH_PORT_RESET		28
+#define USB_PORT_FEAT_C_BH_PORT_RESET		29
+#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT	30
+
+/*
  * Hub Status and Hub Change results
  * See USB 2.0 spec Table 11-19 and Table 11-20
  */
@@ -84,6 +105,32 @@ struct usb_port_status {
 #define USB_PORT_STAT_SUPER_SPEED	0x8000	/* Linux-internal */
 
 /*
+ * Additions to wPortStatus bit field from USB 3.0
+ * See USB 3.0 spec Table 10-10
+ */
+#define USB_PORT_STAT_LINK_STATE	0x01e0
+#define USB_SS_PORT_STAT_POWER		0x0200
+#define USB_PORT_STAT_SPEED_5GBPS	0x0000
+/* Valid only if port is enabled */
+
+/*
+ * Definitions for PORT_LINK_STATE values
+ * (bits 5-8) in wPortStatus
+ */
+#define USB_SS_PORT_LS_U0		0x0000
+#define USB_SS_PORT_LS_U1		0x0020
+#define USB_SS_PORT_LS_U2		0x0040
+#define USB_SS_PORT_LS_U3		0x0060
+#define USB_SS_PORT_LS_SS_DISABLED	0x0080
+#define USB_SS_PORT_LS_RX_DETECT	0x00a0
+#define USB_SS_PORT_LS_SS_INACTIVE	0x00c0
+#define USB_SS_PORT_LS_POLLING		0x00e0
+#define USB_SS_PORT_LS_RECOVERY		0x0100
+#define USB_SS_PORT_LS_HOT_RESET	0x0120
+#define USB_SS_PORT_LS_COMP_MOD		0x0140
+#define USB_SS_PORT_LS_LOOPBACK		0x0160
+
+/*
  * wPortChange bit field
  * See USB 2.0 spec Table 11-22
  * Bits 0 to 4 shown, bits 5 to 15 are reserved
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 3b571f1..d7738de 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -469,6 +469,10 @@ extern void usb_ep0_reinit(struct usb_device *);
 
 /*-------------------------------------------------------------------------*/
 
+/* class requests from USB 3.0 hub spec, table 10-5 */
+#define SetHubDepth		(0x3000 | HUB_SET_DEPTH)
+#define GetPortErrorCount	(0x8000 | HUB_GET_PORT_ERR_COUNT)
+
 /*
  * Generic bandwidth allocation constants/support
  */
-- 
1.6.3.3

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v4 2/3] usb: dummy_hcd code simplification
  2010-11-01 15:38 ` [PATCH v4 1/3] usb: USB3.0 ch11 definitions Tatyana Brokhman
@ 2010-11-01 15:38   ` Tatyana Brokhman
  2010-11-01 15:38     ` [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd Tatyana Brokhman
  2010-11-01 15:48   ` [PATCH v4 1/3] usb: USB3.0 ch11 definitions Greg KH
  1 sibling, 1 reply; 6+ messages in thread
From: Tatyana Brokhman @ 2010-11-01 15:38 UTC (permalink / raw)
  To: linux-usb
  Cc: linux-arm-msm, Tatyana Brokhman, David Brownell,
	Greg Kroah-Hartman, Alan Stern, linux-kernel

Take handling of the control requests out from dummy_timer to a different
function.

Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
---
 drivers/usb/gadget/dummy_hcd.c |  251 +++++++++++++++++++++------------------
 1 files changed, 135 insertions(+), 116 deletions(-)

diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index dc65462..ef34c4d 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -1195,6 +1195,139 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
 #define Ep_Request	(USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
 #define Ep_InRequest	(Ep_Request | USB_DIR_IN)
 
+
+/**
+ * handle_control_request() - handles all control transfers
+ * @dum: pointer to dummy (the_controller)
+ * @urb: the urb request to handle
+ * @setup: pointer to the setup data for a USB device control
+ *	 request
+ * @status: pointer to request handling status
+ *
+ * Return 0 - if the request was handled
+ *	  1 - if the request wasn't handles
+ *	  error code on error
+ */
+static int handle_control_request(struct dummy *dum, struct urb *urb,
+				  struct usb_ctrlrequest *setup,
+				  int *status)
+{
+	struct dummy_ep		*ep2;
+	int			ret_val = 1;
+	unsigned	w_index;
+	unsigned	w_value;
+
+	w_index = le16_to_cpu(setup->wIndex);
+	w_value = le16_to_cpu(setup->wValue);
+	switch (setup->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (setup->bRequestType != Dev_Request)
+			break;
+		dum->address = w_value;
+		*status = 0;
+		dev_dbg(udc_dev(dum), "set_address = %d\n",
+				w_value);
+		ret_val = 0;
+		break;
+	case USB_REQ_SET_FEATURE:
+		if (setup->bRequestType == Dev_Request) {
+			ret_val = 0;
+			switch (w_value) {
+			case USB_DEVICE_REMOTE_WAKEUP:
+				break;
+			case USB_DEVICE_B_HNP_ENABLE:
+				dum->gadget.b_hnp_enable = 1;
+				break;
+			case USB_DEVICE_A_HNP_SUPPORT:
+				dum->gadget.a_hnp_support = 1;
+				break;
+			case USB_DEVICE_A_ALT_HNP_SUPPORT:
+				dum->gadget.a_alt_hnp_support = 1;
+				break;
+			default:
+				ret_val = -EOPNOTSUPP;
+			}
+			if (ret_val == 0) {
+				dum->devstatus |= (1 << w_value);
+				*status = 0;
+			}
+		} else if (setup->bRequestType == Ep_Request) {
+			/* endpoint halt */
+			ep2 = find_endpoint(dum, w_index);
+			if (!ep2 || ep2->ep.name == ep0name) {
+				ret_val = -EOPNOTSUPP;
+				break;
+			}
+			ep2->halted = 1;
+			ret_val = 0;
+			*status = 0;
+		}
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		if (setup->bRequestType == Dev_Request) {
+			ret_val = 0;
+			switch (w_value) {
+			case USB_DEVICE_REMOTE_WAKEUP:
+				w_value = USB_DEVICE_REMOTE_WAKEUP;
+				break;
+			default:
+				ret_val = -EOPNOTSUPP;
+				break;
+			}
+			if (ret_val == 0) {
+				dum->devstatus &= ~(1 << w_value);
+				*status = 0;
+			}
+		} else if (setup->bRequestType == Ep_Request) {
+			/* endpoint halt */
+			ep2 = find_endpoint(dum, w_index);
+			if (!ep2) {
+				ret_val = -EOPNOTSUPP;
+				break;
+			}
+			if (!ep2->wedged)
+				ep2->halted = 0;
+			ret_val = 0;
+			*status = 0;
+		}
+		break;
+	case USB_REQ_GET_STATUS:
+		if (setup->bRequestType == Dev_InRequest
+				|| setup->bRequestType == Intf_InRequest
+				|| setup->bRequestType == Ep_InRequest) {
+			char *buf;
+			/*
+			 * device: remote wakeup, selfpowered
+			 * interface: nothing
+			 * endpoint: halt
+			 */
+			buf = (char *)urb->transfer_buffer;
+			if (urb->transfer_buffer_length > 0) {
+				if (setup->bRequestType == Ep_InRequest) {
+					ep2 = find_endpoint(dum, w_index);
+					if (!ep2) {
+						ret_val = -EOPNOTSUPP;
+						break;
+					}
+					buf[0] = ep2->halted;
+				} else if (setup->bRequestType ==
+					   Dev_InRequest) {
+					buf[0] = (u8)dum->devstatus;
+				} else
+					buf[0] = 0;
+			}
+			if (urb->transfer_buffer_length > 1)
+				buf[1] = 0;
+			urb->actual_length = min_t(u32, 2,
+				urb->transfer_buffer_length);
+			ret_val = 0;
+			*status = 0;
+		}
+		break;
+	}
+	return ret_val;
+}
+
 /* drive both sides of the transfers; looks like irq handlers to
  * both drivers except the callbacks aren't in_irq().
  */
@@ -1297,14 +1430,8 @@ restart:
 		if (ep == &dum->ep [0] && ep->setup_stage) {
 			struct usb_ctrlrequest		setup;
 			int				value = 1;
-			struct dummy_ep			*ep2;
-			unsigned			w_index;
-			unsigned			w_value;
 
 			setup = *(struct usb_ctrlrequest*) urb->setup_packet;
-			w_index = le16_to_cpu(setup.wIndex);
-			w_value = le16_to_cpu(setup.wValue);
-
 			/* paranoia, in case of stale queued data */
 			list_for_each_entry (req, &ep->queue, queue) {
 				list_del_init (&req->queue);
@@ -1326,117 +1453,9 @@ restart:
 			ep->last_io = jiffies;
 			ep->setup_stage = 0;
 			ep->halted = 0;
-			switch (setup.bRequest) {
-			case USB_REQ_SET_ADDRESS:
-				if (setup.bRequestType != Dev_Request)
-					break;
-				dum->address = w_value;
-				status = 0;
-				dev_dbg (udc_dev(dum), "set_address = %d\n",
-						w_value);
-				value = 0;
-				break;
-			case USB_REQ_SET_FEATURE:
-				if (setup.bRequestType == Dev_Request) {
-					value = 0;
-					switch (w_value) {
-					case USB_DEVICE_REMOTE_WAKEUP:
-						break;
-					case USB_DEVICE_B_HNP_ENABLE:
-						dum->gadget.b_hnp_enable = 1;
-						break;
-					case USB_DEVICE_A_HNP_SUPPORT:
-						dum->gadget.a_hnp_support = 1;
-						break;
-					case USB_DEVICE_A_ALT_HNP_SUPPORT:
-						dum->gadget.a_alt_hnp_support
-							= 1;
-						break;
-					default:
-						value = -EOPNOTSUPP;
-					}
-					if (value == 0) {
-						dum->devstatus |=
-							(1 << w_value);
-						status = 0;
-					}
 
-				} else if (setup.bRequestType == Ep_Request) {
-					// endpoint halt
-					ep2 = find_endpoint (dum, w_index);
-					if (!ep2 || ep2->ep.name == ep0name) {
-						value = -EOPNOTSUPP;
-						break;
-					}
-					ep2->halted = 1;
-					value = 0;
-					status = 0;
-				}
-				break;
-			case USB_REQ_CLEAR_FEATURE:
-				if (setup.bRequestType == Dev_Request) {
-					switch (w_value) {
-					case USB_DEVICE_REMOTE_WAKEUP:
-						dum->devstatus &= ~(1 <<
-							USB_DEVICE_REMOTE_WAKEUP);
-						value = 0;
-						status = 0;
-						break;
-					default:
-						value = -EOPNOTSUPP;
-						break;
-					}
-				} else if (setup.bRequestType == Ep_Request) {
-					// endpoint halt
-					ep2 = find_endpoint (dum, w_index);
-					if (!ep2) {
-						value = -EOPNOTSUPP;
-						break;
-					}
-					if (!ep2->wedged)
-						ep2->halted = 0;
-					value = 0;
-					status = 0;
-				}
-				break;
-			case USB_REQ_GET_STATUS:
-				if (setup.bRequestType == Dev_InRequest
-						|| setup.bRequestType
-							== Intf_InRequest
-						|| setup.bRequestType
-							== Ep_InRequest
-						) {
-					char *buf;
-
-					// device: remote wakeup, selfpowered
-					// interface: nothing
-					// endpoint: halt
-					buf = (char *)urb->transfer_buffer;
-					if (urb->transfer_buffer_length > 0) {
-						if (setup.bRequestType ==
-								Ep_InRequest) {
-	ep2 = find_endpoint (dum, w_index);
-	if (!ep2) {
-		value = -EOPNOTSUPP;
-		break;
-	}
-	buf [0] = ep2->halted;
-						} else if (setup.bRequestType ==
-								Dev_InRequest) {
-							buf [0] = (u8)
-								dum->devstatus;
-						} else
-							buf [0] = 0;
-					}
-					if (urb->transfer_buffer_length > 1)
-						buf [1] = 0;
-					urb->actual_length = min_t(u32, 2,
-						urb->transfer_buffer_length);
-					value = 0;
-					status = 0;
-				}
-				break;
-			}
+			value = handle_control_request(dum, urb, &setup,
+						       &status);
 
 			/* gadget driver handles all other requests.  block
 			 * until setup() returns; no reentrancy issues etc.
-- 
1.6.3.3

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd
  2010-11-01 15:38   ` [PATCH v4 2/3] usb: dummy_hcd code simplification Tatyana Brokhman
@ 2010-11-01 15:38     ` Tatyana Brokhman
  2010-11-15 10:52       ` Meena Jain
  0 siblings, 1 reply; 6+ messages in thread
From: Tatyana Brokhman @ 2010-11-01 15:38 UTC (permalink / raw)
  To: linux-usb
  Cc: linux-arm-msm, Tatyana Brokhman, David Brownell,
	Greg Kroah-Hartman, Alan Stern, linux-kernel

USB 3.0 hub includes 2 hubs - HS and SS ones.
Thus, when dummy_hcd enabled it will register 2 root hubs (SS and HS).

Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
---
 drivers/usb/gadget/dummy_hcd.c |  501 ++++++++++++++++++++++++++++++++++++++--
 1 files changed, 487 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index ef34c4d..71e1eaf 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 2003 David Brownell
  * Copyright (C) 2003-2005 Alan Stern
+ * Copyright (C) 2010 Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -61,10 +62,13 @@
 
 #define POWER_BUDGET	500	/* in mA; use 8 for low-power port testing */
 
-static const char	driver_name [] = "dummy_hcd";
-static const char	driver_desc [] = "USB Host+Gadget Emulator";
+static const char	driver_name[] = "dummy_hcd";
+static const char	ss_driver_name[] = "ss_dummy_hcd";
+static const char	driver_desc[] = "USB Host+Gadget Emulator";
+static const char	ss_driver_desc[] = "SS USB Host+Gadget Emulator";
 
-static const char	gadget_name [] = "dummy_udc";
+static const char	gadget_name[] = "dummy_udc";
+static const char	ss_gadget_name[] = "ss_dummy_udc";
 
 MODULE_DESCRIPTION (DRIVER_DESC);
 MODULE_AUTHOR ("David Brownell");
@@ -220,6 +224,7 @@ static inline struct dummy *gadget_dev_to_dummy (struct device *dev)
 }
 
 static struct dummy			*the_controller;
+static struct dummy			*the_ss_controller;
 
 /*-------------------------------------------------------------------------*/
 
@@ -259,10 +264,97 @@ stop_activity (struct dummy *dum)
 	/* driver now does any non-usb quiescing necessary */
 }
 
-/* caller must hold lock */
+/**
+ * set_ss_link_state() - Sets the current state of the
+ * SuperSpeed link
+ * @dum: pointer to the dummy_hcd structure to update the link
+ *     state for
+ *
+ * This function updates the port_status according to the link
+ * state. The old status is saved befor updating.
+ * Note: this function should be called only for SuperSpeed
+ * master and the caller must hold the lock.
+ */
+static void
+set_ss_link_state(struct dummy *dum)
+{
+	dum->active = 0;
+	if ((dum->port_status & USB_SS_PORT_STAT_POWER) == 0)
+		dum->port_status = 0;
+
+	/* UDC suspend must cause a disconnect */
+	else if (!dum->pullup || dum->udc_suspended) {
+		dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
+					USB_PORT_STAT_ENABLE);
+		if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
+			dum->port_status |=
+					(USB_PORT_STAT_C_CONNECTION << 16);
+	} else {
+		/* device is connected and not suspended */
+		dum->port_status |= (USB_PORT_STAT_CONNECTION |
+				     USB_PORT_STAT_SUPER_SPEED);
+		if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
+			dum->port_status |=
+				(USB_PORT_STAT_C_CONNECTION << 16);
+		if ((dum->port_status & USB_PORT_STAT_ENABLE) == 1 &&
+		    (dum->port_status & USB_SS_PORT_LS_U0) == 1 &&
+		    dum->rh_state != DUMMY_RH_SUSPENDED)
+			dum->active = 1;
+	}
+
+
+	if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 ||
+	    dum->active)
+		dum->resuming = 0;
+
+	/* if !connected or reset */
+	if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+			(dum->port_status & USB_PORT_STAT_RESET) != 0) {
+		/*
+		 * We're connected and not reseted (reset occured now),
+		 * and driver attached - disconnect!
+		 */
+		if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
+		    (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
+		    dum->driver) {
+			stop_activity(dum);
+			spin_unlock(&dum->lock);
+			dum->driver->disconnect(&dum->gadget);
+			spin_lock(&dum->lock);
+		}
+	} else if (dum->active != dum->old_active) {
+		if (dum->old_active && dum->driver->suspend) {
+			spin_unlock(&dum->lock);
+			dum->driver->suspend(&dum->gadget);
+			spin_lock(&dum->lock);
+		} else if (!dum->old_active &&
+			   dum->driver->resume) {
+			spin_unlock(&dum->lock);
+			dum->driver->resume(&dum->gadget);
+			spin_lock(&dum->lock);
+		}
+	}
+
+	dum->old_status = dum->port_status;
+	dum->old_active = dum->active;
+}
+
+/**
+ * set_link_state() - Sets the current state of the link
+ * @dum: pointer to the dummy_hcd structure to update the link
+ *     state for
+ *
+ * This function updates the port_status according to the link
+ * state. The old status is saved befor updating.
+ * Note: caller must hold the lock.
+ */
 static void
 set_link_state (struct dummy *dum)
 {
+	if (dum == the_ss_controller) {
+		set_ss_link_state(dum);
+		return;
+	}
 	dum->active = 0;
 	if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
 		dum->port_status = 0;
@@ -343,7 +435,13 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	dum = ep_to_dummy (ep);
 	if (!dum->driver || !is_enabled (dum))
 		return -ESHUTDOWN;
-	max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
+	max = le16_to_cpu(desc->wMaxPacketSize) ;
+	/*
+	 * For HS/FS devices only bits 0..9 of the wMaxPacketSize represent the
+	 * maximum packet size
+	 */
+	if (dum->gadget.speed < USB_SPEED_SUPER)
+		max &= 0x3ff;
 
 	/* drivers must not request bad settings, since lower levels
 	 * (hardware or its drivers) may not check.  some endpoints
@@ -361,6 +459,10 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 			goto done;
 		}
 		switch (dum->gadget.speed) {
+		case USB_SPEED_SUPER:
+			if (max == 1024)
+				break;
+			goto done;
 		case USB_SPEED_HIGH:
 			if (max == 512)
 				break;
@@ -379,6 +481,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 			goto done;
 		/* real hardware might not handle all packet sizes */
 		switch (dum->gadget.speed) {
+		case USB_SPEED_SUPER:
 		case USB_SPEED_HIGH:
 			if (max <= 1024)
 				break;
@@ -399,6 +502,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 			goto done;
 		/* real hardware might not handle all packet sizes */
 		switch (dum->gadget.speed) {
+		case USB_SPEED_SUPER:
 		case USB_SPEED_HIGH:
 			if (max <= 1024)
 				break;
@@ -750,16 +854,22 @@ static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
 int
 usb_gadget_register_driver (struct usb_gadget_driver *driver)
 {
-	struct dummy	*dum = the_controller;
+	struct dummy	*dum;
 	int		retval, i;
 
+	if (!driver->bind || !driver->setup
+			|| driver->speed == USB_SPEED_UNKNOWN)
+		return -EINVAL;
+
+	if (driver->speed == USB_SPEED_SUPER)
+		dum = the_ss_controller;
+	else
+		dum = the_controller;
+
 	if (!dum)
 		return -EINVAL;
 	if (dum->driver)
 		return -EBUSY;
-	if (!driver->bind || !driver->setup
-			|| driver->speed == USB_SPEED_UNKNOWN)
-		return -EINVAL;
 
 	/*
 	 * SLAVE side init ... the layer above hardware, which
@@ -787,7 +897,6 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
 	}
 
 	dum->gadget.ep0 = &dum->ep [0].ep;
-	dum->ep [0].ep.maxpacket = 64;
 	list_del_init (&dum->ep [0].ep.ep_list);
 	INIT_LIST_HEAD(&dum->fifo_req.queue);
 
@@ -806,6 +915,11 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
 	/* khubd will enumerate this in a while */
 	spin_lock_irq (&dum->lock);
 	dum->pullup = 1;
+	dum->gadget.speed = driver->speed;;
+	if (driver->speed == USB_SPEED_SUPER)
+		dum->ep[0].ep.maxpacket = 9;
+	else
+		dum->ep[0].ep.maxpacket = 64;
 	set_link_state (dum);
 	spin_unlock_irq (&dum->lock);
 
@@ -817,12 +931,20 @@ EXPORT_SYMBOL (usb_gadget_register_driver);
 int
 usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
 {
-	struct dummy	*dum = the_controller;
+	struct dummy	*dum ;
 	unsigned long	flags;
 
+	if (!driver || !driver->unbind)
+		return -EINVAL;
+
+	if (driver->speed == USB_SPEED_SUPER)
+		dum = the_ss_controller;
+	else
+		dum = the_controller;
+
 	if (!dum)
 		return -ENODEV;
-	if (!driver || driver != dum->driver || !driver->unbind)
+	if (driver != dum->driver)
 		return -EINVAL;
 
 	dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
@@ -897,6 +1019,34 @@ static int dummy_udc_probe (struct platform_device *pdev)
 	return rc;
 }
 
+static int dummy_ss_udc_probe(struct platform_device *pdev)
+{
+	struct dummy	*dum = the_ss_controller;
+	int		rc;
+
+	dum->gadget.name = gadget_name;
+	dum->gadget.ops = &dummy_ops;
+	dum->gadget.is_dualspeed = 1;
+
+	/* maybe claim OTG support, though we won't complete HNP */
+	dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
+
+	dev_set_name(&dum->gadget.dev, "ss_gadget");
+	dum->gadget.dev.parent = &pdev->dev;
+	dum->gadget.dev.release = dummy_gadget_release;
+	rc = device_register(&dum->gadget.dev);
+	if (rc < 0)
+		return rc;
+
+	usb_get_hcd(dummy_to_hcd(dum));
+
+	platform_set_drvdata(pdev, dum);
+	rc = device_create_file(&dum->gadget.dev, &dev_attr_function);
+	if (rc < 0)
+		device_unregister(&dum->gadget.dev);
+	return rc;
+}
+
 static int dummy_udc_remove (struct platform_device *pdev)
 {
 	struct dummy	*dum = platform_get_drvdata (pdev);
@@ -946,6 +1096,17 @@ static struct platform_driver dummy_udc_driver = {
 	},
 };
 
+static struct platform_driver dummy_ss_udc_driver = {
+	.probe		= dummy_ss_udc_probe,
+	.remove		= dummy_udc_remove,
+	.suspend	= dummy_udc_suspend,
+	.resume		= dummy_udc_resume,
+	.driver		= {
+		.name	= (char *) ss_gadget_name,
+		.owner	= THIS_MODULE,
+	},
+};
+
 /*-------------------------------------------------------------------------*/
 
 /* MASTER/HOST SIDE DRIVER
@@ -1244,6 +1405,24 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
 			case USB_DEVICE_A_ALT_HNP_SUPPORT:
 				dum->gadget.a_alt_hnp_support = 1;
 				break;
+			case USB_DEVICE_U1_ENABLE:
+				if (dum->gadget.speed == USB_SPEED_SUPER)
+					w_value = USB_DEV_STAT_U1_ENABLED;
+				else
+					ret_val = -EOPNOTSUPP;
+				break;
+			case USB_DEVICE_U2_ENABLE:
+				if (dum->gadget.speed == USB_SPEED_SUPER)
+					w_value = USB_DEV_STAT_U2_ENABLED;
+				else
+					ret_val = -EOPNOTSUPP;
+				break;
+			case USB_DEVICE_LTM_ENABLE:
+				if (dum->gadget.speed == USB_SPEED_SUPER)
+					w_value = USB_DEV_STAT_LTM_ENABLED;
+				else
+					ret_val = -EOPNOTSUPP;
+				break;
 			default:
 				ret_val = -EOPNOTSUPP;
 			}
@@ -1270,6 +1449,24 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
 			case USB_DEVICE_REMOTE_WAKEUP:
 				w_value = USB_DEVICE_REMOTE_WAKEUP;
 				break;
+			case USB_DEVICE_U1_ENABLE:
+				if (dum->gadget.speed == USB_SPEED_SUPER)
+					w_value = USB_DEV_STAT_U1_ENABLED;
+				else
+					ret_val = -EOPNOTSUPP;
+				break;
+			case USB_DEVICE_U2_ENABLE:
+				if (dum->gadget.speed == USB_SPEED_SUPER)
+					w_value = USB_DEV_STAT_U2_ENABLED;
+				else
+					ret_val = -EOPNOTSUPP;
+				break;
+			case USB_DEVICE_LTM_ENABLE:
+				if (dum->gadget.speed == USB_SPEED_SUPER)
+					w_value = USB_DEV_STAT_LTM_ENABLED;
+				else
+					ret_val = -EOPNOTSUPP;
+				break;
 			default:
 				ret_val = -EOPNOTSUPP;
 				break;
@@ -1350,6 +1547,9 @@ static void dummy_timer (unsigned long _dum)
 	case USB_SPEED_HIGH:
 		total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
 		break;
+	case USB_SPEED_SUPER:
+		total = 400 << 20; /* 400MB = 400*(2^20) bytes */
+		break;
 	default:
 		dev_err (dummy_dev(dum), "bogus device speed\n");
 		return;
@@ -1595,7 +1795,168 @@ hub_descriptor (struct usb_hub_descriptor *desc)
 	desc->bitmap [1] = 0xff;
 }
 
-static int dummy_hub_control (
+/**
+ * dummy_ss_hub_control() - handles the control requests of the
+ * dummy (ss hub) master.
+ * @hcd: pointer to the hcd to handle
+ * @typeReq: type of the request
+ * @wValue: wValue of the request
+ * @wIndex: wIndex of the request
+ * @buf: buffer for reply in case of an IN request
+ * @wLength: wLength of the request
+ *
+ * Return int - 0 on success, error code otherwise
+ *
+ * This function handles the control requests of the dummy (ss
+ * hub) master. All control requests that are part of the
+ * enumeration are handled by the dummy_timer
+ * Note: this function is used only for the SS root hub
+ */
+static int dummy_ss_hub_control(
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+) {
+	struct dummy		*dum;
+	int		retval = 0;
+	unsigned long	flags;
+
+	if (!HCD_HW_ACCESSIBLE(hcd))
+		return -ETIMEDOUT;
+
+	dum = hcd_to_dummy(hcd);
+	spin_lock_irqsave(&dum->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+		break;
+	case ClearPortFeature:
+		switch (wValue) {
+		case USB_PORT_FEAT_POWER:
+			if (dum->port_status & USB_SS_PORT_STAT_POWER)
+				dev_dbg(dummy_dev(dum), "power-off\n");
+			/* FALLS THROUGH */
+		default:
+			dum->port_status &= ~(1 << wValue);
+			set_link_state(dum);
+		}
+		break;
+	case GetHubDescriptor:
+		hub_descriptor((struct usb_hub_descriptor *) buf);
+		break;
+	case GetHubStatus:
+		/* We report that no change occured in the hub status
+		 * (power and overcurent conditions)
+		 */
+		*(__le32 *) buf = cpu_to_le32 (0);
+		break;
+	case GetPortStatus:
+		/* We have only one port */
+		if (wIndex != 1)
+			retval = -EPIPE;
+
+		/* whoever resets or resumes must GetPortStatus to
+		 * complete it!!
+		 */
+		/* TODO: add support for suspend/resume */
+		if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
+			time_after_eq(jiffies, dum->re_timeout)) {
+			dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
+			dum->port_status &= ~USB_PORT_STAT_RESET;
+		}
+		if (dum->pullup)
+			dum->port_status |= USB_PORT_STAT_ENABLE;
+
+		set_link_state(dum);
+
+		((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
+		((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
+		break;
+	case SetHubFeature:
+		retval = -EPIPE;
+		break;
+	case SetPortFeature:
+		switch (wValue) {
+		case USB_PORT_FEAT_LINK_STATE:
+			/* Since this is dummy we don't have an actual link so
+			 * there is nothing to do for the SET_LINK_STATE cmd
+			 */
+			break;
+		case USB_PORT_FEAT_U1_TIMEOUT:
+		case USB_PORT_FEAT_U2_TIMEOUT:
+			/* TODO: add suspend/resume support! */
+			break;
+		case USB_PORT_FEAT_POWER:
+			dum->port_status |= USB_SS_PORT_STAT_POWER;
+			set_link_state(dum);
+			break;
+		case USB_PORT_FEAT_BH_PORT_RESET:
+		case USB_PORT_FEAT_RESET:
+			/* if it's already enabled, disable */
+			dum->port_status = 0;
+			dum->port_status = (USB_SS_PORT_STAT_POWER |
+					       USB_PORT_STAT_CONNECTION |
+					       USB_PORT_STAT_RESET);
+			/* We want to reset device status. All but the
+			 * Self powered feature
+			 */
+			dum->devstatus &= 0x0000 |
+				(1 << USB_DEVICE_SELF_POWERED);
+			/* FIXME: what is the correct reset signaling interval?
+			 * Is it still 50msec as for HS?
+			 */
+			dum->re_timeout = jiffies + msecs_to_jiffies(50);
+			/* FALLS THROUGH */
+		default:
+			if ((dum->port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				dum->port_status |= (1 << wValue);
+				set_link_state(dum);
+			}
+		}
+		break;
+	case GetPortErrorCount:
+		/* We'll always return 0 since this is a dummy hub */
+		*(__le32 *) buf = cpu_to_le32 (0);
+		break;
+	case SetHubDepth:
+		break;
+	default:
+		dev_dbg(dummy_dev(dum),
+			"hub control req%04x v%04x i%04x l%d\n",
+			typeReq, wValue, wIndex, wLength);
+
+		/* "protocol stall" on error */
+		retval = -EPIPE;
+	}
+	spin_unlock_irqrestore(&dum->lock, flags);
+
+	if ((dum->port_status & PORT_C_MASK) != 0)
+		usb_hcd_poll_rh_status(hcd);
+	return retval;
+}
+
+
+/**
+ * dummy_hub_control() - handles the control requests of the
+ * dummy (hs hub) master.
+ * @hcd: pointer to the hcd to handle
+ * @typeReq: type of the request
+ * @wValue: wValue of the request
+ * @wIndex: wIndex of the request
+ * @buf: buffer for reply in case of an IN request
+ * @wLength: wLength of the request
+ *
+ * Return int - 0 on success, error code otherwise
+ *
+ * This function handles the control requests of the dummy (hs
+ * hub) master. All control requests that are part of the
+ * enumeration are handled by the dummy_timer
+ * Note: this function is used only for the HS root hub
+ */
+static int dummy_hub_control(
 	struct usb_hcd	*hcd,
 	u16		typeReq,
 	u16		wValue,
@@ -1709,7 +2070,11 @@ static int dummy_hub_control (
 			dum->port_status &= ~(USB_PORT_STAT_ENABLE
 					| USB_PORT_STAT_LOW_SPEED
 					| USB_PORT_STAT_HIGH_SPEED);
-			dum->devstatus = 0;
+			/*
+			 * We want to reset device status. All but the
+			 * Self powered feature
+			 */
+			dum->devstatus &= (1 << USB_DEVICE_SELF_POWERED);
 			/* 50msec reset signaling */
 			dum->re_timeout = jiffies + msecs_to_jiffies(50);
 			/* FALLS THROUGH */
@@ -1892,6 +2257,27 @@ static const struct hc_driver dummy_hcd = {
 	.bus_resume =		dummy_bus_resume,
 };
 
+static const struct hc_driver dummy_ss_hcd = {
+	.description =		(char *) ss_driver_name,
+	.product_desc =		"Dummy SS host controller",
+	.hcd_priv_size =	sizeof(struct dummy),
+
+	.flags =		HCD_USB3,
+
+	.start =		dummy_start,
+	.stop =			dummy_stop,
+
+	.urb_enqueue =		dummy_urb_enqueue,
+	.urb_dequeue =		dummy_urb_dequeue,
+
+	.get_frame_number =	dummy_h_get_frame,
+
+	.hub_status_data =	dummy_hub_status,
+	.hub_control =		dummy_ss_hub_control,
+	.bus_suspend =		dummy_bus_suspend,
+	.bus_resume =		dummy_bus_resume,
+};
+
 static int dummy_hcd_probe(struct platform_device *pdev)
 {
 	struct usb_hcd		*hcd;
@@ -1912,6 +2298,26 @@ static int dummy_hcd_probe(struct platform_device *pdev)
 	return retval;
 }
 
+static int dummy_hcd_probe_ss(struct platform_device *pdev)
+{
+	struct usb_hcd		*hcd;
+	int			retval;
+
+	dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", ss_driver_desc);
+
+	hcd = usb_create_hcd(&dummy_ss_hcd, &pdev->dev, dev_name(&pdev->dev));
+	if (!hcd)
+		return -ENOMEM;
+	the_ss_controller = hcd_to_dummy(hcd);
+
+	retval = usb_add_hcd(hcd, 0, 0);
+	if (retval != 0) {
+		usb_put_hcd(hcd);
+		the_ss_controller = NULL;
+	}
+	return retval;
+}
+
 static int dummy_hcd_remove (struct platform_device *pdev)
 {
 	struct usb_hcd		*hcd;
@@ -1923,6 +2329,17 @@ static int dummy_hcd_remove (struct platform_device *pdev)
 	return 0;
 }
 
+static int dummy_ss_hcd_remove(struct platform_device *pdev)
+{
+	struct usb_hcd		*hcd;
+
+	hcd = platform_get_drvdata(pdev);
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+	the_ss_controller = NULL;
+	return 0;
+}
+
 static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state)
 {
 	struct usb_hcd		*hcd;
@@ -1964,10 +2381,23 @@ static struct platform_driver dummy_hcd_driver = {
 	},
 };
 
+static struct platform_driver dummy_ss_hcd_driver = {
+	.probe		= dummy_hcd_probe_ss,
+	.remove		= dummy_ss_hcd_remove,
+	.suspend	= dummy_hcd_suspend,
+	.resume		= dummy_hcd_resume,
+	.driver		= {
+		.name	= (char *) ss_driver_name,
+		.owner	= THIS_MODULE,
+	},
+};
+
 /*-------------------------------------------------------------------------*/
 
 static struct platform_device *the_udc_pdev;
+static struct platform_device *the_ss_udc_pdev;
 static struct platform_device *the_hcd_pdev;
+static struct platform_device *the_ss_hcd_pdev;
 
 static int __init init (void)
 {
@@ -1979,34 +2409,73 @@ static int __init init (void)
 	the_hcd_pdev = platform_device_alloc(driver_name, -1);
 	if (!the_hcd_pdev)
 		return retval;
+
+	the_ss_hcd_pdev = platform_device_alloc(ss_driver_name, -1);
+	if (!the_ss_hcd_pdev)
+		goto err_alloc_ss_hcd;
+
 	the_udc_pdev = platform_device_alloc(gadget_name, -1);
 	if (!the_udc_pdev)
 		goto err_alloc_udc;
 
+	the_ss_udc_pdev = platform_device_alloc(ss_gadget_name, -1);
+	if (!the_ss_udc_pdev)
+		goto err_alloc_ss_udc;
+
 	retval = platform_driver_register(&dummy_hcd_driver);
 	if (retval < 0)
 		goto err_register_hcd_driver;
+
+	retval = platform_driver_register(&dummy_ss_hcd_driver);
+	if (retval < 0)
+		goto err_register_ss_hcd_driver;
+
 	retval = platform_driver_register(&dummy_udc_driver);
 	if (retval < 0)
 		goto err_register_udc_driver;
 
+	retval = platform_driver_register(&dummy_ss_udc_driver);
+	if (retval < 0)
+		goto err_register_ss_udc_driver;
+
 	retval = platform_device_add(the_hcd_pdev);
 	if (retval < 0)
 		goto err_add_hcd;
+
+	retval = platform_device_add(the_ss_hcd_pdev);
+	if (retval < 0)
+		goto err_add_ss_hcd;
+
 	retval = platform_device_add(the_udc_pdev);
 	if (retval < 0)
 		goto err_add_udc;
+
+	retval = platform_device_add(the_ss_udc_pdev);
+	if (retval < 0)
+		goto err_add_ss_udc;
 	return retval;
 
+err_add_ss_udc:
+	platform_device_unregister(the_udc_pdev);
 err_add_udc:
+	platform_device_del(the_ss_hcd_pdev);
+err_add_ss_hcd:
 	platform_device_del(the_hcd_pdev);
 err_add_hcd:
+	platform_driver_unregister(&dummy_ss_udc_driver);
+err_register_ss_udc_driver:
 	platform_driver_unregister(&dummy_udc_driver);
 err_register_udc_driver:
+	platform_driver_unregister(&dummy_ss_hcd_driver);
+err_register_ss_hcd_driver:
 	platform_driver_unregister(&dummy_hcd_driver);
 err_register_hcd_driver:
+	platform_device_put(the_ss_udc_pdev);
+err_alloc_ss_udc:
 	platform_device_put(the_udc_pdev);
 err_alloc_udc:
+	platform_device_put(the_ss_hcd_pdev);
+err_alloc_ss_hcd:
 	platform_device_put(the_hcd_pdev);
 	return retval;
 }
@@ -2015,8 +2484,12 @@ module_init (init);
 static void __exit cleanup (void)
 {
 	platform_device_unregister(the_udc_pdev);
+	platform_device_unregister(the_ss_udc_pdev);
 	platform_device_unregister(the_hcd_pdev);
+	platform_device_unregister(the_ss_hcd_pdev);
 	platform_driver_unregister(&dummy_udc_driver);
+	platform_driver_unregister(&dummy_ss_udc_driver);
 	platform_driver_unregister(&dummy_hcd_driver);
+	platform_driver_unregister(&dummy_ss_hcd_driver);
 }
 module_exit (cleanup);
-- 
1.6.3.3

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v4 1/3] usb: USB3.0 ch11 definitions
  2010-11-01 15:38 ` [PATCH v4 1/3] usb: USB3.0 ch11 definitions Tatyana Brokhman
  2010-11-01 15:38   ` [PATCH v4 2/3] usb: dummy_hcd code simplification Tatyana Brokhman
@ 2010-11-01 15:48   ` Greg KH
  1 sibling, 0 replies; 6+ messages in thread
From: Greg KH @ 2010-11-01 15:48 UTC (permalink / raw)
  To: Tatyana Brokhman
  Cc: linux-usb, linux-arm-msm, Greg Kroah-Hartman, Alan Stern,
	Sarah Sharp, Jacob Pan, linux-kernel

On Mon, Nov 01, 2010 at 05:38:04PM +0200, Tatyana Brokhman wrote:
> Adding hub SuperSpeed usb definitions as defined by ch10 of the USB3.0
> spec.
> 
> Signed-off-by: tlinder <tlinder@codeaurora.org>

Please use your real name here.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd
  2010-11-01 15:38     ` [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd Tatyana Brokhman
@ 2010-11-15 10:52       ` Meena Jain
  2010-11-15 13:43         ` Tanya Brokhman
  0 siblings, 1 reply; 6+ messages in thread
From: Meena Jain @ 2010-11-15 10:52 UTC (permalink / raw)
  To: Tatyana Brokhman
  Cc: linux-usb, linux-arm-msm, David Brownell, Greg Kroah-Hartman,
	Alan Stern, linux-kernel

Hi Tanya!

I have doubt regarding the below changes done in
usb_gadget_register_driver() w.r.t superspeed.

>        dum->gadget.ep0 = &dum->ep [0].ep;
> -       dum->ep [0].ep.maxpacket = 64;
>        list_del_init (&dum->ep [0].ep.ep_list);
>        INIT_LIST_HEAD(&dum->fifo_req.queue);
>
> @@ -806,6 +915,11 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
>        /* khubd will enumerate this in a while */
>        spin_lock_irq (&dum->lock);
>        dum->pullup = 1;
> +       dum->gadget.speed = driver->speed;;
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum->ep[0].ep.maxpacket = 9;
> +       else
> +               dum->ep[0].ep.maxpacket = 64;
>        set_link_state (dum);
>        spin_unlock_irq (&dum->lock);

Here, we are setting the ep[0].ep.maxpacket size as 9 or 64 depending
upon speed. As per the patch, ep maxpacket size information is being
updated after the driver->bind function is called i.e. after below
code

retval = driver->bind(&dum->gadget);

Earlier this information was being updating before the bind function called.

Due to this, we are getting wrong packet size as "0xffff" instead of
64 (in case of high speed) inside the bind function which is causing
enumeration problem while loading the existing file_storage gadget
driver.

IMO, the above code should be placed before bind function call.

Please confirm.

Thanks and Regards,

Meetu



On Mon, Nov 1, 2010 at 9:08 PM, Tatyana Brokhman <tlinder@codeaurora.org> wrote:
> USB 3.0 hub includes 2 hubs - HS and SS ones.
> Thus, when dummy_hcd enabled it will register 2 root hubs (SS and HS).
>
> Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
> ---
>  drivers/usb/gadget/dummy_hcd.c |  501 ++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 487 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
> index ef34c4d..71e1eaf 100644
> --- a/drivers/usb/gadget/dummy_hcd.c
> +++ b/drivers/usb/gadget/dummy_hcd.c
> @@ -5,6 +5,7 @@
>  *
>  * Copyright (C) 2003 David Brownell
>  * Copyright (C) 2003-2005 Alan Stern
> + * Copyright (C) 2010 Code Aurora Forum. All rights reserved.
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
> @@ -61,10 +62,13 @@
>
>  #define POWER_BUDGET   500     /* in mA; use 8 for low-power port testing */
>
> -static const char      driver_name [] = "dummy_hcd";
> -static const char      driver_desc [] = "USB Host+Gadget Emulator";
> +static const char      driver_name[] = "dummy_hcd";
> +static const char      ss_driver_name[] = "ss_dummy_hcd";
> +static const char      driver_desc[] = "USB Host+Gadget Emulator";
> +static const char      ss_driver_desc[] = "SS USB Host+Gadget Emulator";
>
> -static const char      gadget_name [] = "dummy_udc";
> +static const char      gadget_name[] = "dummy_udc";
> +static const char      ss_gadget_name[] = "ss_dummy_udc";
>
>  MODULE_DESCRIPTION (DRIVER_DESC);
>  MODULE_AUTHOR ("David Brownell");
> @@ -220,6 +224,7 @@ static inline struct dummy *gadget_dev_to_dummy (struct device *dev)
>  }
>
>  static struct dummy                    *the_controller;
> +static struct dummy                    *the_ss_controller;
>
>  /*-------------------------------------------------------------------------*/
>
> @@ -259,10 +264,97 @@ stop_activity (struct dummy *dum)
>        /* driver now does any non-usb quiescing necessary */
>  }
>
> -/* caller must hold lock */
> +/**
> + * set_ss_link_state() - Sets the current state of the
> + * SuperSpeed link
> + * @dum: pointer to the dummy_hcd structure to update the link
> + *     state for
> + *
> + * This function updates the port_status according to the link
> + * state. The old status is saved befor updating.
> + * Note: this function should be called only for SuperSpeed
> + * master and the caller must hold the lock.
> + */
> +static void
> +set_ss_link_state(struct dummy *dum)
> +{
> +       dum->active = 0;
> +       if ((dum->port_status & USB_SS_PORT_STAT_POWER) == 0)
> +               dum->port_status = 0;
> +
> +       /* UDC suspend must cause a disconnect */
> +       else if (!dum->pullup || dum->udc_suspended) {
> +               dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
> +                                       USB_PORT_STAT_ENABLE);
> +               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
> +                       dum->port_status |=
> +                                       (USB_PORT_STAT_C_CONNECTION << 16);
> +       } else {
> +               /* device is connected and not suspended */
> +               dum->port_status |= (USB_PORT_STAT_CONNECTION |
> +                                    USB_PORT_STAT_SUPER_SPEED);
> +               if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
> +                       dum->port_status |=
> +                               (USB_PORT_STAT_C_CONNECTION << 16);
> +               if ((dum->port_status & USB_PORT_STAT_ENABLE) == 1 &&
> +                   (dum->port_status & USB_SS_PORT_LS_U0) == 1 &&
> +                   dum->rh_state != DUMMY_RH_SUSPENDED)
> +                       dum->active = 1;
> +       }
> +
> +
> +       if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 ||
> +           dum->active)
> +               dum->resuming = 0;
> +
> +       /* if !connected or reset */
> +       if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
> +                       (dum->port_status & USB_PORT_STAT_RESET) != 0) {
> +               /*
> +                * We're connected and not reseted (reset occured now),
> +                * and driver attached - disconnect!
> +                */
> +               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
> +                   (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
> +                   dum->driver) {
> +                       stop_activity(dum);
> +                       spin_unlock(&dum->lock);
> +                       dum->driver->disconnect(&dum->gadget);
> +                       spin_lock(&dum->lock);
> +               }
> +       } else if (dum->active != dum->old_active) {
> +               if (dum->old_active && dum->driver->suspend) {
> +                       spin_unlock(&dum->lock);
> +                       dum->driver->suspend(&dum->gadget);
> +                       spin_lock(&dum->lock);
> +               } else if (!dum->old_active &&
> +                          dum->driver->resume) {
> +                       spin_unlock(&dum->lock);
> +                       dum->driver->resume(&dum->gadget);
> +                       spin_lock(&dum->lock);
> +               }
> +       }
> +
> +       dum->old_status = dum->port_status;
> +       dum->old_active = dum->active;
> +}
> +
> +/**
> + * set_link_state() - Sets the current state of the link
> + * @dum: pointer to the dummy_hcd structure to update the link
> + *     state for
> + *
> + * This function updates the port_status according to the link
> + * state. The old status is saved befor updating.
> + * Note: caller must hold the lock.
> + */
>  static void
>  set_link_state (struct dummy *dum)
>  {
> +       if (dum == the_ss_controller) {
> +               set_ss_link_state(dum);
> +               return;
> +       }
>        dum->active = 0;
>        if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
>                dum->port_status = 0;
> @@ -343,7 +435,13 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
>        dum = ep_to_dummy (ep);
>        if (!dum->driver || !is_enabled (dum))
>                return -ESHUTDOWN;
> -       max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
> +       max = le16_to_cpu(desc->wMaxPacketSize) ;
> +       /*
> +        * For HS/FS devices only bits 0..9 of the wMaxPacketSize represent the
> +        * maximum packet size
> +        */
> +       if (dum->gadget.speed < USB_SPEED_SUPER)
> +               max &= 0x3ff;
>
>        /* drivers must not request bad settings, since lower levels
>         * (hardware or its drivers) may not check.  some endpoints
> @@ -361,6 +459,10 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
>                        goto done;
>                }
>                switch (dum->gadget.speed) {
> +               case USB_SPEED_SUPER:
> +                       if (max == 1024)
> +                               break;
> +                       goto done;
>                case USB_SPEED_HIGH:
>                        if (max == 512)
>                                break;
> @@ -379,6 +481,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
>                        goto done;
>                /* real hardware might not handle all packet sizes */
>                switch (dum->gadget.speed) {
> +               case USB_SPEED_SUPER:
>                case USB_SPEED_HIGH:
>                        if (max <= 1024)
>                                break;
> @@ -399,6 +502,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
>                        goto done;
>                /* real hardware might not handle all packet sizes */
>                switch (dum->gadget.speed) {
> +               case USB_SPEED_SUPER:
>                case USB_SPEED_HIGH:
>                        if (max <= 1024)
>                                break;
> @@ -750,16 +854,22 @@ static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
>  int
>  usb_gadget_register_driver (struct usb_gadget_driver *driver)
>  {
> -       struct dummy    *dum = the_controller;
> +       struct dummy    *dum;
>        int             retval, i;
>
> +       if (!driver->bind || !driver->setup
> +                       || driver->speed == USB_SPEED_UNKNOWN)
> +               return -EINVAL;
> +
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum = the_ss_controller;
> +       else
> +               dum = the_controller;
> +
>        if (!dum)
>                return -EINVAL;
>        if (dum->driver)
>                return -EBUSY;
> -       if (!driver->bind || !driver->setup
> -                       || driver->speed == USB_SPEED_UNKNOWN)
> -               return -EINVAL;
>
>        /*
>         * SLAVE side init ... the layer above hardware, which
> @@ -787,7 +897,6 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
>        }
>
>        dum->gadget.ep0 = &dum->ep [0].ep;
> -       dum->ep [0].ep.maxpacket = 64;
>        list_del_init (&dum->ep [0].ep.ep_list);
>        INIT_LIST_HEAD(&dum->fifo_req.queue);
>
> @@ -806,6 +915,11 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
>        /* khubd will enumerate this in a while */
>        spin_lock_irq (&dum->lock);
>        dum->pullup = 1;
> +       dum->gadget.speed = driver->speed;;
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum->ep[0].ep.maxpacket = 9;
> +       else
> +               dum->ep[0].ep.maxpacket = 64;
>        set_link_state (dum);
>        spin_unlock_irq (&dum->lock);
>
> @@ -817,12 +931,20 @@ EXPORT_SYMBOL (usb_gadget_register_driver);
>  int
>  usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
>  {
> -       struct dummy    *dum = the_controller;
> +       struct dummy    *dum ;
>        unsigned long   flags;
>
> +       if (!driver || !driver->unbind)
> +               return -EINVAL;
> +
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum = the_ss_controller;
> +       else
> +               dum = the_controller;
> +
>        if (!dum)
>                return -ENODEV;
> -       if (!driver || driver != dum->driver || !driver->unbind)
> +       if (driver != dum->driver)
>                return -EINVAL;
>
>        dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
> @@ -897,6 +1019,34 @@ static int dummy_udc_probe (struct platform_device *pdev)
>        return rc;
>  }
>
> +static int dummy_ss_udc_probe(struct platform_device *pdev)
> +{
> +       struct dummy    *dum = the_ss_controller;
> +       int             rc;
> +
> +       dum->gadget.name = gadget_name;
> +       dum->gadget.ops = &dummy_ops;
> +       dum->gadget.is_dualspeed = 1;
> +
> +       /* maybe claim OTG support, though we won't complete HNP */
> +       dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
> +
> +       dev_set_name(&dum->gadget.dev, "ss_gadget");
> +       dum->gadget.dev.parent = &pdev->dev;
> +       dum->gadget.dev.release = dummy_gadget_release;
> +       rc = device_register(&dum->gadget.dev);
> +       if (rc < 0)
> +               return rc;
> +
> +       usb_get_hcd(dummy_to_hcd(dum));
> +
> +       platform_set_drvdata(pdev, dum);
> +       rc = device_create_file(&dum->gadget.dev, &dev_attr_function);
> +       if (rc < 0)
> +               device_unregister(&dum->gadget.dev);
> +       return rc;
> +}
> +
>  static int dummy_udc_remove (struct platform_device *pdev)
>  {
>        struct dummy    *dum = platform_get_drvdata (pdev);
> @@ -946,6 +1096,17 @@ static struct platform_driver dummy_udc_driver = {
>        },
>  };
>
> +static struct platform_driver dummy_ss_udc_driver = {
> +       .probe          = dummy_ss_udc_probe,
> +       .remove         = dummy_udc_remove,
> +       .suspend        = dummy_udc_suspend,
> +       .resume         = dummy_udc_resume,
> +       .driver         = {
> +               .name   = (char *) ss_gadget_name,
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
>  /*-------------------------------------------------------------------------*/
>
>  /* MASTER/HOST SIDE DRIVER
> @@ -1244,6 +1405,24 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
>                        case USB_DEVICE_A_ALT_HNP_SUPPORT:
>                                dum->gadget.a_alt_hnp_support = 1;
>                                break;
> +                       case USB_DEVICE_U1_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U1_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_U2_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U2_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_LTM_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_LTM_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
>                        default:
>                                ret_val = -EOPNOTSUPP;
>                        }
> @@ -1270,6 +1449,24 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
>                        case USB_DEVICE_REMOTE_WAKEUP:
>                                w_value = USB_DEVICE_REMOTE_WAKEUP;
>                                break;
> +                       case USB_DEVICE_U1_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U1_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_U2_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U2_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_LTM_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_LTM_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
>                        default:
>                                ret_val = -EOPNOTSUPP;
>                                break;
> @@ -1350,6 +1547,9 @@ static void dummy_timer (unsigned long _dum)
>        case USB_SPEED_HIGH:
>                total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
>                break;
> +       case USB_SPEED_SUPER:
> +               total = 400 << 20; /* 400MB = 400*(2^20) bytes */
> +               break;
>        default:
>                dev_err (dummy_dev(dum), "bogus device speed\n");
>                return;
> @@ -1595,7 +1795,168 @@ hub_descriptor (struct usb_hub_descriptor *desc)
>        desc->bitmap [1] = 0xff;
>  }
>
> -static int dummy_hub_control (
> +/**
> + * dummy_ss_hub_control() - handles the control requests of the
> + * dummy (ss hub) master.
> + * @hcd: pointer to the hcd to handle
> + * @typeReq: type of the request
> + * @wValue: wValue of the request
> + * @wIndex: wIndex of the request
> + * @buf: buffer for reply in case of an IN request
> + * @wLength: wLength of the request
> + *
> + * Return int - 0 on success, error code otherwise
> + *
> + * This function handles the control requests of the dummy (ss
> + * hub) master. All control requests that are part of the
> + * enumeration are handled by the dummy_timer
> + * Note: this function is used only for the SS root hub
> + */
> +static int dummy_ss_hub_control(
> +       struct usb_hcd  *hcd,
> +       u16             typeReq,
> +       u16             wValue,
> +       u16             wIndex,
> +       char            *buf,
> +       u16             wLength
> +) {
> +       struct dummy            *dum;
> +       int             retval = 0;
> +       unsigned long   flags;
> +
> +       if (!HCD_HW_ACCESSIBLE(hcd))
> +               return -ETIMEDOUT;
> +
> +       dum = hcd_to_dummy(hcd);
> +       spin_lock_irqsave(&dum->lock, flags);
> +       switch (typeReq) {
> +       case ClearHubFeature:
> +               break;
> +       case ClearPortFeature:
> +               switch (wValue) {
> +               case USB_PORT_FEAT_POWER:
> +                       if (dum->port_status & USB_SS_PORT_STAT_POWER)
> +                               dev_dbg(dummy_dev(dum), "power-off\n");
> +                       /* FALLS THROUGH */
> +               default:
> +                       dum->port_status &= ~(1 << wValue);
> +                       set_link_state(dum);
> +               }
> +               break;
> +       case GetHubDescriptor:
> +               hub_descriptor((struct usb_hub_descriptor *) buf);
> +               break;
> +       case GetHubStatus:
> +               /* We report that no change occured in the hub status
> +                * (power and overcurent conditions)
> +                */
> +               *(__le32 *) buf = cpu_to_le32 (0);
> +               break;
> +       case GetPortStatus:
> +               /* We have only one port */
> +               if (wIndex != 1)
> +                       retval = -EPIPE;
> +
> +               /* whoever resets or resumes must GetPortStatus to
> +                * complete it!!
> +                */
> +               /* TODO: add support for suspend/resume */
> +               if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
> +                       time_after_eq(jiffies, dum->re_timeout)) {
> +                       dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
> +                       dum->port_status &= ~USB_PORT_STAT_RESET;
> +               }
> +               if (dum->pullup)
> +                       dum->port_status |= USB_PORT_STAT_ENABLE;
> +
> +               set_link_state(dum);
> +
> +               ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
> +               ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
> +               break;
> +       case SetHubFeature:
> +               retval = -EPIPE;
> +               break;
> +       case SetPortFeature:
> +               switch (wValue) {
> +               case USB_PORT_FEAT_LINK_STATE:
> +                       /* Since this is dummy we don't have an actual link so
> +                        * there is nothing to do for the SET_LINK_STATE cmd
> +                        */
> +                       break;
> +               case USB_PORT_FEAT_U1_TIMEOUT:
> +               case USB_PORT_FEAT_U2_TIMEOUT:
> +                       /* TODO: add suspend/resume support! */
> +                       break;
> +               case USB_PORT_FEAT_POWER:
> +                       dum->port_status |= USB_SS_PORT_STAT_POWER;
> +                       set_link_state(dum);
> +                       break;
> +               case USB_PORT_FEAT_BH_PORT_RESET:
> +               case USB_PORT_FEAT_RESET:
> +                       /* if it's already enabled, disable */
> +                       dum->port_status = 0;
> +                       dum->port_status = (USB_SS_PORT_STAT_POWER |
> +                                              USB_PORT_STAT_CONNECTION |
> +                                              USB_PORT_STAT_RESET);
> +                       /* We want to reset device status. All but the
> +                        * Self powered feature
> +                        */
> +                       dum->devstatus &= 0x0000 |
> +                               (1 << USB_DEVICE_SELF_POWERED);
> +                       /* FIXME: what is the correct reset signaling interval?
> +                        * Is it still 50msec as for HS?
> +                        */
> +                       dum->re_timeout = jiffies + msecs_to_jiffies(50);
> +                       /* FALLS THROUGH */
> +               default:
> +                       if ((dum->port_status &
> +                            USB_SS_PORT_STAT_POWER) != 0) {
> +                               dum->port_status |= (1 << wValue);
> +                               set_link_state(dum);
> +                       }
> +               }
> +               break;
> +       case GetPortErrorCount:
> +               /* We'll always return 0 since this is a dummy hub */
> +               *(__le32 *) buf = cpu_to_le32 (0);
> +               break;
> +       case SetHubDepth:
> +               break;
> +       default:
> +               dev_dbg(dummy_dev(dum),
> +                       "hub control req%04x v%04x i%04x l%d\n",
> +                       typeReq, wValue, wIndex, wLength);
> +
> +               /* "protocol stall" on error */
> +               retval = -EPIPE;
> +       }
> +       spin_unlock_irqrestore(&dum->lock, flags);
> +
> +       if ((dum->port_status & PORT_C_MASK) != 0)
> +               usb_hcd_poll_rh_status(hcd);
> +       return retval;
> +}
> +
> +
> +/**
> + * dummy_hub_control() - handles the control requests of the
> + * dummy (hs hub) master.
> + * @hcd: pointer to the hcd to handle
> + * @typeReq: type of the request
> + * @wValue: wValue of the request
> + * @wIndex: wIndex of the request
> + * @buf: buffer for reply in case of an IN request
> + * @wLength: wLength of the request
> + *
> + * Return int - 0 on success, error code otherwise
> + *
> + * This function handles the control requests of the dummy (hs
> + * hub) master. All control requests that are part of the
> + * enumeration are handled by the dummy_timer
> + * Note: this function is used only for the HS root hub
> + */
> +static int dummy_hub_control(
>        struct usb_hcd  *hcd,
>        u16             typeReq,
>        u16             wValue,
> @@ -1709,7 +2070,11 @@ static int dummy_hub_control (
>                        dum->port_status &= ~(USB_PORT_STAT_ENABLE
>                                        | USB_PORT_STAT_LOW_SPEED
>                                        | USB_PORT_STAT_HIGH_SPEED);
> -                       dum->devstatus = 0;
> +                       /*
> +                        * We want to reset device status. All but the
> +                        * Self powered feature
> +                        */
> +                       dum->devstatus &= (1 << USB_DEVICE_SELF_POWERED);
>                        /* 50msec reset signaling */
>                        dum->re_timeout = jiffies + msecs_to_jiffies(50);
>                        /* FALLS THROUGH */
> @@ -1892,6 +2257,27 @@ static const struct hc_driver dummy_hcd = {
>        .bus_resume =           dummy_bus_resume,
>  };
>
> +static const struct hc_driver dummy_ss_hcd = {
> +       .description =          (char *) ss_driver_name,
> +       .product_desc =         "Dummy SS host controller",
> +       .hcd_priv_size =        sizeof(struct dummy),
> +
> +       .flags =                HCD_USB3,
> +
> +       .start =                dummy_start,
> +       .stop =                 dummy_stop,
> +
> +       .urb_enqueue =          dummy_urb_enqueue,
> +       .urb_dequeue =          dummy_urb_dequeue,
> +
> +       .get_frame_number =     dummy_h_get_frame,
> +
> +       .hub_status_data =      dummy_hub_status,
> +       .hub_control =          dummy_ss_hub_control,
> +       .bus_suspend =          dummy_bus_suspend,
> +       .bus_resume =           dummy_bus_resume,
> +};
> +
>  static int dummy_hcd_probe(struct platform_device *pdev)
>  {
>        struct usb_hcd          *hcd;
> @@ -1912,6 +2298,26 @@ static int dummy_hcd_probe(struct platform_device *pdev)
>        return retval;
>  }
>
> +static int dummy_hcd_probe_ss(struct platform_device *pdev)
> +{
> +       struct usb_hcd          *hcd;
> +       int                     retval;
> +
> +       dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", ss_driver_desc);
> +
> +       hcd = usb_create_hcd(&dummy_ss_hcd, &pdev->dev, dev_name(&pdev->dev));
> +       if (!hcd)
> +               return -ENOMEM;
> +       the_ss_controller = hcd_to_dummy(hcd);
> +
> +       retval = usb_add_hcd(hcd, 0, 0);
> +       if (retval != 0) {
> +               usb_put_hcd(hcd);
> +               the_ss_controller = NULL;
> +       }
> +       return retval;
> +}
> +
>  static int dummy_hcd_remove (struct platform_device *pdev)
>  {
>        struct usb_hcd          *hcd;
> @@ -1923,6 +2329,17 @@ static int dummy_hcd_remove (struct platform_device *pdev)
>        return 0;
>  }
>
> +static int dummy_ss_hcd_remove(struct platform_device *pdev)
> +{
> +       struct usb_hcd          *hcd;
> +
> +       hcd = platform_get_drvdata(pdev);
> +       usb_remove_hcd(hcd);
> +       usb_put_hcd(hcd);
> +       the_ss_controller = NULL;
> +       return 0;
> +}
> +
>  static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state)
>  {
>        struct usb_hcd          *hcd;
> @@ -1964,10 +2381,23 @@ static struct platform_driver dummy_hcd_driver = {
>        },
>  };
>
> +static struct platform_driver dummy_ss_hcd_driver = {
> +       .probe          = dummy_hcd_probe_ss,
> +       .remove         = dummy_ss_hcd_remove,
> +       .suspend        = dummy_hcd_suspend,
> +       .resume         = dummy_hcd_resume,
> +       .driver         = {
> +               .name   = (char *) ss_driver_name,
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
>  /*-------------------------------------------------------------------------*/
>
>  static struct platform_device *the_udc_pdev;
> +static struct platform_device *the_ss_udc_pdev;
>  static struct platform_device *the_hcd_pdev;
> +static struct platform_device *the_ss_hcd_pdev;
>
>  static int __init init (void)
>  {
> @@ -1979,34 +2409,73 @@ static int __init init (void)
>        the_hcd_pdev = platform_device_alloc(driver_name, -1);
>        if (!the_hcd_pdev)
>                return retval;
> +
> +       the_ss_hcd_pdev = platform_device_alloc(ss_driver_name, -1);
> +       if (!the_ss_hcd_pdev)
> +               goto err_alloc_ss_hcd;
> +
>        the_udc_pdev = platform_device_alloc(gadget_name, -1);
>        if (!the_udc_pdev)
>                goto err_alloc_udc;
>
> +       the_ss_udc_pdev = platform_device_alloc(ss_gadget_name, -1);
> +       if (!the_ss_udc_pdev)
> +               goto err_alloc_ss_udc;
> +
>        retval = platform_driver_register(&dummy_hcd_driver);
>        if (retval < 0)
>                goto err_register_hcd_driver;
> +
> +       retval = platform_driver_register(&dummy_ss_hcd_driver);
> +       if (retval < 0)
> +               goto err_register_ss_hcd_driver;
> +
>        retval = platform_driver_register(&dummy_udc_driver);
>        if (retval < 0)
>                goto err_register_udc_driver;
>
> +       retval = platform_driver_register(&dummy_ss_udc_driver);
> +       if (retval < 0)
> +               goto err_register_ss_udc_driver;
> +
>        retval = platform_device_add(the_hcd_pdev);
>        if (retval < 0)
>                goto err_add_hcd;
> +
> +       retval = platform_device_add(the_ss_hcd_pdev);
> +       if (retval < 0)
> +               goto err_add_ss_hcd;
> +
>        retval = platform_device_add(the_udc_pdev);
>        if (retval < 0)
>                goto err_add_udc;
> +
> +       retval = platform_device_add(the_ss_udc_pdev);
> +       if (retval < 0)
> +               goto err_add_ss_udc;
>        return retval;
>
> +err_add_ss_udc:
> +       platform_device_unregister(the_udc_pdev);
>  err_add_udc:
> +       platform_device_del(the_ss_hcd_pdev);
> +err_add_ss_hcd:
>        platform_device_del(the_hcd_pdev);
>  err_add_hcd:
> +       platform_driver_unregister(&dummy_ss_udc_driver);
> +err_register_ss_udc_driver:
>        platform_driver_unregister(&dummy_udc_driver);
>  err_register_udc_driver:
> +       platform_driver_unregister(&dummy_ss_hcd_driver);
> +err_register_ss_hcd_driver:
>        platform_driver_unregister(&dummy_hcd_driver);
>  err_register_hcd_driver:
> +       platform_device_put(the_ss_udc_pdev);
> +err_alloc_ss_udc:
>        platform_device_put(the_udc_pdev);
>  err_alloc_udc:
> +       platform_device_put(the_ss_hcd_pdev);
> +err_alloc_ss_hcd:
>        platform_device_put(the_hcd_pdev);
>        return retval;
>  }
> @@ -2015,8 +2484,12 @@ module_init (init);
>  static void __exit cleanup (void)
>  {
>        platform_device_unregister(the_udc_pdev);
> +       platform_device_unregister(the_ss_udc_pdev);
>        platform_device_unregister(the_hcd_pdev);
> +       platform_device_unregister(the_ss_hcd_pdev);
>        platform_driver_unregister(&dummy_udc_driver);
> +       platform_driver_unregister(&dummy_ss_udc_driver);
>        platform_driver_unregister(&dummy_hcd_driver);
> +       platform_driver_unregister(&dummy_ss_hcd_driver);
>  }
>  module_exit (cleanup);
> --
> 1.6.3.3
>
> --
> Sent by an employee of the Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd
  2010-11-15 10:52       ` Meena Jain
@ 2010-11-15 13:43         ` Tanya Brokhman
  0 siblings, 0 replies; 6+ messages in thread
From: Tanya Brokhman @ 2010-11-15 13:43 UTC (permalink / raw)
  To: 'Meena Jain'
  Cc: linux-usb, linux-arm-msm, 'David Brownell',
	'Greg Kroah-Hartman', 'Alan Stern',
	linux-kernel

Hi Meetu,

Thank you for your input. You're right in your observation. 
I'll upload a fixed patch soon.

Regards,
Tanya Brokhman
Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum



-----Original Message-----
From: linux-usb-owner@vger.kernel.org
[mailto:linux-usb-owner@vger.kernel.org] On Behalf Of Meena Jain
Sent: Monday, November 15, 2010 12:52 PM
To: Tatyana Brokhman
Cc: linux-usb@vger.kernel.org; linux-arm-msm@vger.kernel.org; David
Brownell; Greg Kroah-Hartman; Alan Stern; linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd

Hi Tanya!

I have doubt regarding the below changes done in
usb_gadget_register_driver() w.r.t superspeed.

>        dum->gadget.ep0 = &dum->ep [0].ep;
> -       dum->ep [0].ep.maxpacket = 64;
>        list_del_init (&dum->ep [0].ep.ep_list);
>        INIT_LIST_HEAD(&dum->fifo_req.queue);
>
> @@ -806,6 +915,11 @@ usb_gadget_register_driver (struct usb_gadget_driver
*driver)
>        /* khubd will enumerate this in a while */
>        spin_lock_irq (&dum->lock);
>        dum->pullup = 1;
> +       dum->gadget.speed = driver->speed;;
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum->ep[0].ep.maxpacket = 9;
> +       else
> +               dum->ep[0].ep.maxpacket = 64;
>        set_link_state (dum);
>        spin_unlock_irq (&dum->lock);

Here, we are setting the ep[0].ep.maxpacket size as 9 or 64 depending
upon speed. As per the patch, ep maxpacket size information is being
updated after the driver->bind function is called i.e. after below
code

retval = driver->bind(&dum->gadget);

Earlier this information was being updating before the bind function called.

Due to this, we are getting wrong packet size as "0xffff" instead of
64 (in case of high speed) inside the bind function which is causing
enumeration problem while loading the existing file_storage gadget
driver.

IMO, the above code should be placed before bind function call.

Please confirm.

Thanks and Regards,

Meetu



On Mon, Nov 1, 2010 at 9:08 PM, Tatyana Brokhman <tlinder@codeaurora.org>
wrote:
> USB 3.0 hub includes 2 hubs - HS and SS ones.
> Thus, when dummy_hcd enabled it will register 2 root hubs (SS and HS).
>
> Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
> ---
>  drivers/usb/gadget/dummy_hcd.c |  501
++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 487 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/usb/gadget/dummy_hcd.c
b/drivers/usb/gadget/dummy_hcd.c
> index ef34c4d..71e1eaf 100644
> --- a/drivers/usb/gadget/dummy_hcd.c
> +++ b/drivers/usb/gadget/dummy_hcd.c
> @@ -5,6 +5,7 @@
>  *
>  * Copyright (C) 2003 David Brownell
>  * Copyright (C) 2003-2005 Alan Stern
> + * Copyright (C) 2010 Code Aurora Forum. All rights reserved.
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
> @@ -61,10 +62,13 @@
>
>  #define POWER_BUDGET   500     /* in mA; use 8 for low-power port testing
*/
>
> -static const char      driver_name [] = "dummy_hcd";
> -static const char      driver_desc [] = "USB Host+Gadget Emulator";
> +static const char      driver_name[] = "dummy_hcd";
> +static const char      ss_driver_name[] = "ss_dummy_hcd";
> +static const char      driver_desc[] = "USB Host+Gadget Emulator";
> +static const char      ss_driver_desc[] = "SS USB Host+Gadget Emulator";
>
> -static const char      gadget_name [] = "dummy_udc";
> +static const char      gadget_name[] = "dummy_udc";
> +static const char      ss_gadget_name[] = "ss_dummy_udc";
>
>  MODULE_DESCRIPTION (DRIVER_DESC);
>  MODULE_AUTHOR ("David Brownell");
> @@ -220,6 +224,7 @@ static inline struct dummy *gadget_dev_to_dummy
(struct device *dev)
>  }
>
>  static struct dummy                    *the_controller;
> +static struct dummy                    *the_ss_controller;
>
>
 /*-------------------------------------------------------------------------
*/
>
> @@ -259,10 +264,97 @@ stop_activity (struct dummy *dum)
>        /* driver now does any non-usb quiescing necessary */
>  }
>
> -/* caller must hold lock */
> +/**
> + * set_ss_link_state() - Sets the current state of the
> + * SuperSpeed link
> + * @dum: pointer to the dummy_hcd structure to update the link
> + *     state for
> + *
> + * This function updates the port_status according to the link
> + * state. The old status is saved befor updating.
> + * Note: this function should be called only for SuperSpeed
> + * master and the caller must hold the lock.
> + */
> +static void
> +set_ss_link_state(struct dummy *dum)
> +{
> +       dum->active = 0;
> +       if ((dum->port_status & USB_SS_PORT_STAT_POWER) == 0)
> +               dum->port_status = 0;
> +
> +       /* UDC suspend must cause a disconnect */
> +       else if (!dum->pullup || dum->udc_suspended) {
> +               dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
> +                                       USB_PORT_STAT_ENABLE);
> +               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
> +                       dum->port_status |=
> +                                       (USB_PORT_STAT_C_CONNECTION <<
16);
> +       } else {
> +               /* device is connected and not suspended */
> +               dum->port_status |= (USB_PORT_STAT_CONNECTION |
> +                                    USB_PORT_STAT_SUPER_SPEED);
> +               if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
> +                       dum->port_status |=
> +                               (USB_PORT_STAT_C_CONNECTION << 16);
> +               if ((dum->port_status & USB_PORT_STAT_ENABLE) == 1 &&
> +                   (dum->port_status & USB_SS_PORT_LS_U0) == 1 &&
> +                   dum->rh_state != DUMMY_RH_SUSPENDED)
> +                       dum->active = 1;
> +       }
> +
> +
> +       if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 ||
> +           dum->active)
> +               dum->resuming = 0;
> +
> +       /* if !connected or reset */
> +       if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
> +                       (dum->port_status & USB_PORT_STAT_RESET) != 0) {
> +               /*
> +                * We're connected and not reseted (reset occured now),
> +                * and driver attached - disconnect!
> +                */
> +               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
> +                   (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
> +                   dum->driver) {
> +                       stop_activity(dum);
> +                       spin_unlock(&dum->lock);
> +                       dum->driver->disconnect(&dum->gadget);
> +                       spin_lock(&dum->lock);
> +               }
> +       } else if (dum->active != dum->old_active) {
> +               if (dum->old_active && dum->driver->suspend) {
> +                       spin_unlock(&dum->lock);
> +                       dum->driver->suspend(&dum->gadget);
> +                       spin_lock(&dum->lock);
> +               } else if (!dum->old_active &&
> +                          dum->driver->resume) {
> +                       spin_unlock(&dum->lock);
> +                       dum->driver->resume(&dum->gadget);
> +                       spin_lock(&dum->lock);
> +               }
> +       }
> +
> +       dum->old_status = dum->port_status;
> +       dum->old_active = dum->active;
> +}
> +
> +/**
> + * set_link_state() - Sets the current state of the link
> + * @dum: pointer to the dummy_hcd structure to update the link
> + *     state for
> + *
> + * This function updates the port_status according to the link
> + * state. The old status is saved befor updating.
> + * Note: caller must hold the lock.
> + */
>  static void
>  set_link_state (struct dummy *dum)
>  {
> +       if (dum == the_ss_controller) {
> +               set_ss_link_state(dum);
> +               return;
> +       }
>        dum->active = 0;
>        if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
>                dum->port_status = 0;
> @@ -343,7 +435,13 @@ dummy_enable (struct usb_ep *_ep, const struct
usb_endpoint_descriptor *desc)
>        dum = ep_to_dummy (ep);
>        if (!dum->driver || !is_enabled (dum))
>                return -ESHUTDOWN;
> -       max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
> +       max = le16_to_cpu(desc->wMaxPacketSize) ;
> +       /*
> +        * For HS/FS devices only bits 0..9 of the wMaxPacketSize
represent the
> +        * maximum packet size
> +        */
> +       if (dum->gadget.speed < USB_SPEED_SUPER)
> +               max &= 0x3ff;
>
>        /* drivers must not request bad settings, since lower levels
>         * (hardware or its drivers) may not check.  some endpoints
> @@ -361,6 +459,10 @@ dummy_enable (struct usb_ep *_ep, const struct
usb_endpoint_descriptor *desc)
>                        goto done;
>                }
>                switch (dum->gadget.speed) {
> +               case USB_SPEED_SUPER:
> +                       if (max == 1024)
> +                               break;
> +                       goto done;
>                case USB_SPEED_HIGH:
>                        if (max == 512)
>                                break;
> @@ -379,6 +481,7 @@ dummy_enable (struct usb_ep *_ep, const struct
usb_endpoint_descriptor *desc)
>                        goto done;
>                /* real hardware might not handle all packet sizes */
>                switch (dum->gadget.speed) {
> +               case USB_SPEED_SUPER:
>                case USB_SPEED_HIGH:
>                        if (max <= 1024)
>                                break;
> @@ -399,6 +502,7 @@ dummy_enable (struct usb_ep *_ep, const struct
usb_endpoint_descriptor *desc)
>                        goto done;
>                /* real hardware might not handle all packet sizes */
>                switch (dum->gadget.speed) {
> +               case USB_SPEED_SUPER:
>                case USB_SPEED_HIGH:
>                        if (max <= 1024)
>                                break;
> @@ -750,16 +854,22 @@ static DEVICE_ATTR (function, S_IRUGO,
show_function, NULL);
>  int
>  usb_gadget_register_driver (struct usb_gadget_driver *driver)
>  {
> -       struct dummy    *dum = the_controller;
> +       struct dummy    *dum;
>        int             retval, i;
>
> +       if (!driver->bind || !driver->setup
> +                       || driver->speed == USB_SPEED_UNKNOWN)
> +               return -EINVAL;
> +
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum = the_ss_controller;
> +       else
> +               dum = the_controller;
> +
>        if (!dum)
>                return -EINVAL;
>        if (dum->driver)
>                return -EBUSY;
> -       if (!driver->bind || !driver->setup
> -                       || driver->speed == USB_SPEED_UNKNOWN)
> -               return -EINVAL;
>
>        /*
>         * SLAVE side init ... the layer above hardware, which
> @@ -787,7 +897,6 @@ usb_gadget_register_driver (struct usb_gadget_driver
*driver)
>        }
>
>        dum->gadget.ep0 = &dum->ep [0].ep;
> -       dum->ep [0].ep.maxpacket = 64;
>        list_del_init (&dum->ep [0].ep.ep_list);
>        INIT_LIST_HEAD(&dum->fifo_req.queue);
>
> @@ -806,6 +915,11 @@ usb_gadget_register_driver (struct usb_gadget_driver
*driver)
>        /* khubd will enumerate this in a while */
>        spin_lock_irq (&dum->lock);
>        dum->pullup = 1;
> +       dum->gadget.speed = driver->speed;;
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum->ep[0].ep.maxpacket = 9;
> +       else
> +               dum->ep[0].ep.maxpacket = 64;
>        set_link_state (dum);
>        spin_unlock_irq (&dum->lock);
>
> @@ -817,12 +931,20 @@ EXPORT_SYMBOL (usb_gadget_register_driver);
>  int
>  usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
>  {
> -       struct dummy    *dum = the_controller;
> +       struct dummy    *dum ;
>        unsigned long   flags;
>
> +       if (!driver || !driver->unbind)
> +               return -EINVAL;
> +
> +       if (driver->speed == USB_SPEED_SUPER)
> +               dum = the_ss_controller;
> +       else
> +               dum = the_controller;
> +
>        if (!dum)
>                return -ENODEV;
> -       if (!driver || driver != dum->driver || !driver->unbind)
> +       if (driver != dum->driver)
>                return -EINVAL;
>
>        dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
> @@ -897,6 +1019,34 @@ static int dummy_udc_probe (struct platform_device
*pdev)
>        return rc;
>  }
>
> +static int dummy_ss_udc_probe(struct platform_device *pdev)
> +{
> +       struct dummy    *dum = the_ss_controller;
> +       int             rc;
> +
> +       dum->gadget.name = gadget_name;
> +       dum->gadget.ops = &dummy_ops;
> +       dum->gadget.is_dualspeed = 1;
> +
> +       /* maybe claim OTG support, though we won't complete HNP */
> +       dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
> +
> +       dev_set_name(&dum->gadget.dev, "ss_gadget");
> +       dum->gadget.dev.parent = &pdev->dev;
> +       dum->gadget.dev.release = dummy_gadget_release;
> +       rc = device_register(&dum->gadget.dev);
> +       if (rc < 0)
> +               return rc;
> +
> +       usb_get_hcd(dummy_to_hcd(dum));
> +
> +       platform_set_drvdata(pdev, dum);
> +       rc = device_create_file(&dum->gadget.dev, &dev_attr_function);
> +       if (rc < 0)
> +               device_unregister(&dum->gadget.dev);
> +       return rc;
> +}
> +
>  static int dummy_udc_remove (struct platform_device *pdev)
>  {
>        struct dummy    *dum = platform_get_drvdata (pdev);
> @@ -946,6 +1096,17 @@ static struct platform_driver dummy_udc_driver = {
>        },
>  };
>
> +static struct platform_driver dummy_ss_udc_driver = {
> +       .probe          = dummy_ss_udc_probe,
> +       .remove         = dummy_udc_remove,
> +       .suspend        = dummy_udc_suspend,
> +       .resume         = dummy_udc_resume,
> +       .driver         = {
> +               .name   = (char *) ss_gadget_name,
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
>
 /*-------------------------------------------------------------------------
*/
>
>  /* MASTER/HOST SIDE DRIVER
> @@ -1244,6 +1405,24 @@ static int handle_control_request(struct dummy
*dum, struct urb *urb,
>                        case USB_DEVICE_A_ALT_HNP_SUPPORT:
>                                dum->gadget.a_alt_hnp_support = 1;
>                                break;
> +                       case USB_DEVICE_U1_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U1_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_U2_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U2_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_LTM_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value =
USB_DEV_STAT_LTM_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
>                        default:
>                                ret_val = -EOPNOTSUPP;
>                        }
> @@ -1270,6 +1449,24 @@ static int handle_control_request(struct dummy
*dum, struct urb *urb,
>                        case USB_DEVICE_REMOTE_WAKEUP:
>                                w_value = USB_DEVICE_REMOTE_WAKEUP;
>                                break;
> +                       case USB_DEVICE_U1_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U1_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_U2_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value = USB_DEV_STAT_U2_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
> +                       case USB_DEVICE_LTM_ENABLE:
> +                               if (dum->gadget.speed == USB_SPEED_SUPER)
> +                                       w_value =
USB_DEV_STAT_LTM_ENABLED;
> +                               else
> +                                       ret_val = -EOPNOTSUPP;
> +                               break;
>                        default:
>                                ret_val = -EOPNOTSUPP;
>                                break;
> @@ -1350,6 +1547,9 @@ static void dummy_timer (unsigned long _dum)
>        case USB_SPEED_HIGH:
>                total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
>                break;
> +       case USB_SPEED_SUPER:
> +               total = 400 << 20; /* 400MB = 400*(2^20) bytes */
> +               break;
>        default:
>                dev_err (dummy_dev(dum), "bogus device speed\n");
>                return;
> @@ -1595,7 +1795,168 @@ hub_descriptor (struct usb_hub_descriptor *desc)
>        desc->bitmap [1] = 0xff;
>  }
>
> -static int dummy_hub_control (
> +/**
> + * dummy_ss_hub_control() - handles the control requests of the
> + * dummy (ss hub) master.
> + * @hcd: pointer to the hcd to handle
> + * @typeReq: type of the request
> + * @wValue: wValue of the request
> + * @wIndex: wIndex of the request
> + * @buf: buffer for reply in case of an IN request
> + * @wLength: wLength of the request
> + *
> + * Return int - 0 on success, error code otherwise
> + *
> + * This function handles the control requests of the dummy (ss
> + * hub) master. All control requests that are part of the
> + * enumeration are handled by the dummy_timer
> + * Note: this function is used only for the SS root hub
> + */
> +static int dummy_ss_hub_control(
> +       struct usb_hcd  *hcd,
> +       u16             typeReq,
> +       u16             wValue,
> +       u16             wIndex,
> +       char            *buf,
> +       u16             wLength
> +) {
> +       struct dummy            *dum;
> +       int             retval = 0;
> +       unsigned long   flags;
> +
> +       if (!HCD_HW_ACCESSIBLE(hcd))
> +               return -ETIMEDOUT;
> +
> +       dum = hcd_to_dummy(hcd);
> +       spin_lock_irqsave(&dum->lock, flags);
> +       switch (typeReq) {
> +       case ClearHubFeature:
> +               break;
> +       case ClearPortFeature:
> +               switch (wValue) {
> +               case USB_PORT_FEAT_POWER:
> +                       if (dum->port_status & USB_SS_PORT_STAT_POWER)
> +                               dev_dbg(dummy_dev(dum), "power-off\n");
> +                       /* FALLS THROUGH */
> +               default:
> +                       dum->port_status &= ~(1 << wValue);
> +                       set_link_state(dum);
> +               }
> +               break;
> +       case GetHubDescriptor:
> +               hub_descriptor((struct usb_hub_descriptor *) buf);
> +               break;
> +       case GetHubStatus:
> +               /* We report that no change occured in the hub status
> +                * (power and overcurent conditions)
> +                */
> +               *(__le32 *) buf = cpu_to_le32 (0);
> +               break;
> +       case GetPortStatus:
> +               /* We have only one port */
> +               if (wIndex != 1)
> +                       retval = -EPIPE;
> +
> +               /* whoever resets or resumes must GetPortStatus to
> +                * complete it!!
> +                */
> +               /* TODO: add support for suspend/resume */
> +               if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
> +                       time_after_eq(jiffies, dum->re_timeout)) {
> +                       dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
> +                       dum->port_status &= ~USB_PORT_STAT_RESET;
> +               }
> +               if (dum->pullup)
> +                       dum->port_status |= USB_PORT_STAT_ENABLE;
> +
> +               set_link_state(dum);
> +
> +               ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
> +               ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >>
16);
> +               break;
> +       case SetHubFeature:
> +               retval = -EPIPE;
> +               break;
> +       case SetPortFeature:
> +               switch (wValue) {
> +               case USB_PORT_FEAT_LINK_STATE:
> +                       /* Since this is dummy we don't have an actual
link so
> +                        * there is nothing to do for the SET_LINK_STATE
cmd
> +                        */
> +                       break;
> +               case USB_PORT_FEAT_U1_TIMEOUT:
> +               case USB_PORT_FEAT_U2_TIMEOUT:
> +                       /* TODO: add suspend/resume support! */
> +                       break;
> +               case USB_PORT_FEAT_POWER:
> +                       dum->port_status |= USB_SS_PORT_STAT_POWER;
> +                       set_link_state(dum);
> +                       break;
> +               case USB_PORT_FEAT_BH_PORT_RESET:
> +               case USB_PORT_FEAT_RESET:
> +                       /* if it's already enabled, disable */
> +                       dum->port_status = 0;
> +                       dum->port_status = (USB_SS_PORT_STAT_POWER |
> +                                              USB_PORT_STAT_CONNECTION |
> +                                              USB_PORT_STAT_RESET);
> +                       /* We want to reset device status. All but the
> +                        * Self powered feature
> +                        */
> +                       dum->devstatus &= 0x0000 |
> +                               (1 << USB_DEVICE_SELF_POWERED);
> +                       /* FIXME: what is the correct reset signaling
interval?
> +                        * Is it still 50msec as for HS?
> +                        */
> +                       dum->re_timeout = jiffies + msecs_to_jiffies(50);
> +                       /* FALLS THROUGH */
> +               default:
> +                       if ((dum->port_status &
> +                            USB_SS_PORT_STAT_POWER) != 0) {
> +                               dum->port_status |= (1 << wValue);
> +                               set_link_state(dum);
> +                       }
> +               }
> +               break;
> +       case GetPortErrorCount:
> +               /* We'll always return 0 since this is a dummy hub */
> +               *(__le32 *) buf = cpu_to_le32 (0);
> +               break;
> +       case SetHubDepth:
> +               break;
> +       default:
> +               dev_dbg(dummy_dev(dum),
> +                       "hub control req%04x v%04x i%04x l%d\n",
> +                       typeReq, wValue, wIndex, wLength);
> +
> +               /* "protocol stall" on error */
> +               retval = -EPIPE;
> +       }
> +       spin_unlock_irqrestore(&dum->lock, flags);
> +
> +       if ((dum->port_status & PORT_C_MASK) != 0)
> +               usb_hcd_poll_rh_status(hcd);
> +       return retval;
> +}
> +
> +
> +/**
> + * dummy_hub_control() - handles the control requests of the
> + * dummy (hs hub) master.
> + * @hcd: pointer to the hcd to handle
> + * @typeReq: type of the request
> + * @wValue: wValue of the request
> + * @wIndex: wIndex of the request
> + * @buf: buffer for reply in case of an IN request
> + * @wLength: wLength of the request
> + *
> + * Return int - 0 on success, error code otherwise
> + *
> + * This function handles the control requests of the dummy (hs
> + * hub) master. All control requests that are part of the
> + * enumeration are handled by the dummy_timer
> + * Note: this function is used only for the HS root hub
> + */
> +static int dummy_hub_control(
>        struct usb_hcd  *hcd,
>        u16             typeReq,
>        u16             wValue,
> @@ -1709,7 +2070,11 @@ static int dummy_hub_control (
>                        dum->port_status &= ~(USB_PORT_STAT_ENABLE
>                                        | USB_PORT_STAT_LOW_SPEED
>                                        | USB_PORT_STAT_HIGH_SPEED);
> -                       dum->devstatus = 0;
> +                       /*
> +                        * We want to reset device status. All but the
> +                        * Self powered feature
> +                        */
> +                       dum->devstatus &= (1 << USB_DEVICE_SELF_POWERED);
>                        /* 50msec reset signaling */
>                        dum->re_timeout = jiffies + msecs_to_jiffies(50);
>                        /* FALLS THROUGH */
> @@ -1892,6 +2257,27 @@ static const struct hc_driver dummy_hcd = {
>        .bus_resume =           dummy_bus_resume,
>  };
>
> +static const struct hc_driver dummy_ss_hcd = {
> +       .description =          (char *) ss_driver_name,
> +       .product_desc =         "Dummy SS host controller",
> +       .hcd_priv_size =        sizeof(struct dummy),
> +
> +       .flags =                HCD_USB3,
> +
> +       .start =                dummy_start,
> +       .stop =                 dummy_stop,
> +
> +       .urb_enqueue =          dummy_urb_enqueue,
> +       .urb_dequeue =          dummy_urb_dequeue,
> +
> +       .get_frame_number =     dummy_h_get_frame,
> +
> +       .hub_status_data =      dummy_hub_status,
> +       .hub_control =          dummy_ss_hub_control,
> +       .bus_suspend =          dummy_bus_suspend,
> +       .bus_resume =           dummy_bus_resume,
> +};
> +
>  static int dummy_hcd_probe(struct platform_device *pdev)
>  {
>        struct usb_hcd          *hcd;
> @@ -1912,6 +2298,26 @@ static int dummy_hcd_probe(struct platform_device
*pdev)
>        return retval;
>  }
>
> +static int dummy_hcd_probe_ss(struct platform_device *pdev)
> +{
> +       struct usb_hcd          *hcd;
> +       int                     retval;
> +
> +       dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n",
ss_driver_desc);
> +
> +       hcd = usb_create_hcd(&dummy_ss_hcd, &pdev->dev,
dev_name(&pdev->dev));
> +       if (!hcd)
> +               return -ENOMEM;
> +       the_ss_controller = hcd_to_dummy(hcd);
> +
> +       retval = usb_add_hcd(hcd, 0, 0);
> +       if (retval != 0) {
> +               usb_put_hcd(hcd);
> +               the_ss_controller = NULL;
> +       }
> +       return retval;
> +}
> +
>  static int dummy_hcd_remove (struct platform_device *pdev)
>  {
>        struct usb_hcd          *hcd;
> @@ -1923,6 +2329,17 @@ static int dummy_hcd_remove (struct platform_device
*pdev)
>        return 0;
>  }
>
> +static int dummy_ss_hcd_remove(struct platform_device *pdev)
> +{
> +       struct usb_hcd          *hcd;
> +
> +       hcd = platform_get_drvdata(pdev);
> +       usb_remove_hcd(hcd);
> +       usb_put_hcd(hcd);
> +       the_ss_controller = NULL;
> +       return 0;
> +}
> +
>  static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t
state)
>  {
>        struct usb_hcd          *hcd;
> @@ -1964,10 +2381,23 @@ static struct platform_driver dummy_hcd_driver = {
>        },
>  };
>
> +static struct platform_driver dummy_ss_hcd_driver = {
> +       .probe          = dummy_hcd_probe_ss,
> +       .remove         = dummy_ss_hcd_remove,
> +       .suspend        = dummy_hcd_suspend,
> +       .resume         = dummy_hcd_resume,
> +       .driver         = {
> +               .name   = (char *) ss_driver_name,
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
>
 /*-------------------------------------------------------------------------
*/
>
>  static struct platform_device *the_udc_pdev;
> +static struct platform_device *the_ss_udc_pdev;
>  static struct platform_device *the_hcd_pdev;
> +static struct platform_device *the_ss_hcd_pdev;
>
>  static int __init init (void)
>  {
> @@ -1979,34 +2409,73 @@ static int __init init (void)
>        the_hcd_pdev = platform_device_alloc(driver_name, -1);
>        if (!the_hcd_pdev)
>                return retval;
> +
> +       the_ss_hcd_pdev = platform_device_alloc(ss_driver_name, -1);
> +       if (!the_ss_hcd_pdev)
> +               goto err_alloc_ss_hcd;
> +
>        the_udc_pdev = platform_device_alloc(gadget_name, -1);
>        if (!the_udc_pdev)
>                goto err_alloc_udc;
>
> +       the_ss_udc_pdev = platform_device_alloc(ss_gadget_name, -1);
> +       if (!the_ss_udc_pdev)
> +               goto err_alloc_ss_udc;
> +
>        retval = platform_driver_register(&dummy_hcd_driver);
>        if (retval < 0)
>                goto err_register_hcd_driver;
> +
> +       retval = platform_driver_register(&dummy_ss_hcd_driver);
> +       if (retval < 0)
> +               goto err_register_ss_hcd_driver;
> +
>        retval = platform_driver_register(&dummy_udc_driver);
>        if (retval < 0)
>                goto err_register_udc_driver;
>
> +       retval = platform_driver_register(&dummy_ss_udc_driver);
> +       if (retval < 0)
> +               goto err_register_ss_udc_driver;
> +
>        retval = platform_device_add(the_hcd_pdev);
>        if (retval < 0)
>                goto err_add_hcd;
> +
> +       retval = platform_device_add(the_ss_hcd_pdev);
> +       if (retval < 0)
> +               goto err_add_ss_hcd;
> +
>        retval = platform_device_add(the_udc_pdev);
>        if (retval < 0)
>                goto err_add_udc;
> +
> +       retval = platform_device_add(the_ss_udc_pdev);
> +       if (retval < 0)
> +               goto err_add_ss_udc;
>        return retval;
>
> +err_add_ss_udc:
> +       platform_device_unregister(the_udc_pdev);
>  err_add_udc:
> +       platform_device_del(the_ss_hcd_pdev);
> +err_add_ss_hcd:
>        platform_device_del(the_hcd_pdev);
>  err_add_hcd:
> +       platform_driver_unregister(&dummy_ss_udc_driver);
> +err_register_ss_udc_driver:
>        platform_driver_unregister(&dummy_udc_driver);
>  err_register_udc_driver:
> +       platform_driver_unregister(&dummy_ss_hcd_driver);
> +err_register_ss_hcd_driver:
>        platform_driver_unregister(&dummy_hcd_driver);
>  err_register_hcd_driver:
> +       platform_device_put(the_ss_udc_pdev);
> +err_alloc_ss_udc:
>        platform_device_put(the_udc_pdev);
>  err_alloc_udc:
> +       platform_device_put(the_ss_hcd_pdev);
> +err_alloc_ss_hcd:
>        platform_device_put(the_hcd_pdev);
>        return retval;
>  }
> @@ -2015,8 +2484,12 @@ module_init (init);
>  static void __exit cleanup (void)
>  {
>        platform_device_unregister(the_udc_pdev);
> +       platform_device_unregister(the_ss_udc_pdev);
>        platform_device_unregister(the_hcd_pdev);
> +       platform_device_unregister(the_ss_hcd_pdev);
>        platform_driver_unregister(&dummy_udc_driver);
> +       platform_driver_unregister(&dummy_ss_udc_driver);
>        platform_driver_unregister(&dummy_hcd_driver);
> +       platform_driver_unregister(&dummy_ss_hcd_driver);
>  }
>  module_exit (cleanup);
> --
> 1.6.3.3
>
> --
> Sent by an employee of the Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-11-15 13:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <1288625887-30303-1-git-send-email-tlinder@codeaurora.org>
2010-11-01 15:38 ` [PATCH v4 1/3] usb: USB3.0 ch11 definitions Tatyana Brokhman
2010-11-01 15:38   ` [PATCH v4 2/3] usb: dummy_hcd code simplification Tatyana Brokhman
2010-11-01 15:38     ` [PATCH v4 3/3] usb: Adding SuperSpeed support to dummy_hcd Tatyana Brokhman
2010-11-15 10:52       ` Meena Jain
2010-11-15 13:43         ` Tanya Brokhman
2010-11-01 15:48   ` [PATCH v4 1/3] usb: USB3.0 ch11 definitions Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).