[PATCH] usb: add usb test mode support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds test mode support for Langwell gadget driver, and it
implements debug interface for Host & Client test mode entry.

Change-Id: Ib82d1e577b0c33ad52866bb227a1a6178340012b
Signed-off-by: Henry Yuan <hang.yuan@xxxxxxxxx>
Signed-off-by: Andy Luo <yifei.luo@xxxxxxxxx>
---
 Documentation/ABI/testing/sysfs-bus-usb |   14 +++++
 drivers/usb/core/sysfs.c                |   18 +++++++
 drivers/usb/gadget/langwell_udc.c       |   20 +++++++
 drivers/usb/host/ehci-dbg.c             |   86 +++++++++++++++++++++++++++++++
 drivers/usb/host/ehci-hub.c             |    7 +++
 drivers/usb/host/ehci.h                 |    1 +
 include/linux/usb/ehci_def.h            |    4 ++
 7 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 294aa86..0f860ee 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -142,3 +142,17 @@ Description:
 		such devices.
 Users:
 		usb_modeswitch
+
+What:		/sys/bus/usb/device/.../test
+Date:		November 2010
+Contact:	Andy Luo <yifei.luo@xxxxxxxxx>
+Description:
+		Writing a Test Mode Selector value to this file will put the
+		device upstream facing port into test mode. There are the
+		availeable selector values (refer to USB 2.0 spec Table 9-7
+		for more info):
+			* 1 -> Test_J
+			* 2 -> Test_K
+			* 3 -> Test_SE0_NAK
+			* 4 -> Test_Packet
+			* 5 -> Test_Force_Enable
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 448f5b4..e864b8d 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -581,6 +581,23 @@ static ssize_t usb_remove_store(struct device *dev,
 }
 static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store);
 
