162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/usb/misc/lvstest.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Test pattern generation for Link Layer Validation System Tests
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2014 ST Microelectronics
862306a36Sopenharmony_ci * Pratyush Anand <pratyush.anand@gmail.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/usb.h>
1762306a36Sopenharmony_ci#include <linux/usb/ch11.h>
1862306a36Sopenharmony_ci#include <linux/usb/hcd.h>
1962306a36Sopenharmony_ci#include <linux/usb/phy.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct lvs_rh {
2262306a36Sopenharmony_ci	/* root hub interface */
2362306a36Sopenharmony_ci	struct usb_interface *intf;
2462306a36Sopenharmony_ci	/* if lvs device connected */
2562306a36Sopenharmony_ci	bool present;
2662306a36Sopenharmony_ci	/* port no at which lvs device is present */
2762306a36Sopenharmony_ci	int portnum;
2862306a36Sopenharmony_ci	/* urb buffer */
2962306a36Sopenharmony_ci	u8 buffer[8];
3062306a36Sopenharmony_ci	/* class descriptor */
3162306a36Sopenharmony_ci	struct usb_hub_descriptor descriptor;
3262306a36Sopenharmony_ci	/* urb for polling interrupt pipe */
3362306a36Sopenharmony_ci	struct urb *urb;
3462306a36Sopenharmony_ci	/* LVH RH work */
3562306a36Sopenharmony_ci	struct work_struct	rh_work;
3662306a36Sopenharmony_ci	/* RH port status */
3762306a36Sopenharmony_ci	struct usb_port_status port_status;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic struct usb_device *create_lvs_device(struct usb_interface *intf)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct usb_device *udev, *hdev;
4362306a36Sopenharmony_ci	struct usb_hcd *hcd;
4462306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (!lvs->present) {
4762306a36Sopenharmony_ci		dev_err(&intf->dev, "No LVS device is present\n");
4862306a36Sopenharmony_ci		return NULL;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	hdev = interface_to_usbdev(intf);
5262306a36Sopenharmony_ci	hcd = bus_to_hcd(hdev->bus);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum);
5562306a36Sopenharmony_ci	if (!udev) {
5662306a36Sopenharmony_ci		dev_err(&intf->dev, "Could not allocate lvs udev\n");
5762306a36Sopenharmony_ci		return NULL;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	udev->speed = USB_SPEED_SUPER;
6062306a36Sopenharmony_ci	udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
6162306a36Sopenharmony_ci	usb_set_device_state(udev, USB_STATE_DEFAULT);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (hcd->driver->enable_device) {
6462306a36Sopenharmony_ci		if (hcd->driver->enable_device(hcd, udev) < 0) {
6562306a36Sopenharmony_ci			dev_err(&intf->dev, "Failed to enable\n");
6662306a36Sopenharmony_ci			usb_put_dev(udev);
6762306a36Sopenharmony_ci			return NULL;
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return udev;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void destroy_lvs_device(struct usb_device *udev)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct usb_device *hdev = udev->parent;
7762306a36Sopenharmony_ci	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (hcd->driver->free_dev)
8062306a36Sopenharmony_ci		hcd->driver->free_dev(hcd, udev);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	usb_put_dev(udev);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int lvs_rh_clear_port_feature(struct usb_device *hdev,
8662306a36Sopenharmony_ci		int port1, int feature)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
8962306a36Sopenharmony_ci		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
9062306a36Sopenharmony_ci		NULL, 0, 1000);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int lvs_rh_set_port_feature(struct usb_device *hdev,
9462306a36Sopenharmony_ci		int port1, int feature)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
9762306a36Sopenharmony_ci		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
9862306a36Sopenharmony_ci		NULL, 0, 1000);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic ssize_t u3_entry_store(struct device *dev,
10262306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
10562306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
10662306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
10762306a36Sopenharmony_ci	struct usb_device *udev;
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	udev = create_lvs_device(intf);
11162306a36Sopenharmony_ci	if (!udev) {
11262306a36Sopenharmony_ci		dev_err(dev, "failed to create lvs device\n");
11362306a36Sopenharmony_ci		return -ENOMEM;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
11762306a36Sopenharmony_ci			USB_PORT_FEAT_SUSPEND);
11862306a36Sopenharmony_ci	if (ret < 0)
11962306a36Sopenharmony_ci		dev_err(dev, "can't issue U3 entry %d\n", ret);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	destroy_lvs_device(udev);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (ret < 0)
12462306a36Sopenharmony_ci		return ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return count;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_cistatic DEVICE_ATTR_WO(u3_entry);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic ssize_t u3_exit_store(struct device *dev,
13162306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
13462306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
13562306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
13662306a36Sopenharmony_ci	struct usb_device *udev;
13762306a36Sopenharmony_ci	int ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	udev = create_lvs_device(intf);
14062306a36Sopenharmony_ci	if (!udev) {
14162306a36Sopenharmony_ci		dev_err(dev, "failed to create lvs device\n");
14262306a36Sopenharmony_ci		return -ENOMEM;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	ret = lvs_rh_clear_port_feature(hdev, lvs->portnum,
14662306a36Sopenharmony_ci			USB_PORT_FEAT_SUSPEND);
14762306a36Sopenharmony_ci	if (ret < 0)
14862306a36Sopenharmony_ci		dev_err(dev, "can't issue U3 exit %d\n", ret);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	destroy_lvs_device(udev);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (ret < 0)
15362306a36Sopenharmony_ci		return ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return count;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(u3_exit);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic ssize_t hot_reset_store(struct device *dev,
16062306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
16362306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
16462306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
16562306a36Sopenharmony_ci	int ret;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
16862306a36Sopenharmony_ci			USB_PORT_FEAT_RESET);
16962306a36Sopenharmony_ci	if (ret < 0) {
17062306a36Sopenharmony_ci		dev_err(dev, "can't issue hot reset %d\n", ret);
17162306a36Sopenharmony_ci		return ret;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return count;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_cistatic DEVICE_ATTR_WO(hot_reset);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic ssize_t warm_reset_store(struct device *dev,
17962306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
18262306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
18362306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
18462306a36Sopenharmony_ci	int ret;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
18762306a36Sopenharmony_ci			USB_PORT_FEAT_BH_PORT_RESET);
18862306a36Sopenharmony_ci	if (ret < 0) {
18962306a36Sopenharmony_ci		dev_err(dev, "can't issue warm reset %d\n", ret);
19062306a36Sopenharmony_ci		return ret;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return count;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_cistatic DEVICE_ATTR_WO(warm_reset);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic ssize_t u2_timeout_store(struct device *dev,
19862306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
20162306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
20262306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
20362306a36Sopenharmony_ci	unsigned long val;
20462306a36Sopenharmony_ci	int ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = kstrtoul(buf, 10, &val);
20762306a36Sopenharmony_ci	if (ret < 0) {
20862306a36Sopenharmony_ci		dev_err(dev, "couldn't parse string %d\n", ret);
20962306a36Sopenharmony_ci		return ret;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (val > 127)
21362306a36Sopenharmony_ci		return -EINVAL;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
21662306a36Sopenharmony_ci			USB_PORT_FEAT_U2_TIMEOUT);
21762306a36Sopenharmony_ci	if (ret < 0) {
21862306a36Sopenharmony_ci		dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val);
21962306a36Sopenharmony_ci		return ret;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return count;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(u2_timeout);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic ssize_t u1_timeout_store(struct device *dev,
22762306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
23062306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
23162306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
23262306a36Sopenharmony_ci	unsigned long val;
23362306a36Sopenharmony_ci	int ret;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ret = kstrtoul(buf, 10, &val);
23662306a36Sopenharmony_ci	if (ret < 0) {
23762306a36Sopenharmony_ci		dev_err(dev, "couldn't parse string %d\n", ret);
23862306a36Sopenharmony_ci		return ret;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (val > 127)
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
24562306a36Sopenharmony_ci			USB_PORT_FEAT_U1_TIMEOUT);
24662306a36Sopenharmony_ci	if (ret < 0) {
24762306a36Sopenharmony_ci		dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val);
24862306a36Sopenharmony_ci		return ret;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return count;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_cistatic DEVICE_ATTR_WO(u1_timeout);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic ssize_t get_dev_desc_store(struct device *dev,
25662306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
25962306a36Sopenharmony_ci	struct usb_device *udev;
26062306a36Sopenharmony_ci	struct usb_device_descriptor *descriptor;
26162306a36Sopenharmony_ci	int ret;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
26462306a36Sopenharmony_ci	if (!descriptor)
26562306a36Sopenharmony_ci		return -ENOMEM;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	udev = create_lvs_device(intf);
26862306a36Sopenharmony_ci	if (!udev) {
26962306a36Sopenharmony_ci		dev_err(dev, "failed to create lvs device\n");
27062306a36Sopenharmony_ci		ret = -ENOMEM;
27162306a36Sopenharmony_ci		goto free_desc;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN,
27562306a36Sopenharmony_ci			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8,
27662306a36Sopenharmony_ci			0, descriptor, sizeof(*descriptor),
27762306a36Sopenharmony_ci			USB_CTRL_GET_TIMEOUT);
27862306a36Sopenharmony_ci	if (ret < 0)
27962306a36Sopenharmony_ci		dev_err(dev, "can't read device descriptor %d\n", ret);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	destroy_lvs_device(udev);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cifree_desc:
28462306a36Sopenharmony_ci	kfree(descriptor);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (ret < 0)
28762306a36Sopenharmony_ci		return ret;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return count;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(get_dev_desc);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic ssize_t enable_compliance_store(struct device *dev,
29462306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
29762306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
29862306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
29962306a36Sopenharmony_ci	int ret;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev,
30262306a36Sopenharmony_ci			lvs->portnum | USB_SS_PORT_LS_COMP_MOD << 3,
30362306a36Sopenharmony_ci			USB_PORT_FEAT_LINK_STATE);
30462306a36Sopenharmony_ci	if (ret < 0) {
30562306a36Sopenharmony_ci		dev_err(dev, "can't enable compliance mode %d\n", ret);
30662306a36Sopenharmony_ci		return ret;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return count;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(enable_compliance);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic struct attribute *lvs_attrs[] = {
31462306a36Sopenharmony_ci	&dev_attr_get_dev_desc.attr,
31562306a36Sopenharmony_ci	&dev_attr_u1_timeout.attr,
31662306a36Sopenharmony_ci	&dev_attr_u2_timeout.attr,
31762306a36Sopenharmony_ci	&dev_attr_hot_reset.attr,
31862306a36Sopenharmony_ci	&dev_attr_warm_reset.attr,
31962306a36Sopenharmony_ci	&dev_attr_u3_entry.attr,
32062306a36Sopenharmony_ci	&dev_attr_u3_exit.attr,
32162306a36Sopenharmony_ci	&dev_attr_enable_compliance.attr,
32262306a36Sopenharmony_ci	NULL
32362306a36Sopenharmony_ci};
32462306a36Sopenharmony_ciATTRIBUTE_GROUPS(lvs);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void lvs_rh_work(struct work_struct *work)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work);
32962306a36Sopenharmony_ci	struct usb_interface *intf = lvs->intf;
33062306a36Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
33162306a36Sopenharmony_ci	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
33262306a36Sopenharmony_ci	struct usb_hub_descriptor *descriptor = &lvs->descriptor;
33362306a36Sopenharmony_ci	struct usb_port_status *port_status = &lvs->port_status;
33462306a36Sopenharmony_ci	int i, ret = 0;
33562306a36Sopenharmony_ci	u16 portchange;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Examine each root port */
33862306a36Sopenharmony_ci	for (i = 1; i <= descriptor->bNbrPorts; i++) {
33962306a36Sopenharmony_ci		ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
34062306a36Sopenharmony_ci			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i,
34162306a36Sopenharmony_ci			port_status, sizeof(*port_status), 1000);
34262306a36Sopenharmony_ci		if (ret < 4)
34362306a36Sopenharmony_ci			continue;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		portchange = le16_to_cpu(port_status->wPortChange);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_LINK_STATE)
34862306a36Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
34962306a36Sopenharmony_ci					USB_PORT_FEAT_C_PORT_LINK_STATE);
35062306a36Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_ENABLE)
35162306a36Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
35262306a36Sopenharmony_ci					USB_PORT_FEAT_C_ENABLE);
35362306a36Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_RESET)
35462306a36Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
35562306a36Sopenharmony_ci					USB_PORT_FEAT_C_RESET);
35662306a36Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_BH_RESET)
35762306a36Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
35862306a36Sopenharmony_ci					USB_PORT_FEAT_C_BH_PORT_RESET);
35962306a36Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_CONNECTION) {
36062306a36Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
36162306a36Sopenharmony_ci					USB_PORT_FEAT_C_CONNECTION);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci			if (le16_to_cpu(port_status->wPortStatus) &
36462306a36Sopenharmony_ci					USB_PORT_STAT_CONNECTION) {
36562306a36Sopenharmony_ci				lvs->present = true;
36662306a36Sopenharmony_ci				lvs->portnum = i;
36762306a36Sopenharmony_ci				if (hcd->usb_phy)
36862306a36Sopenharmony_ci					usb_phy_notify_connect(hcd->usb_phy,
36962306a36Sopenharmony_ci							USB_SPEED_SUPER);
37062306a36Sopenharmony_ci			} else {
37162306a36Sopenharmony_ci				lvs->present = false;
37262306a36Sopenharmony_ci				if (hcd->usb_phy)
37362306a36Sopenharmony_ci					usb_phy_notify_disconnect(hcd->usb_phy,
37462306a36Sopenharmony_ci							USB_SPEED_SUPER);
37562306a36Sopenharmony_ci			}
37662306a36Sopenharmony_ci			break;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
38162306a36Sopenharmony_ci	if (ret != 0 && ret != -ENODEV && ret != -EPERM)
38262306a36Sopenharmony_ci		dev_err(&intf->dev, "urb resubmit error %d\n", ret);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic void lvs_rh_irq(struct urb *urb)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct lvs_rh *lvs = urb->context;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	schedule_work(&lvs->rh_work);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int lvs_rh_probe(struct usb_interface *intf,
39362306a36Sopenharmony_ci		const struct usb_device_id *id)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct usb_device *hdev;
39662306a36Sopenharmony_ci	struct usb_host_interface *desc;
39762306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
39862306a36Sopenharmony_ci	struct lvs_rh *lvs;
39962306a36Sopenharmony_ci	unsigned int pipe;
40062306a36Sopenharmony_ci	int ret, maxp;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	hdev = interface_to_usbdev(intf);
40362306a36Sopenharmony_ci	desc = intf->cur_altsetting;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = usb_find_int_in_endpoint(desc, &endpoint);
40662306a36Sopenharmony_ci	if (ret)
40762306a36Sopenharmony_ci		return ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* valid only for SS root hub */
41062306a36Sopenharmony_ci	if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) {
41162306a36Sopenharmony_ci		dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n");
41262306a36Sopenharmony_ci		return -EINVAL;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL);
41662306a36Sopenharmony_ci	if (!lvs)
41762306a36Sopenharmony_ci		return -ENOMEM;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	lvs->intf = intf;
42062306a36Sopenharmony_ci	usb_set_intfdata(intf, lvs);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* how many number of ports this root hub has */
42362306a36Sopenharmony_ci	ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
42462306a36Sopenharmony_ci			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
42562306a36Sopenharmony_ci			USB_DT_SS_HUB << 8, 0, &lvs->descriptor,
42662306a36Sopenharmony_ci			USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
42762306a36Sopenharmony_ci	if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
42862306a36Sopenharmony_ci		dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
42962306a36Sopenharmony_ci		return ret < 0 ? ret : -EINVAL;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* submit urb to poll interrupt endpoint */
43362306a36Sopenharmony_ci	lvs->urb = usb_alloc_urb(0, GFP_KERNEL);
43462306a36Sopenharmony_ci	if (!lvs->urb)
43562306a36Sopenharmony_ci		return -ENOMEM;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	INIT_WORK(&lvs->rh_work, lvs_rh_work);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
44062306a36Sopenharmony_ci	maxp = usb_maxpacket(hdev, pipe);
44162306a36Sopenharmony_ci	usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp,
44262306a36Sopenharmony_ci			lvs_rh_irq, lvs, endpoint->bInterval);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
44562306a36Sopenharmony_ci	if (ret < 0) {
44662306a36Sopenharmony_ci		dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret);
44762306a36Sopenharmony_ci		goto free_urb;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return ret;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cifree_urb:
45362306a36Sopenharmony_ci	usb_free_urb(lvs->urb);
45462306a36Sopenharmony_ci	return ret;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void lvs_rh_disconnect(struct usb_interface *intf)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	usb_poison_urb(lvs->urb); /* used in scheduled work */
46262306a36Sopenharmony_ci	flush_work(&lvs->rh_work);
46362306a36Sopenharmony_ci	usb_free_urb(lvs->urb);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic struct usb_driver lvs_driver = {
46762306a36Sopenharmony_ci	.name =		"lvs",
46862306a36Sopenharmony_ci	.probe =	lvs_rh_probe,
46962306a36Sopenharmony_ci	.disconnect =	lvs_rh_disconnect,
47062306a36Sopenharmony_ci	.dev_groups =	lvs_groups,
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cimodule_usb_driver(lvs_driver);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciMODULE_DESCRIPTION("Link Layer Validation System Driver");
47662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
477