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  
21  extern 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   */
28  static 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  
ehset_prepare_port_for_testing(struct usb_device * hub_udev,u16 portnum)35  static 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  
ehset_probe(struct usb_interface * intf,const struct usb_device_id * id)67  static 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  
ehset_disconnect(struct usb_interface * intf)164  static void ehset_disconnect(struct usb_interface *intf)
165  {
166  }
167  
168  static 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  };
178  MODULE_DEVICE_TABLE(usb, ehset_id_table);
179  
180  static 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  
187  module_usb_driver(ehset_driver);
188  
189  MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture");
190  MODULE_LICENSE("GPL v2");
191