xref: /linux/drivers/usb/misc/lvstest.c (revision c4ba329cabca7c839ab48fb58b5bcc2582951a48)
1ce21bfe6SPratyush Anand /*
2ce21bfe6SPratyush Anand  * drivers/usb/misc/lvstest.c
3ce21bfe6SPratyush Anand  *
4ce21bfe6SPratyush Anand  * Test pattern generation for Link Layer Validation System Tests
5ce21bfe6SPratyush Anand  *
6ce21bfe6SPratyush Anand  * Copyright (C) 2014 ST Microelectronics
7e34caddeSPratyush Anand  * Pratyush Anand <pratyush.anand@gmail.com>
8ce21bfe6SPratyush Anand  *
9ce21bfe6SPratyush Anand  * This file is licensed under the terms of the GNU General Public
10ce21bfe6SPratyush Anand  * License version 2. This program is licensed "as is" without any
11ce21bfe6SPratyush Anand  * warranty of any kind, whether express or implied.
12ce21bfe6SPratyush Anand  */
13ce21bfe6SPratyush Anand 
14ce21bfe6SPratyush Anand #include <linux/init.h>
15ce21bfe6SPratyush Anand #include <linux/kernel.h>
16ce21bfe6SPratyush Anand #include <linux/module.h>
17ce21bfe6SPratyush Anand #include <linux/platform_device.h>
18ce21bfe6SPratyush Anand #include <linux/slab.h>
19ce21bfe6SPratyush Anand #include <linux/usb.h>
20ce21bfe6SPratyush Anand #include <linux/usb/ch11.h>
21ce21bfe6SPratyush Anand #include <linux/usb/hcd.h>
22ce21bfe6SPratyush Anand #include <linux/usb/phy.h>
23ce21bfe6SPratyush Anand 
24ce21bfe6SPratyush Anand struct lvs_rh {
25ce21bfe6SPratyush Anand 	/* root hub interface */
26ce21bfe6SPratyush Anand 	struct usb_interface *intf;
27ce21bfe6SPratyush Anand 	/* if lvs device connected */
28ce21bfe6SPratyush Anand 	bool present;
29ce21bfe6SPratyush Anand 	/* port no at which lvs device is present */
30ce21bfe6SPratyush Anand 	int portnum;
31ce21bfe6SPratyush Anand 	/* urb buffer */
32ce21bfe6SPratyush Anand 	u8 buffer[8];
33ce21bfe6SPratyush Anand 	/* class descriptor */
34ce21bfe6SPratyush Anand 	struct usb_hub_descriptor descriptor;
35ce21bfe6SPratyush Anand 	/* urb for polling interrupt pipe */
36ce21bfe6SPratyush Anand 	struct urb *urb;
37ce21bfe6SPratyush Anand 	/* LVH RH work */
38ce21bfe6SPratyush Anand 	struct work_struct	rh_work;
39ce21bfe6SPratyush Anand 	/* RH port status */
40ce21bfe6SPratyush Anand 	struct usb_port_status port_status;
41ce21bfe6SPratyush Anand };
42ce21bfe6SPratyush Anand 
43ce21bfe6SPratyush Anand static struct usb_device *create_lvs_device(struct usb_interface *intf)
44ce21bfe6SPratyush Anand {
45ce21bfe6SPratyush Anand 	struct usb_device *udev, *hdev;
46ce21bfe6SPratyush Anand 	struct usb_hcd *hcd;
47ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
48ce21bfe6SPratyush Anand 
49ce21bfe6SPratyush Anand 	if (!lvs->present) {
50ce21bfe6SPratyush Anand 		dev_err(&intf->dev, "No LVS device is present\n");
51ce21bfe6SPratyush Anand 		return NULL;
52ce21bfe6SPratyush Anand 	}
53ce21bfe6SPratyush Anand 
54ce21bfe6SPratyush Anand 	hdev = interface_to_usbdev(intf);
55ce21bfe6SPratyush Anand 	hcd = bus_to_hcd(hdev->bus);
56ce21bfe6SPratyush Anand 
57ce21bfe6SPratyush Anand 	udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum);
58ce21bfe6SPratyush Anand 	if (!udev) {
59ce21bfe6SPratyush Anand 		dev_err(&intf->dev, "Could not allocate lvs udev\n");
60ce21bfe6SPratyush Anand 		return NULL;
61ce21bfe6SPratyush Anand 	}
62ce21bfe6SPratyush Anand 	udev->speed = USB_SPEED_SUPER;
63ce21bfe6SPratyush Anand 	udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
64ce21bfe6SPratyush Anand 	usb_set_device_state(udev, USB_STATE_DEFAULT);
65ce21bfe6SPratyush Anand 
66ce21bfe6SPratyush Anand 	if (hcd->driver->enable_device) {
67ce21bfe6SPratyush Anand 		if (hcd->driver->enable_device(hcd, udev) < 0) {
68ce21bfe6SPratyush Anand 			dev_err(&intf->dev, "Failed to enable\n");
69ce21bfe6SPratyush Anand 			usb_put_dev(udev);
70ce21bfe6SPratyush Anand 			return NULL;
71ce21bfe6SPratyush Anand 		}
72ce21bfe6SPratyush Anand 	}
73ce21bfe6SPratyush Anand 
74ce21bfe6SPratyush Anand 	return udev;
75ce21bfe6SPratyush Anand }
76ce21bfe6SPratyush Anand 
77ce21bfe6SPratyush Anand static void destroy_lvs_device(struct usb_device *udev)
78ce21bfe6SPratyush Anand {
79ce21bfe6SPratyush Anand 	struct usb_device *hdev = udev->parent;
80ce21bfe6SPratyush Anand 	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
81ce21bfe6SPratyush Anand 
82ce21bfe6SPratyush Anand 	if (hcd->driver->free_dev)
83ce21bfe6SPratyush Anand 		hcd->driver->free_dev(hcd, udev);
84ce21bfe6SPratyush Anand 
85ce21bfe6SPratyush Anand 	usb_put_dev(udev);
86ce21bfe6SPratyush Anand }
87ce21bfe6SPratyush Anand 
88ce21bfe6SPratyush Anand static int lvs_rh_clear_port_feature(struct usb_device *hdev,
89ce21bfe6SPratyush Anand 		int port1, int feature)
90ce21bfe6SPratyush Anand {
91ce21bfe6SPratyush Anand 	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
92ce21bfe6SPratyush Anand 		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
93ce21bfe6SPratyush Anand 		NULL, 0, 1000);
94ce21bfe6SPratyush Anand }
95ce21bfe6SPratyush Anand 
96ce21bfe6SPratyush Anand static int lvs_rh_set_port_feature(struct usb_device *hdev,
97ce21bfe6SPratyush Anand 		int port1, int feature)
98ce21bfe6SPratyush Anand {
99ce21bfe6SPratyush Anand 	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
100ce21bfe6SPratyush Anand 		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
101ce21bfe6SPratyush Anand 		NULL, 0, 1000);
102ce21bfe6SPratyush Anand }
103ce21bfe6SPratyush Anand 
104ce21bfe6SPratyush Anand static ssize_t u3_entry_store(struct device *dev,
105ce21bfe6SPratyush Anand 		struct device_attribute *attr, const char *buf, size_t count)
106ce21bfe6SPratyush Anand {
107ce21bfe6SPratyush Anand 	struct usb_interface *intf = to_usb_interface(dev);
108ce21bfe6SPratyush Anand 	struct usb_device *hdev = interface_to_usbdev(intf);
109ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
110ce21bfe6SPratyush Anand 	struct usb_device *udev;
111ce21bfe6SPratyush Anand 	int ret;
112ce21bfe6SPratyush Anand 
113ce21bfe6SPratyush Anand 	udev = create_lvs_device(intf);
114ce21bfe6SPratyush Anand 	if (!udev) {
115ce21bfe6SPratyush Anand 		dev_err(dev, "failed to create lvs device\n");
116ce21bfe6SPratyush Anand 		return -ENOMEM;
117ce21bfe6SPratyush Anand 	}
118ce21bfe6SPratyush Anand 
119ce21bfe6SPratyush Anand 	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
120ce21bfe6SPratyush Anand 			USB_PORT_FEAT_SUSPEND);
121ce21bfe6SPratyush Anand 	if (ret < 0)
122ce21bfe6SPratyush Anand 		dev_err(dev, "can't issue U3 entry %d\n", ret);
123ce21bfe6SPratyush Anand 
124ce21bfe6SPratyush Anand 	destroy_lvs_device(udev);
125ce21bfe6SPratyush Anand 
126ce21bfe6SPratyush Anand 	if (ret < 0)
127ce21bfe6SPratyush Anand 		return ret;
128ce21bfe6SPratyush Anand 
129ce21bfe6SPratyush Anand 	return count;
130ce21bfe6SPratyush Anand }
131ce21bfe6SPratyush Anand static DEVICE_ATTR_WO(u3_entry);
132ce21bfe6SPratyush Anand 
133ce21bfe6SPratyush Anand static ssize_t u3_exit_store(struct device *dev,
134ce21bfe6SPratyush Anand 		struct device_attribute *attr, const char *buf, size_t count)
135ce21bfe6SPratyush Anand {
136ce21bfe6SPratyush Anand 	struct usb_interface *intf = to_usb_interface(dev);
137ce21bfe6SPratyush Anand 	struct usb_device *hdev = interface_to_usbdev(intf);
138ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
139ce21bfe6SPratyush Anand 	struct usb_device *udev;
140ce21bfe6SPratyush Anand 	int ret;
141ce21bfe6SPratyush Anand 
142ce21bfe6SPratyush Anand 	udev = create_lvs_device(intf);
143ce21bfe6SPratyush Anand 	if (!udev) {
144ce21bfe6SPratyush Anand 		dev_err(dev, "failed to create lvs device\n");
145ce21bfe6SPratyush Anand 		return -ENOMEM;
146ce21bfe6SPratyush Anand 	}
147ce21bfe6SPratyush Anand 
148ce21bfe6SPratyush Anand 	ret = lvs_rh_clear_port_feature(hdev, lvs->portnum,
149ce21bfe6SPratyush Anand 			USB_PORT_FEAT_SUSPEND);
150ce21bfe6SPratyush Anand 	if (ret < 0)
151ce21bfe6SPratyush Anand 		dev_err(dev, "can't issue U3 exit %d\n", ret);
152ce21bfe6SPratyush Anand 
153ce21bfe6SPratyush Anand 	destroy_lvs_device(udev);
154ce21bfe6SPratyush Anand 
155ce21bfe6SPratyush Anand 	if (ret < 0)
156ce21bfe6SPratyush Anand 		return ret;
157ce21bfe6SPratyush Anand 
158ce21bfe6SPratyush Anand 	return count;
159ce21bfe6SPratyush Anand }
160ce21bfe6SPratyush Anand static DEVICE_ATTR_WO(u3_exit);
161ce21bfe6SPratyush Anand 
162ce21bfe6SPratyush Anand static ssize_t hot_reset_store(struct device *dev,
163ce21bfe6SPratyush Anand 		struct device_attribute *attr, const char *buf, size_t count)
164ce21bfe6SPratyush Anand {
165ce21bfe6SPratyush Anand 	struct usb_interface *intf = to_usb_interface(dev);
166ce21bfe6SPratyush Anand 	struct usb_device *hdev = interface_to_usbdev(intf);
167ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
168ce21bfe6SPratyush Anand 	int ret;
169ce21bfe6SPratyush Anand 
170ce21bfe6SPratyush Anand 	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
171ce21bfe6SPratyush Anand 			USB_PORT_FEAT_RESET);
172ce21bfe6SPratyush Anand 	if (ret < 0) {
173ce21bfe6SPratyush Anand 		dev_err(dev, "can't issue hot reset %d\n", ret);
174ce21bfe6SPratyush Anand 		return ret;
175ce21bfe6SPratyush Anand 	}
176ce21bfe6SPratyush Anand 
177ce21bfe6SPratyush Anand 	return count;
178ce21bfe6SPratyush Anand }
179ce21bfe6SPratyush Anand static DEVICE_ATTR_WO(hot_reset);
180ce21bfe6SPratyush Anand 
181ce21bfe6SPratyush Anand static ssize_t u2_timeout_store(struct device *dev,
182ce21bfe6SPratyush Anand 		struct device_attribute *attr, const char *buf, size_t count)
183ce21bfe6SPratyush Anand {
184ce21bfe6SPratyush Anand 	struct usb_interface *intf = to_usb_interface(dev);
185ce21bfe6SPratyush Anand 	struct usb_device *hdev = interface_to_usbdev(intf);
186ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
187ce21bfe6SPratyush Anand 	unsigned long val;
188ce21bfe6SPratyush Anand 	int ret;
189ce21bfe6SPratyush Anand 
190ce21bfe6SPratyush Anand 	ret = kstrtoul(buf, 10, &val);
191ce21bfe6SPratyush Anand 	if (ret < 0) {
192ce21bfe6SPratyush Anand 		dev_err(dev, "couldn't parse string %d\n", ret);
193ce21bfe6SPratyush Anand 		return ret;
194ce21bfe6SPratyush Anand 	}
195ce21bfe6SPratyush Anand 
196ce21bfe6SPratyush Anand 	if (val < 0 || val > 127)
197ce21bfe6SPratyush Anand 		return -EINVAL;
198ce21bfe6SPratyush Anand 
199ce21bfe6SPratyush Anand 	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
200ce21bfe6SPratyush Anand 			USB_PORT_FEAT_U2_TIMEOUT);
201ce21bfe6SPratyush Anand 	if (ret < 0) {
202ce21bfe6SPratyush Anand 		dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val);
203ce21bfe6SPratyush Anand 		return ret;
204ce21bfe6SPratyush Anand 	}
205ce21bfe6SPratyush Anand 
206ce21bfe6SPratyush Anand 	return count;
207ce21bfe6SPratyush Anand }
208ce21bfe6SPratyush Anand static DEVICE_ATTR_WO(u2_timeout);
209ce21bfe6SPratyush Anand 
210ce21bfe6SPratyush Anand static ssize_t u1_timeout_store(struct device *dev,
211ce21bfe6SPratyush Anand 		struct device_attribute *attr, const char *buf, size_t count)
212ce21bfe6SPratyush Anand {
213ce21bfe6SPratyush Anand 	struct usb_interface *intf = to_usb_interface(dev);
214ce21bfe6SPratyush Anand 	struct usb_device *hdev = interface_to_usbdev(intf);
215ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
216ce21bfe6SPratyush Anand 	unsigned long val;
217ce21bfe6SPratyush Anand 	int ret;
218ce21bfe6SPratyush Anand 
219ce21bfe6SPratyush Anand 	ret = kstrtoul(buf, 10, &val);
220ce21bfe6SPratyush Anand 	if (ret < 0) {
221ce21bfe6SPratyush Anand 		dev_err(dev, "couldn't parse string %d\n", ret);
222ce21bfe6SPratyush Anand 		return ret;
223ce21bfe6SPratyush Anand 	}
224ce21bfe6SPratyush Anand 
225b7963dacSGustavo A. R. Silva 	if (val > 127)
226ce21bfe6SPratyush Anand 		return -EINVAL;
227ce21bfe6SPratyush Anand 
228ce21bfe6SPratyush Anand 	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
229ce21bfe6SPratyush Anand 			USB_PORT_FEAT_U1_TIMEOUT);
230ce21bfe6SPratyush Anand 	if (ret < 0) {
231ce21bfe6SPratyush Anand 		dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val);
232ce21bfe6SPratyush Anand 		return ret;
233ce21bfe6SPratyush Anand 	}
234ce21bfe6SPratyush Anand 
235ce21bfe6SPratyush Anand 	return count;
236ce21bfe6SPratyush Anand }
237ce21bfe6SPratyush Anand static DEVICE_ATTR_WO(u1_timeout);
238ce21bfe6SPratyush Anand 
239ce21bfe6SPratyush Anand static ssize_t get_dev_desc_store(struct device *dev,
240ce21bfe6SPratyush Anand 		struct device_attribute *attr, const char *buf, size_t count)
241ce21bfe6SPratyush Anand {
242ce21bfe6SPratyush Anand 	struct usb_interface *intf = to_usb_interface(dev);
243ce21bfe6SPratyush Anand 	struct usb_device *udev;
244ce21bfe6SPratyush Anand 	struct usb_device_descriptor *descriptor;
245ce21bfe6SPratyush Anand 	int ret;
246ce21bfe6SPratyush Anand 
247ce21bfe6SPratyush Anand 	descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
2485c47fd61SWolfram Sang 	if (!descriptor)
249ce21bfe6SPratyush Anand 		return -ENOMEM;
250ce21bfe6SPratyush Anand 
251ce21bfe6SPratyush Anand 	udev = create_lvs_device(intf);
252ce21bfe6SPratyush Anand 	if (!udev) {
253ce21bfe6SPratyush Anand 		dev_err(dev, "failed to create lvs device\n");
254ce21bfe6SPratyush Anand 		ret = -ENOMEM;
255ce21bfe6SPratyush Anand 		goto free_desc;
256ce21bfe6SPratyush Anand 	}
257ce21bfe6SPratyush Anand 
258ce21bfe6SPratyush Anand 	ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN,
259ce21bfe6SPratyush Anand 			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8,
260ce21bfe6SPratyush Anand 			0, descriptor, sizeof(*descriptor),
261ce21bfe6SPratyush Anand 			USB_CTRL_GET_TIMEOUT);
262ce21bfe6SPratyush Anand 	if (ret < 0)
263ce21bfe6SPratyush Anand 		dev_err(dev, "can't read device descriptor %d\n", ret);
264ce21bfe6SPratyush Anand 
265ce21bfe6SPratyush Anand 	destroy_lvs_device(udev);
266ce21bfe6SPratyush Anand 
267ce21bfe6SPratyush Anand free_desc:
268ce21bfe6SPratyush Anand 	kfree(descriptor);
269ce21bfe6SPratyush Anand 
270ce21bfe6SPratyush Anand 	if (ret < 0)
271ce21bfe6SPratyush Anand 		return ret;
272ce21bfe6SPratyush Anand 
273ce21bfe6SPratyush Anand 	return count;
274ce21bfe6SPratyush Anand }
275ce21bfe6SPratyush Anand static DEVICE_ATTR_WO(get_dev_desc);
276ce21bfe6SPratyush Anand 
277ce21bfe6SPratyush Anand static struct attribute *lvs_attributes[] = {
278ce21bfe6SPratyush Anand 	&dev_attr_get_dev_desc.attr,
279ce21bfe6SPratyush Anand 	&dev_attr_u1_timeout.attr,
280ce21bfe6SPratyush Anand 	&dev_attr_u2_timeout.attr,
281ce21bfe6SPratyush Anand 	&dev_attr_hot_reset.attr,
282ce21bfe6SPratyush Anand 	&dev_attr_u3_entry.attr,
283ce21bfe6SPratyush Anand 	&dev_attr_u3_exit.attr,
284ce21bfe6SPratyush Anand 	NULL
285ce21bfe6SPratyush Anand };
286ce21bfe6SPratyush Anand 
287ce21bfe6SPratyush Anand static const struct attribute_group lvs_attr_group = {
288ce21bfe6SPratyush Anand 	.attrs = lvs_attributes,
289ce21bfe6SPratyush Anand };
290ce21bfe6SPratyush Anand 
291ce21bfe6SPratyush Anand static void lvs_rh_work(struct work_struct *work)
292ce21bfe6SPratyush Anand {
293ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work);
294ce21bfe6SPratyush Anand 	struct usb_interface *intf = lvs->intf;
295ce21bfe6SPratyush Anand 	struct usb_device *hdev = interface_to_usbdev(intf);
296ce21bfe6SPratyush Anand 	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
297ce21bfe6SPratyush Anand 	struct usb_hub_descriptor *descriptor = &lvs->descriptor;
298ce21bfe6SPratyush Anand 	struct usb_port_status *port_status = &lvs->port_status;
299ce21bfe6SPratyush Anand 	int i, ret = 0;
300ce21bfe6SPratyush Anand 	u16 portchange;
301ce21bfe6SPratyush Anand 
302ce21bfe6SPratyush Anand 	/* Examine each root port */
303ce21bfe6SPratyush Anand 	for (i = 1; i <= descriptor->bNbrPorts; i++) {
304ce21bfe6SPratyush Anand 		ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
305ce21bfe6SPratyush Anand 			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i,
306ce21bfe6SPratyush Anand 			port_status, sizeof(*port_status), 1000);
307ce21bfe6SPratyush Anand 		if (ret < 4)
308ce21bfe6SPratyush Anand 			continue;
309ce21bfe6SPratyush Anand 
310b1bd3f1aSPratyush Anand 		portchange = le16_to_cpu(port_status->wPortChange);
311ce21bfe6SPratyush Anand 
312ce21bfe6SPratyush Anand 		if (portchange & USB_PORT_STAT_C_LINK_STATE)
313ce21bfe6SPratyush Anand 			lvs_rh_clear_port_feature(hdev, i,
314ce21bfe6SPratyush Anand 					USB_PORT_FEAT_C_PORT_LINK_STATE);
315ce21bfe6SPratyush Anand 		if (portchange & USB_PORT_STAT_C_ENABLE)
316ce21bfe6SPratyush Anand 			lvs_rh_clear_port_feature(hdev, i,
317ce21bfe6SPratyush Anand 					USB_PORT_FEAT_C_ENABLE);
318ce21bfe6SPratyush Anand 		if (portchange & USB_PORT_STAT_C_RESET)
319ce21bfe6SPratyush Anand 			lvs_rh_clear_port_feature(hdev, i,
320ce21bfe6SPratyush Anand 					USB_PORT_FEAT_C_RESET);
321ce21bfe6SPratyush Anand 		if (portchange & USB_PORT_STAT_C_BH_RESET)
322ce21bfe6SPratyush Anand 			lvs_rh_clear_port_feature(hdev, i,
323ce21bfe6SPratyush Anand 					USB_PORT_FEAT_C_BH_PORT_RESET);
324ce21bfe6SPratyush Anand 		if (portchange & USB_PORT_STAT_C_CONNECTION) {
325ce21bfe6SPratyush Anand 			lvs_rh_clear_port_feature(hdev, i,
326ce21bfe6SPratyush Anand 					USB_PORT_FEAT_C_CONNECTION);
327ce21bfe6SPratyush Anand 
328b1bd3f1aSPratyush Anand 			if (le16_to_cpu(port_status->wPortStatus) &
329ce21bfe6SPratyush Anand 					USB_PORT_STAT_CONNECTION) {
330ce21bfe6SPratyush Anand 				lvs->present = true;
331ce21bfe6SPratyush Anand 				lvs->portnum = i;
3323d46e73dSAntoine Tenart 				if (hcd->usb_phy)
3333d46e73dSAntoine Tenart 					usb_phy_notify_connect(hcd->usb_phy,
334ce21bfe6SPratyush Anand 							USB_SPEED_SUPER);
335ce21bfe6SPratyush Anand 			} else {
336ce21bfe6SPratyush Anand 				lvs->present = false;
3373d46e73dSAntoine Tenart 				if (hcd->usb_phy)
3383d46e73dSAntoine Tenart 					usb_phy_notify_disconnect(hcd->usb_phy,
339ce21bfe6SPratyush Anand 							USB_SPEED_SUPER);
340ce21bfe6SPratyush Anand 			}
341ce21bfe6SPratyush Anand 			break;
342ce21bfe6SPratyush Anand 		}
343ce21bfe6SPratyush Anand 	}
344ce21bfe6SPratyush Anand 
345ce21bfe6SPratyush Anand 	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
346ce21bfe6SPratyush Anand 	if (ret != 0 && ret != -ENODEV && ret != -EPERM)
347ce21bfe6SPratyush Anand 		dev_err(&intf->dev, "urb resubmit error %d\n", ret);
348ce21bfe6SPratyush Anand }
349ce21bfe6SPratyush Anand 
350ce21bfe6SPratyush Anand static void lvs_rh_irq(struct urb *urb)
351ce21bfe6SPratyush Anand {
352ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = urb->context;
353ce21bfe6SPratyush Anand 
354bd783108SBhaktipriya Shridhar 	schedule_work(&lvs->rh_work);
355ce21bfe6SPratyush Anand }
356ce21bfe6SPratyush Anand 
357ce21bfe6SPratyush Anand static int lvs_rh_probe(struct usb_interface *intf,
358ce21bfe6SPratyush Anand 		const struct usb_device_id *id)
359ce21bfe6SPratyush Anand {
360ce21bfe6SPratyush Anand 	struct usb_device *hdev;
361ce21bfe6SPratyush Anand 	struct usb_host_interface *desc;
362ce21bfe6SPratyush Anand 	struct usb_endpoint_descriptor *endpoint;
363ce21bfe6SPratyush Anand 	struct lvs_rh *lvs;
364ce21bfe6SPratyush Anand 	unsigned int pipe;
365ce21bfe6SPratyush Anand 	int ret, maxp;
366ce21bfe6SPratyush Anand 
367ce21bfe6SPratyush Anand 	hdev = interface_to_usbdev(intf);
368ce21bfe6SPratyush Anand 	desc = intf->cur_altsetting;
369ce21bfe6SPratyush Anand 	endpoint = &desc->endpoint[0].desc;
370ce21bfe6SPratyush Anand 
371ce21bfe6SPratyush Anand 	/* valid only for SS root hub */
372ce21bfe6SPratyush Anand 	if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) {
373ce21bfe6SPratyush Anand 		dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n");
374ce21bfe6SPratyush Anand 		return -EINVAL;
375ce21bfe6SPratyush Anand 	}
376ce21bfe6SPratyush Anand 
377ce21bfe6SPratyush Anand 	lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL);
378ce21bfe6SPratyush Anand 	if (!lvs)
379ce21bfe6SPratyush Anand 		return -ENOMEM;
380ce21bfe6SPratyush Anand 
381ce21bfe6SPratyush Anand 	lvs->intf = intf;
382ce21bfe6SPratyush Anand 	usb_set_intfdata(intf, lvs);
383ce21bfe6SPratyush Anand 
384ce21bfe6SPratyush Anand 	/* how many number of ports this root hub has */
385ce21bfe6SPratyush Anand 	ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
386ce21bfe6SPratyush Anand 			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
387ce21bfe6SPratyush Anand 			USB_DT_SS_HUB << 8, 0, &lvs->descriptor,
388ce21bfe6SPratyush Anand 			USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
389ce21bfe6SPratyush Anand 	if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
390ce21bfe6SPratyush Anand 		dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
391ce21bfe6SPratyush Anand 		return ret;
392ce21bfe6SPratyush Anand 	}
393ce21bfe6SPratyush Anand 
394ce21bfe6SPratyush Anand 	/* submit urb to poll interrupt endpoint */
395ce21bfe6SPratyush Anand 	lvs->urb = usb_alloc_urb(0, GFP_KERNEL);
396da4e20ffSWolfram Sang 	if (!lvs->urb)
397ce21bfe6SPratyush Anand 		return -ENOMEM;
398ce21bfe6SPratyush Anand 
399ce21bfe6SPratyush Anand 	INIT_WORK(&lvs->rh_work, lvs_rh_work);
400ce21bfe6SPratyush Anand 
401ce21bfe6SPratyush Anand 	ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group);
402ce21bfe6SPratyush Anand 	if (ret < 0) {
403ce21bfe6SPratyush Anand 		dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret);
404bd783108SBhaktipriya Shridhar 		goto free_urb;
405ce21bfe6SPratyush Anand 	}
406ce21bfe6SPratyush Anand 
407ce21bfe6SPratyush Anand 	pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
408ce21bfe6SPratyush Anand 	maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
409ce21bfe6SPratyush Anand 	usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp,
410ce21bfe6SPratyush Anand 			lvs_rh_irq, lvs, endpoint->bInterval);
411ce21bfe6SPratyush Anand 
412ce21bfe6SPratyush Anand 	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
413ce21bfe6SPratyush Anand 	if (ret < 0) {
414ce21bfe6SPratyush Anand 		dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret);
415ce21bfe6SPratyush Anand 		goto sysfs_remove;
416ce21bfe6SPratyush Anand 	}
417ce21bfe6SPratyush Anand 
418ce21bfe6SPratyush Anand 	return ret;
419ce21bfe6SPratyush Anand 
420ce21bfe6SPratyush Anand sysfs_remove:
421ce21bfe6SPratyush Anand 	sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
422ce21bfe6SPratyush Anand free_urb:
423ce21bfe6SPratyush Anand 	usb_free_urb(lvs->urb);
424ce21bfe6SPratyush Anand 	return ret;
425ce21bfe6SPratyush Anand }
426ce21bfe6SPratyush Anand 
427ce21bfe6SPratyush Anand static void lvs_rh_disconnect(struct usb_interface *intf)
428ce21bfe6SPratyush Anand {
429ce21bfe6SPratyush Anand 	struct lvs_rh *lvs = usb_get_intfdata(intf);
430ce21bfe6SPratyush Anand 
431ce21bfe6SPratyush Anand 	sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
432*c4ba329cSOliver Neukum 	usb_poison_urb(lvs->urb); /* used in scheduled work */
433bd783108SBhaktipriya Shridhar 	flush_work(&lvs->rh_work);
434ce21bfe6SPratyush Anand 	usb_free_urb(lvs->urb);
435ce21bfe6SPratyush Anand }
436ce21bfe6SPratyush Anand 
437ce21bfe6SPratyush Anand static struct usb_driver lvs_driver = {
438ce21bfe6SPratyush Anand 	.name =		"lvs",
439ce21bfe6SPratyush Anand 	.probe =	lvs_rh_probe,
440ce21bfe6SPratyush Anand 	.disconnect =	lvs_rh_disconnect,
441ce21bfe6SPratyush Anand };
442ce21bfe6SPratyush Anand 
443ce21bfe6SPratyush Anand module_usb_driver(lvs_driver);
444ce21bfe6SPratyush Anand 
445ce21bfe6SPratyush Anand MODULE_DESCRIPTION("Link Layer Validation System Driver");
446ce21bfe6SPratyush Anand MODULE_LICENSE("GPL");
447