ehset.c (5764B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. 4 */ 5 6#include <linux/kernel.h> 7#include <linux/errno.h> 8#include <linux/module.h> 9#include <linux/slab.h> 10#include <linux/usb.h> 11#include <linux/usb/ch11.h> 12 13#define TEST_SE0_NAK_PID 0x0101 14#define TEST_J_PID 0x0102 15#define TEST_K_PID 0x0103 16#define TEST_PACKET_PID 0x0104 17#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106 18#define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107 19#define TEST_SINGLE_STEP_SET_FEATURE 0x0108 20 21extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev, 22 const struct usb_device_id *id); 23 24/* 25 * A list of USB hubs which requires to disable the power 26 * to the port before starting the testing procedures. 27 */ 28static const struct usb_device_id ehset_hub_list[] = { 29 { USB_DEVICE(0x0424, 0x4502) }, 30 { USB_DEVICE(0x0424, 0x4913) }, 31 { USB_DEVICE(0x0451, 0x8027) }, 32 { } 33}; 34 35static int ehset_prepare_port_for_testing(struct usb_device *hub_udev, u16 portnum) 36{ 37 int ret = 0; 38 39 /* 40 * The USB2.0 spec chapter 11.24.2.13 says that the USB port which is 41 * going under test needs to be put in suspend before sending the 42 * test command. Most hubs don't enforce this precondition, but there 43 * are some hubs which needs to disable the power to the port before 44 * starting the test. 45 */ 46 if (usb_device_match_id(hub_udev, ehset_hub_list)) { 47 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_CLEAR_FEATURE, 48 USB_RT_PORT, USB_PORT_FEAT_ENABLE, 49 portnum, NULL, 0, 1000, GFP_KERNEL); 50 /* 51 * Wait for the port to be disabled. It's an arbitrary value 52 * which worked every time. 53 */ 54 msleep(100); 55 } else { 56 /* 57 * For the hubs which are compliant with the spec, 58 * put the port in SUSPEND. 59 */ 60 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 61 USB_RT_PORT, USB_PORT_FEAT_SUSPEND, 62 portnum, NULL, 0, 1000, GFP_KERNEL); 63 } 64 return ret; 65} 66 67static int ehset_probe(struct usb_interface *intf, 68 const struct usb_device_id *id) 69{ 70 int ret = -EINVAL; 71 struct usb_device *dev = interface_to_usbdev(intf); 72 struct usb_device *hub_udev = dev->parent; 73 struct usb_device_descriptor buf; 74 u8 portnum = dev->portnum; 75 u16 test_pid = le16_to_cpu(dev->descriptor.idProduct); 76 77 switch (test_pid) { 78 case TEST_SE0_NAK_PID: 79 ret = ehset_prepare_port_for_testing(hub_udev, portnum); 80 if (!ret) 81 break; 82 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 83 USB_RT_PORT, USB_PORT_FEAT_TEST, 84 (USB_TEST_SE0_NAK << 8) | portnum, 85 NULL, 0, 1000, GFP_KERNEL); 86 break; 87 case TEST_J_PID: 88 ret = ehset_prepare_port_for_testing(hub_udev, portnum); 89 if (!ret) 90 break; 91 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 92 USB_RT_PORT, USB_PORT_FEAT_TEST, 93 (USB_TEST_J << 8) | portnum, NULL, 0, 94 1000, GFP_KERNEL); 95 break; 96 case TEST_K_PID: 97 ret = ehset_prepare_port_for_testing(hub_udev, portnum); 98 if (!ret) 99 break; 100 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 101 USB_RT_PORT, USB_PORT_FEAT_TEST, 102 (USB_TEST_K << 8) | portnum, NULL, 0, 103 1000, GFP_KERNEL); 104 break; 105 case TEST_PACKET_PID: 106 ret = ehset_prepare_port_for_testing(hub_udev, portnum); 107 if (!ret) 108 break; 109 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 110 USB_RT_PORT, USB_PORT_FEAT_TEST, 111 (USB_TEST_PACKET << 8) | portnum, 112 NULL, 0, 1000, GFP_KERNEL); 113 break; 114 case TEST_HS_HOST_PORT_SUSPEND_RESUME: 115 /* Test: wait for 15secs -> suspend -> 15secs delay -> resume */ 116 msleep(15 * 1000); 117 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 118 USB_RT_PORT, USB_PORT_FEAT_SUSPEND, 119 portnum, NULL, 0, 1000, GFP_KERNEL); 120 if (ret < 0) 121 break; 122 123 msleep(15 * 1000); 124 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_CLEAR_FEATURE, 125 USB_RT_PORT, USB_PORT_FEAT_SUSPEND, 126 portnum, NULL, 0, 1000, GFP_KERNEL); 127 break; 128 case TEST_SINGLE_STEP_GET_DEV_DESC: 129 /* Test: wait for 15secs -> GetDescriptor request */ 130 msleep(15 * 1000); 131 132 ret = usb_control_msg_recv(dev, 0, USB_REQ_GET_DESCRIPTOR, 133 USB_DIR_IN, USB_DT_DEVICE << 8, 0, 134 &buf, USB_DT_DEVICE_SIZE, 135 USB_CTRL_GET_TIMEOUT, GFP_KERNEL); 136 break; 137 case TEST_SINGLE_STEP_SET_FEATURE: 138 /* 139 * GetDescriptor SETUP request -> 15secs delay -> IN & STATUS 140 * 141 * Note, this test is only supported on root hubs since the 142 * SetPortFeature handling can only be done inside the HCD's 143 * hub_control callback function. 144 */ 145 if (hub_udev != dev->bus->root_hub) { 146 dev_err(&intf->dev, "SINGLE_STEP_SET_FEATURE test only supported on root hub\n"); 147 break; 148 } 149 150 ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, 151 USB_RT_PORT, USB_PORT_FEAT_TEST, 152 (6 << 8) | portnum, NULL, 0, 153 60 * 1000, GFP_KERNEL); 154 155 break; 156 default: 157 dev_err(&intf->dev, "%s: unsupported PID: 0x%x\n", 158 __func__, test_pid); 159 } 160 161 return ret; 162} 163 164static void ehset_disconnect(struct usb_interface *intf) 165{ 166} 167 168static const struct usb_device_id ehset_id_table[] = { 169 { USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) }, 170 { USB_DEVICE(0x1a0a, TEST_J_PID) }, 171 { USB_DEVICE(0x1a0a, TEST_K_PID) }, 172 { USB_DEVICE(0x1a0a, TEST_PACKET_PID) }, 173 { USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) }, 174 { USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) }, 175 { USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) }, 176 { } /* Terminating entry */ 177}; 178MODULE_DEVICE_TABLE(usb, ehset_id_table); 179 180static struct usb_driver ehset_driver = { 181 .name = "usb_ehset_test", 182 .probe = ehset_probe, 183 .disconnect = ehset_disconnect, 184 .id_table = ehset_id_table, 185}; 186 187module_usb_driver(ehset_driver); 188 189MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture"); 190MODULE_LICENSE("GPL v2");