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