+static ssize_t usb_testmode_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int selector;
+	struct usb_device *udev = to_usb_device(dev);
+
+	if (strict_strtoul(buf, 16, &selector))
+		return -EINVAL;
+	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),	USB_REQ_SET_FEATURE,
+			USB_RECIP_DEVICE, USB_DEVICE_TEST_MODE,
+			selector << 8, NULL, 0, 1000);
+
+	return count;
+}
+static DEVICE_ATTR(test, 0200, NULL, usb_testmode_store);
+
 
 static struct attribute *dev_attrs[] = {
 	/* current configuration's attributes */
@@ -609,6 +626,7 @@ static struct attribute *dev_attrs[] = {
 	&dev_attr_avoid_reset_quirk.attr,
 	&dev_attr_authorized.attr,
 	&dev_attr_remove.attr,
+	&dev_attr_test.attr,
 	NULL,
 };
 static struct attribute_group dev_attr_grp = {
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index 3f17ecf..feaa40a 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -2264,6 +2264,7 @@ static void handle_setup_packet(struct langwell_udc *dev,
 	u16	wValue = le16_to_cpu(setup->wValue);
 	u16	wIndex = le16_to_cpu(setup->wIndex);
 	u16	wLength = le16_to_cpu(setup->wLength);
+	u32	portsc1;
 
 	dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
 
@@ -2364,6 +2365,25 @@ static void handle_setup_packet(struct langwell_udc *dev,
 				dev->gadget.a_alt_hnp_support = 1;
 				dev->dev_status |= (1 << wValue);
 				break;
+			case USB_DEVICE_TEST_MODE:
+				dev_dbg(&dev->pdev->dev, "SETUP: TEST MODE\n");
+				if ((wIndex & 0xff) ||
+					(dev->gadget.speed != USB_SPEED_HIGH))
+					ep0_stall(dev);
+				switch (wIndex >> 8) {
+				case 1:
+				case 2:
+				case 3:
+				case 5:
+				case 4:
+					portsc1 = readl(&dev->op_regs->portsc1);
+					portsc1 |= (wIndex & 0xf00) << 8;
+					writel(portsc1, &dev->op_regs->portsc1);
+					break;
+				default:
+					rc = -EOPNOTSUPP;
+				}
+				break;
 			default:
 				rc = -EOPNOTSUPP;
 				break;
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index df5546b..a163e32 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -18,6 +18,10 @@
 
 /* this file is part of ehci-hcd.c */
 
+#include <linux/seq_file.h>
+
+static int ehci_hub_test_mode(struct usb_hcd *hcd, u16 wIndex);
+
 #define ehci_dbg(ehci, fmt, args...) \
 	dev_dbg (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
 #define ehci_err(ehci, fmt, args...) \
@@ -360,6 +364,9 @@ static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
 static ssize_t debug_lpm_write(struct file *file, const char __user *buffer,
 			      size_t count, loff_t *ppos);
 static int debug_lpm_close(struct inode *inode, struct file *file);
+static int debug_testmode_open(struct inode *, struct file *);
+static ssize_t debug_testmode_write(struct file *file,
+		const char __user *ubuf, size_t count, loff_t *ppos);
 
 static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
 static int debug_close(struct inode *, struct file *);
@@ -389,6 +396,14 @@ static const struct file_operations debug_lpm_fops = {
 	.write		= debug_lpm_write,
 	.release	= debug_lpm_close,
 };
+static const struct file_operations debug_testmode_fops = {
+	.owner		= THIS_MODULE,
+	.open		= debug_testmode_open,
+	.write		= debug_testmode_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
 
 static struct dentry *ehci_debug_root;
 
@@ -1043,6 +1058,67 @@ static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf,
 	return count;
 }
 
+static int debug_testmode_show(struct seq_file *s, void *unused)
+{
+	struct usb_hcd	*hcd;
+	struct ehci_hcd	*ehci;
+	u32 __iomem	*portsc ;
+	u32		test;
+	int		port;
+
+	hcd = bus_to_hcd(s->private);
+	ehci = hcd_to_ehci(hcd);
+	port = HCS_N_PORTS(ehci->hcs_params);
+	while (port) {
+		portsc = &ehci->regs->port_status[port-1];
+		test = (ehci_readl(ehci, portsc)) & 0xf0000;
+
+		if (test == PORT_TEST_FORCE)
+			seq_printf(s, "Port%d mode: force enable\n", port);
+
+		if (test == PORT_TEST_PKT)
+			seq_printf(s, "Port%d mode: test packet\n", port);
+
+		if (test == PORT_TEST_K)
+			seq_printf(s, "Port%d mode: test K\n", port);
+
+		if (test == PORT_TEST_J)
+			seq_printf(s, "Port%d mode: test J\n", port);
+
+		if (test == PORT_TEST_SE0_NAK)
+			seq_printf(s, "Port%d mode: test SE0 NAK\n", port);
+		port--;
+	}
+
+	return 0;
+}
+
+static int debug_testmode_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return single_open(file, debug_testmode_show, inode->i_private);
+}
+
+static ssize_t debug_testmode_write(struct file *file,
+		const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct usb_hcd	*hcd;
+	unsigned long	wIndex;
+	char		buf[8];
+	int		buf_size;
+
+	hcd = bus_to_hcd(file->private_data);
+	buf_size = min(count, (sizeof(buf) - 1));
+	buf[buf_size] = 0;
+	if (copy_from_user(buf, ubuf, buf_size))
+		return -EFAULT;
+	if (strict_strtoul(buf, 16, &wIndex))
+		return -EINVAL;
+	ehci_hub_test_mode(hcd, (u16)wIndex);
+	return count;
+}
+
 static inline void create_debug_files (struct ehci_hcd *ehci)
 {
 	struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
@@ -1072,8 +1148,16 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
 						    &debug_lpm_fops);
 	if (!ehci->debug_registers)
 		goto registers_error;
+
+	ehci->debug_testmode = debugfs_create_file("testmode", S_IRUGO|S_IWUSR,
+						   ehci->debug_dir, bus,
+						   &debug_testmode_fops);
+	if (!ehci->debug_testmode)
+		goto testmode_error;
 	return;
 
+testmode_error:
+	debugfs_remove(ehci->debug_registers);
 registers_error:
 	debugfs_remove(ehci->debug_periodic);
 periodic_error:
@@ -1081,6 +1165,7 @@ periodic_error:
 async_error:
 	debugfs_remove(ehci->debug_dir);
 dir_error:
+	ehci->debug_testmode = NULL;
 	ehci->debug_periodic = NULL;
 	ehci->debug_async = NULL;
 	ehci->debug_dir = NULL;
@@ -1088,6 +1173,7 @@ dir_error:
 
 static inline void remove_debug_files (struct ehci_hcd *ehci)
 {
+	debugfs_remove(ehci->debug_testmode);
 	debugfs_remove(ehci->debug_registers);
 	debugfs_remove(ehci->debug_periodic);
 	debugfs_remove(ehci->debug_async);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 90773c4..c660235 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -1116,6 +1116,13 @@ error_exit:
 	return retval;
 }
 
+static int ehci_hub_test_mode(struct usb_hcd *hcd, u16 wIndex)
+{
+	return ehci_hub_control(hcd, SetPortFeature,
+				USB_PORT_FEAT_TEST, wIndex,
+				NULL, 0);
+}
+
 static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index fd5c887..2c1ef15 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -169,6 +169,7 @@ struct ehci_hcd {			/* one per controller */
 	struct dentry		*debug_periodic;
 	struct dentry		*debug_registers;
 	struct dentry		*debug_lpm;
+	struct dentry		*debug_testmode;
 #endif
 };
 
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
index 2e262cb..c70752c 100644
--- a/include/linux/usb/ehci_def.h
+++ b/include/linux/usb/ehci_def.h
@@ -127,7 +127,11 @@ struct ehci_regs {
 #define PORT_WKDISC_E	(1<<21)		/* wake on disconnect (enable) */
 #define PORT_WKCONN_E	(1<<20)		/* wake on connect (enable) */
 /* 19:16 for port testing */
+#define PORT_TEST_J	(0x1<<16)	/* Test_J mode */
+#define PORT_TEST_K	(0x2<<16)	/* Test_K mode */
+#define PORT_TEST_SE0_NAK	(0x3<<16)	/* Test_SE0_NAK mode */
 #define PORT_TEST_PKT	(0x4<<16)	/* Port Test Control - packet test */
+#define PORT_TEST_FORCE	(0x5<<16)	/* Test_Force_Enable mode */
 #define PORT_LED_OFF	(0<<14)
 #define PORT_LED_AMBER	(1<<14)
 #define PORT_LED_GREEN	(2<<14)
-- 
1.7.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux