| /* Copyright (c) 2010-2011, The Linux Foundation. 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 version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| |
| #include <linux/kernel.h> |
| #include <linux/errno.h> |
| #include <linux/module.h> |
| #include <linux/usb.h> |
| #include <linux/slab.h> |
| #include <linux/usb/ch11.h> |
| #include <linux/usb/hcd.h> |
| |
| #define TEST_SE0_NAK_PID 0x0101 |
| #define TEST_J_PID 0x0102 |
| #define TEST_K_PID 0x0103 |
| #define TEST_PACKET_PID 0x0104 |
| #define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106 |
| #define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107 |
| #define TEST_SINGLE_STEP_SET_FEATURE 0x0108 |
| |
| static int ehset_probe(struct usb_interface *intf, |
| const struct usb_device_id *id) |
| { |
| int status = -1; |
| struct usb_device *dev = interface_to_usbdev(intf); |
| struct usb_device *rh_udev = dev->bus->root_hub; |
| struct usb_device *hub_udev = dev->parent; |
| int port1 = dev->portnum; |
| int test_mode = le16_to_cpu(dev->descriptor.idProduct); |
| |
| switch (test_mode) { |
| case TEST_SE0_NAK_PID: |
| status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), |
| USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST, |
| (3 << 8) | port1, NULL, 0, 1000); |
| break; |
| case TEST_J_PID: |
| status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), |
| USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST, |
| (1 << 8) | port1, NULL, 0, 1000); |
| break; |
| case TEST_K_PID: |
| status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), |
| USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST, |
| (2 << 8) | port1, NULL, 0, 1000); |
| break; |
| case TEST_PACKET_PID: |
| status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), |
| USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST, |
| (4 << 8) | port1, NULL, 0, 1000); |
| break; |
| case TEST_HS_HOST_PORT_SUSPEND_RESUME: |
| /* Test: wait for 15secs -> suspend -> 15secs delay -> resume */ |
| msleep(15 * 1000); |
| status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), |
| USB_REQ_SET_FEATURE, USB_RT_PORT, |
| USB_PORT_FEAT_SUSPEND, port1, NULL, 0, 1000); |
| if (status < 0) |
| break; |
| msleep(15 * 1000); |
| status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), |
| USB_REQ_CLEAR_FEATURE, USB_RT_PORT, |
| USB_PORT_FEAT_SUSPEND, port1, NULL, 0, 1000); |
| break; |
| case TEST_SINGLE_STEP_GET_DEV_DESC: |
| /* Test: wait for 15secs -> GetDescriptor request */ |
| msleep(15 * 1000); |
| { |
| struct usb_device_descriptor *buf; |
| buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| |
| status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
| USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, |
| USB_DT_DEVICE << 8, 0, |
| buf, USB_DT_DEVICE_SIZE, |
| USB_CTRL_GET_TIMEOUT); |
| kfree(buf); |
| } |
| break; |
| case TEST_SINGLE_STEP_SET_FEATURE: |
| /* GetDescriptor's SETUP request -> 15secs delay -> IN & STATUS |
| * Issue request to ehci root hub driver with portnum = 1 |
| */ |
| status = usb_control_msg(rh_udev, usb_sndctrlpipe(rh_udev, 0), |
| USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST, |
| (6 << 8) | 1, NULL, 0, 60 * 1000); |
| |
| break; |
| default: |
| pr_err("%s: undefined test mode ( %X )\n", __func__, test_mode); |
| return -EINVAL; |
| } |
| |
| return (status < 0) ? status : 0; |
| } |
| |
| static void ehset_disconnect(struct usb_interface *intf) |
| { |
| } |
| |
| static struct usb_device_id ehset_id_table[] = { |
| { USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) }, |
| { USB_DEVICE(0x1a0a, TEST_J_PID) }, |
| { USB_DEVICE(0x1a0a, TEST_K_PID) }, |
| { USB_DEVICE(0x1a0a, TEST_PACKET_PID) }, |
| { USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) }, |
| { USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) }, |
| { USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) }, |
| { } /* Terminating entry */ |
| }; |
| |
| MODULE_DEVICE_TABLE(usb, ehset_id_table); |
| |
| static struct usb_driver ehset_driver = { |
| .name = "usb_ehset_test", |
| .probe = ehset_probe, |
| .disconnect = ehset_disconnect, |
| .id_table = ehset_id_table, |
| }; |
| |
| static int __init ehset_init(void) |
| { |
| return usb_register(&ehset_driver); |
| } |
| |
| static void __exit ehset_exit(void) |
| { |
| usb_deregister(&ehset_driver); |
| } |
| |
| module_init(ehset_init); |
| module_exit(ehset_exit); |
| |
| MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture"); |
| MODULE_LICENSE("GPL v2"); |