A small tool to test this patch.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>

#define USBDEVFS_POWER_RESET       _IO('U', 28)

int main(int argc, char **argv)
{
        const char *filename;
        int fd;
        int rc;

        if (argc != 2) {
                fprintf(stderr, "Usage: usbreset device-filename\n");
                return 1;
        }
        filename = argv[1];

        fd = open(filename, O_WRONLY);
        if (fd < 0) {
                perror("Error opening output file");
                return 1;
        }

        printf("Repowering USB device %s\n", filename);
        rc = ioctl(fd, USBDEVFS_POWER_RESET, 0);
        if (rc < 0) {
                perror("Error in ioctl");
                return 1;
        }
        printf("Repower successful\n");

        close(fd);
        return 0;
}


Best Regards 
Tianyu Lan 


-----Original Message-----
From: Lan, Tianyu 
Sent: Thursday, March 28, 2013 1:11 AM
To: gre...@linuxfoundation.org; sarah.a.sh...@linux.intel.com; 
st...@rowland.harvard.edu
Cc: linux-usb@vger.kernel.org; Lan, Tianyu
Subject: [PATCH 2/4] usb: introduce usb force power off mechanism

Some devices' firmware will be broken at some points. Power down and power on 
device can help device to rework in this case.

This patch is to add ioctl cmd USBDEVFS_POWER_RESET for usbfs node to repower 
usb device. First, call hub_port_logical_disconnect() to disconnect device. 
Second, Power down and up usb port.

This patch is also helpful fo some QAs who want to do hcd's memleak test(Plug 
and unplug device thousand times.)

Signed-off-by: Lan Tianyu <tianyu....@intel.com>
---
 drivers/usb/core/devio.c          |   13 +++++++++++++
 drivers/usb/core/hub.c            |   16 ++++++++++++++++
 drivers/usb/core/usb.h            |    2 ++
 include/uapi/linux/usbdevice_fs.h |    1 +
 4 files changed, 32 insertions(+)

diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 
8823e98..a76b326 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1102,6 +1102,14 @@ static int proc_resetdevice(struct dev_state *ps)
        return usb_reset_device(ps->dev);
 }
 
+static int proc_resetdevicepower(struct dev_state *ps) {
+       struct usb_device *udev = ps->dev;
+       struct usb_device *hdev = udev->parent;
+
+       return usb_hub_port_power_reset(hdev, udev->portnum); }
+
 static int proc_setintf(struct dev_state *ps, void __user *arg)  {
        struct usbdevfs_setinterface setintf;
@@ -2011,6 +2019,11 @@ static long usbdev_do_ioctl(struct file *file, unsigned 
int cmd,
                ret = proc_resetdevice(ps);
                break;
 
+       case USBDEVFS_POWER_RESET:
+               snoop(&dev->dev, "%s: RESET POWER\n", __func__);
+               ret = proc_resetdevicepower(ps);
+               break;
+
        case USBDEVFS_CLEAR_HALT:
                snoop(&dev->dev, "%s: CLEAR_HALT\n", __func__);
                ret = proc_clearhalt(ps, p);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 
6b7f5b9..c228e69 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -114,6 +114,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 #define HUB_DEBOUNCE_STABLE     100
 
 static int usb_reset_and_verify_device(struct usb_device *udev);
+static void hub_port_logical_disconnect(struct usb_hub *hub, int 
+port1);
 
 static inline char *portspeed(struct usb_hub *hub, int portstatus)  { @@ 
-741,6 +742,21 @@ int usb_hub_set_port_power(struct usb_device *hdev, int port1,
        return ret;
 }
 
+int usb_hub_port_power_reset(struct usb_device *hdev, int port1) {
+       struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+       struct usb_interface *intf = to_usb_interface(hub->intfdev);
+       int ret;
+
+       usb_autopm_get_interface(intf);
+       hub_port_logical_disconnect(hub, port1);
+       ret = usb_hub_set_port_power(hdev, port1, false);
+       ret |= usb_hub_set_port_power(hdev, port1, true);
+       usb_autopm_put_interface(intf);
+
+       return ret;
+}
+
 /**
  * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
  * @urb: an URB associated with the failed or incomplete split transaction 
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 
a7f20bd..4e7785c 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -185,6 +185,8 @@ extern void usb_set_hub_port_connect_type(struct usb_device 
*hdev, int port1,
        enum usb_port_connect_type type);
 extern void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
                struct usb_hub_descriptor *desc);
+extern int usb_hub_port_power_reset(struct usb_device *hdev,
+               int port1);
 
 #ifdef CONFIG_ACPI
 extern int usb_acpi_register(void);
diff --git a/include/uapi/linux/usbdevice_fs.h 
b/include/uapi/linux/usbdevice_fs.h
index 0c65e4b..b6e0d17 100644
--- a/include/uapi/linux/usbdevice_fs.h
+++ b/include/uapi/linux/usbdevice_fs.h
@@ -176,5 +176,6 @@ struct usbdevfs_disconnect_claim {
 #define USBDEVFS_RELEASE_PORT      _IOR('U', 25, unsigned int)
 #define USBDEVFS_GET_CAPABILITIES  _IOR('U', 26, __u32)  #define 
USBDEVFS_DISCONNECT_CLAIM  _IOR('U', 27, struct usbdevfs_disconnect_claim)
+#define USBDEVFS_POWER_RESET      _IO('U', 28)
 
 #endif /* _UAPI_LINUX_USBDEVICE_FS_H */
--
1.7.9.5

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>

#define USBDEVFS_POWER_RESET       _IO('U', 28)

int main(int argc, char **argv)
{
        const char *filename;
        int fd;
        int rc;

        if (argc != 2) {
                fprintf(stderr, "Usage: usbreset device-filename\n");
                return 1;
        }
        filename = argv[1];

        fd = open(filename, O_WRONLY);
        if (fd < 0) {
                perror("Error opening output file");
                return 1;
        }

        printf("Repowering USB device %s\n", filename);
        rc = ioctl(fd, USBDEVFS_POWER_RESET, 0);
        if (rc < 0) {
                perror("Error in ioctl");
                return 1;
        }
        printf("Repower successful\n");

        close(fd);
        return 0;
}

Reply via email to