>From 604a04773333c221cb9bd83ed9a0b1f052c47bda Mon Sep 17 00:00:00 2001
From: Andy Luo <[email protected]>
Date: Thu, 4 Nov 2010 13:34:33 +0800
Subject: [PATCH] usb: add usb test mode support

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

Change-Id: I9cd76417ad47c201948159353fbb8af926fe8683
Signed-off-by: Andy Luo <[email protected]>
Signed-off-by: Henry Yuan <[email protected]>
---
 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 ++
 6 files changed, 136 insertions(+), 0 deletions(-)

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

Attachment: 0002-usb-add-usb-test-mode-support.patch
Description: 0002-usb-add-usb-test-mode-support.patch

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to