Skip to content

Commit f2b4237

Browse files
heghegregkh
authored andcommitted
usb: misc: ehset: Rework test mode entry
The USB2.0 spec chapter 11.24.2.13 says that the USB port which is going under test needs to be put in suspend state before sending the test command. Many hubs, don't enforce this precondition and they work fine without this step. We should follow the specification and put the USB port in suspend before sending the test command. Also there are some "special" hubs, which requires to disable the USB port power instead of putting it in suspend. I found out only three hubs which requires this step, but if more are found, they can be added to the list. Since this changes the default implementation, it raises the posibility of finding other broken hubs which are not compliant with the spec and the test command might not work is the port is suspended. If such hubs are found, a similar workaround like the disable part can be implemented to skip putting the port in suspend. Signed-off-by: Razvan Heghedus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b1e9e7e commit f2b4237

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

drivers/usb/misc/ehset.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,52 @@
1818
#define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107
1919
#define TEST_SINGLE_STEP_SET_FEATURE 0x0108
2020

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+
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+
2167
static int ehset_probe(struct usb_interface *intf,
2268
const struct usb_device_id *id)
2369
{
@@ -30,24 +76,36 @@ static int ehset_probe(struct usb_interface *intf,
3076

3177
switch (test_pid) {
3278
case TEST_SE0_NAK_PID:
79+
ret = ehset_prepare_port_for_testing(hub_udev, portnum);
80+
if (!ret)
81+
break;
3382
ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE,
3483
USB_RT_PORT, USB_PORT_FEAT_TEST,
3584
(USB_TEST_SE0_NAK << 8) | portnum,
3685
NULL, 0, 1000, GFP_KERNEL);
3786
break;
3887
case TEST_J_PID:
88+
ret = ehset_prepare_port_for_testing(hub_udev, portnum);
89+
if (!ret)
90+
break;
3991
ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE,
4092
USB_RT_PORT, USB_PORT_FEAT_TEST,
4193
(USB_TEST_J << 8) | portnum, NULL, 0,
4294
1000, GFP_KERNEL);
4395
break;
4496
case TEST_K_PID:
97+
ret = ehset_prepare_port_for_testing(hub_udev, portnum);
98+
if (!ret)
99+
break;
45100
ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE,
46101
USB_RT_PORT, USB_PORT_FEAT_TEST,
47102
(USB_TEST_K << 8) | portnum, NULL, 0,
48103
1000, GFP_KERNEL);
49104
break;
50105
case TEST_PACKET_PID:
106+
ret = ehset_prepare_port_for_testing(hub_udev, portnum);
107+
if (!ret)
108+
break;
51109
ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE,
52110
USB_RT_PORT, USB_PORT_FEAT_TEST,
53111
(USB_TEST_PACKET << 8) | portnum,

0 commit comments

Comments
 (0)