18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/usb/misc/lvstest.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Test pattern generation for Link Layer Validation System Tests
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2014 ST Microelectronics
88c2ecf20Sopenharmony_ci * Pratyush Anand <pratyush.anand@gmail.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/usb.h>
178c2ecf20Sopenharmony_ci#include <linux/usb/ch11.h>
188c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h>
198c2ecf20Sopenharmony_ci#include <linux/usb/phy.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct lvs_rh {
228c2ecf20Sopenharmony_ci	/* root hub interface */
238c2ecf20Sopenharmony_ci	struct usb_interface *intf;
248c2ecf20Sopenharmony_ci	/* if lvs device connected */
258c2ecf20Sopenharmony_ci	bool present;
268c2ecf20Sopenharmony_ci	/* port no at which lvs device is present */
278c2ecf20Sopenharmony_ci	int portnum;
288c2ecf20Sopenharmony_ci	/* urb buffer */
298c2ecf20Sopenharmony_ci	u8 buffer[8];
308c2ecf20Sopenharmony_ci	/* class descriptor */
318c2ecf20Sopenharmony_ci	struct usb_hub_descriptor descriptor;
328c2ecf20Sopenharmony_ci	/* urb for polling interrupt pipe */
338c2ecf20Sopenharmony_ci	struct urb *urb;
348c2ecf20Sopenharmony_ci	/* LVH RH work */
358c2ecf20Sopenharmony_ci	struct work_struct	rh_work;
368c2ecf20Sopenharmony_ci	/* RH port status */
378c2ecf20Sopenharmony_ci	struct usb_port_status port_status;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic struct usb_device *create_lvs_device(struct usb_interface *intf)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct usb_device *udev, *hdev;
438c2ecf20Sopenharmony_ci	struct usb_hcd *hcd;
448c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!lvs->present) {
478c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "No LVS device is present\n");
488c2ecf20Sopenharmony_ci		return NULL;
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	hdev = interface_to_usbdev(intf);
528c2ecf20Sopenharmony_ci	hcd = bus_to_hcd(hdev->bus);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum);
558c2ecf20Sopenharmony_ci	if (!udev) {
568c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "Could not allocate lvs udev\n");
578c2ecf20Sopenharmony_ci		return NULL;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci	udev->speed = USB_SPEED_SUPER;
608c2ecf20Sopenharmony_ci	udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
618c2ecf20Sopenharmony_ci	usb_set_device_state(udev, USB_STATE_DEFAULT);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (hcd->driver->enable_device) {
648c2ecf20Sopenharmony_ci		if (hcd->driver->enable_device(hcd, udev) < 0) {
658c2ecf20Sopenharmony_ci			dev_err(&intf->dev, "Failed to enable\n");
668c2ecf20Sopenharmony_ci			usb_put_dev(udev);
678c2ecf20Sopenharmony_ci			return NULL;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return udev;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void destroy_lvs_device(struct usb_device *udev)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct usb_device *hdev = udev->parent;
778c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (hcd->driver->free_dev)
808c2ecf20Sopenharmony_ci		hcd->driver->free_dev(hcd, udev);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	usb_put_dev(udev);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int lvs_rh_clear_port_feature(struct usb_device *hdev,
868c2ecf20Sopenharmony_ci		int port1, int feature)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
898c2ecf20Sopenharmony_ci		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
908c2ecf20Sopenharmony_ci		NULL, 0, 1000);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int lvs_rh_set_port_feature(struct usb_device *hdev,
948c2ecf20Sopenharmony_ci		int port1, int feature)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
978c2ecf20Sopenharmony_ci		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
988c2ecf20Sopenharmony_ci		NULL, 0, 1000);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic ssize_t u3_entry_store(struct device *dev,
1028c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
1058c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
1068c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
1078c2ecf20Sopenharmony_ci	struct usb_device *udev;
1088c2ecf20Sopenharmony_ci	int ret;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	udev = create_lvs_device(intf);
1118c2ecf20Sopenharmony_ci	if (!udev) {
1128c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create lvs device\n");
1138c2ecf20Sopenharmony_ci		return -ENOMEM;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
1178c2ecf20Sopenharmony_ci			USB_PORT_FEAT_SUSPEND);
1188c2ecf20Sopenharmony_ci	if (ret < 0)
1198c2ecf20Sopenharmony_ci		dev_err(dev, "can't issue U3 entry %d\n", ret);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	destroy_lvs_device(udev);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (ret < 0)
1248c2ecf20Sopenharmony_ci		return ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return count;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(u3_entry);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic ssize_t u3_exit_store(struct device *dev,
1318c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
1348c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
1358c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
1368c2ecf20Sopenharmony_ci	struct usb_device *udev;
1378c2ecf20Sopenharmony_ci	int ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	udev = create_lvs_device(intf);
1408c2ecf20Sopenharmony_ci	if (!udev) {
1418c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create lvs device\n");
1428c2ecf20Sopenharmony_ci		return -ENOMEM;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	ret = lvs_rh_clear_port_feature(hdev, lvs->portnum,
1468c2ecf20Sopenharmony_ci			USB_PORT_FEAT_SUSPEND);
1478c2ecf20Sopenharmony_ci	if (ret < 0)
1488c2ecf20Sopenharmony_ci		dev_err(dev, "can't issue U3 exit %d\n", ret);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	destroy_lvs_device(udev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (ret < 0)
1538c2ecf20Sopenharmony_ci		return ret;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return count;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(u3_exit);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic ssize_t hot_reset_store(struct device *dev,
1608c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
1638c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
1648c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
1658c2ecf20Sopenharmony_ci	int ret;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
1688c2ecf20Sopenharmony_ci			USB_PORT_FEAT_RESET);
1698c2ecf20Sopenharmony_ci	if (ret < 0) {
1708c2ecf20Sopenharmony_ci		dev_err(dev, "can't issue hot reset %d\n", ret);
1718c2ecf20Sopenharmony_ci		return ret;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return count;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(hot_reset);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic ssize_t warm_reset_store(struct device *dev,
1798c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
1828c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
1838c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
1848c2ecf20Sopenharmony_ci	int ret;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
1878c2ecf20Sopenharmony_ci			USB_PORT_FEAT_BH_PORT_RESET);
1888c2ecf20Sopenharmony_ci	if (ret < 0) {
1898c2ecf20Sopenharmony_ci		dev_err(dev, "can't issue warm reset %d\n", ret);
1908c2ecf20Sopenharmony_ci		return ret;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return count;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(warm_reset);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic ssize_t u2_timeout_store(struct device *dev,
1988c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2018c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
2028c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
2038c2ecf20Sopenharmony_ci	unsigned long val;
2048c2ecf20Sopenharmony_ci	int ret;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 10, &val);
2078c2ecf20Sopenharmony_ci	if (ret < 0) {
2088c2ecf20Sopenharmony_ci		dev_err(dev, "couldn't parse string %d\n", ret);
2098c2ecf20Sopenharmony_ci		return ret;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (val > 127)
2138c2ecf20Sopenharmony_ci		return -EINVAL;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
2168c2ecf20Sopenharmony_ci			USB_PORT_FEAT_U2_TIMEOUT);
2178c2ecf20Sopenharmony_ci	if (ret < 0) {
2188c2ecf20Sopenharmony_ci		dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val);
2198c2ecf20Sopenharmony_ci		return ret;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return count;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(u2_timeout);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic ssize_t u1_timeout_store(struct device *dev,
2278c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2308c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
2318c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
2328c2ecf20Sopenharmony_ci	unsigned long val;
2338c2ecf20Sopenharmony_ci	int ret;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 10, &val);
2368c2ecf20Sopenharmony_ci	if (ret < 0) {
2378c2ecf20Sopenharmony_ci		dev_err(dev, "couldn't parse string %d\n", ret);
2388c2ecf20Sopenharmony_ci		return ret;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (val > 127)
2428c2ecf20Sopenharmony_ci		return -EINVAL;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
2458c2ecf20Sopenharmony_ci			USB_PORT_FEAT_U1_TIMEOUT);
2468c2ecf20Sopenharmony_ci	if (ret < 0) {
2478c2ecf20Sopenharmony_ci		dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val);
2488c2ecf20Sopenharmony_ci		return ret;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return count;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(u1_timeout);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic ssize_t get_dev_desc_store(struct device *dev,
2568c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2598c2ecf20Sopenharmony_ci	struct usb_device *udev;
2608c2ecf20Sopenharmony_ci	struct usb_device_descriptor *descriptor;
2618c2ecf20Sopenharmony_ci	int ret;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
2648c2ecf20Sopenharmony_ci	if (!descriptor)
2658c2ecf20Sopenharmony_ci		return -ENOMEM;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	udev = create_lvs_device(intf);
2688c2ecf20Sopenharmony_ci	if (!udev) {
2698c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create lvs device\n");
2708c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2718c2ecf20Sopenharmony_ci		goto free_desc;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN,
2758c2ecf20Sopenharmony_ci			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8,
2768c2ecf20Sopenharmony_ci			0, descriptor, sizeof(*descriptor),
2778c2ecf20Sopenharmony_ci			USB_CTRL_GET_TIMEOUT);
2788c2ecf20Sopenharmony_ci	if (ret < 0)
2798c2ecf20Sopenharmony_ci		dev_err(dev, "can't read device descriptor %d\n", ret);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	destroy_lvs_device(udev);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cifree_desc:
2848c2ecf20Sopenharmony_ci	kfree(descriptor);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (ret < 0)
2878c2ecf20Sopenharmony_ci		return ret;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return count;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(get_dev_desc);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic ssize_t enable_compliance_store(struct device *dev,
2948c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2978c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
2988c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
2998c2ecf20Sopenharmony_ci	int ret;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	ret = lvs_rh_set_port_feature(hdev,
3028c2ecf20Sopenharmony_ci			lvs->portnum | USB_SS_PORT_LS_COMP_MOD << 3,
3038c2ecf20Sopenharmony_ci			USB_PORT_FEAT_LINK_STATE);
3048c2ecf20Sopenharmony_ci	if (ret < 0) {
3058c2ecf20Sopenharmony_ci		dev_err(dev, "can't enable compliance mode %d\n", ret);
3068c2ecf20Sopenharmony_ci		return ret;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return count;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(enable_compliance);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic struct attribute *lvs_attrs[] = {
3148c2ecf20Sopenharmony_ci	&dev_attr_get_dev_desc.attr,
3158c2ecf20Sopenharmony_ci	&dev_attr_u1_timeout.attr,
3168c2ecf20Sopenharmony_ci	&dev_attr_u2_timeout.attr,
3178c2ecf20Sopenharmony_ci	&dev_attr_hot_reset.attr,
3188c2ecf20Sopenharmony_ci	&dev_attr_warm_reset.attr,
3198c2ecf20Sopenharmony_ci	&dev_attr_u3_entry.attr,
3208c2ecf20Sopenharmony_ci	&dev_attr_u3_exit.attr,
3218c2ecf20Sopenharmony_ci	&dev_attr_enable_compliance.attr,
3228c2ecf20Sopenharmony_ci	NULL
3238c2ecf20Sopenharmony_ci};
3248c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(lvs);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void lvs_rh_work(struct work_struct *work)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work);
3298c2ecf20Sopenharmony_ci	struct usb_interface *intf = lvs->intf;
3308c2ecf20Sopenharmony_ci	struct usb_device *hdev = interface_to_usbdev(intf);
3318c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
3328c2ecf20Sopenharmony_ci	struct usb_hub_descriptor *descriptor = &lvs->descriptor;
3338c2ecf20Sopenharmony_ci	struct usb_port_status *port_status = &lvs->port_status;
3348c2ecf20Sopenharmony_ci	int i, ret = 0;
3358c2ecf20Sopenharmony_ci	u16 portchange;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* Examine each root port */
3388c2ecf20Sopenharmony_ci	for (i = 1; i <= descriptor->bNbrPorts; i++) {
3398c2ecf20Sopenharmony_ci		ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
3408c2ecf20Sopenharmony_ci			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i,
3418c2ecf20Sopenharmony_ci			port_status, sizeof(*port_status), 1000);
3428c2ecf20Sopenharmony_ci		if (ret < 4)
3438c2ecf20Sopenharmony_ci			continue;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		portchange = le16_to_cpu(port_status->wPortChange);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_LINK_STATE)
3488c2ecf20Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
3498c2ecf20Sopenharmony_ci					USB_PORT_FEAT_C_PORT_LINK_STATE);
3508c2ecf20Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_ENABLE)
3518c2ecf20Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
3528c2ecf20Sopenharmony_ci					USB_PORT_FEAT_C_ENABLE);
3538c2ecf20Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_RESET)
3548c2ecf20Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
3558c2ecf20Sopenharmony_ci					USB_PORT_FEAT_C_RESET);
3568c2ecf20Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_BH_RESET)
3578c2ecf20Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
3588c2ecf20Sopenharmony_ci					USB_PORT_FEAT_C_BH_PORT_RESET);
3598c2ecf20Sopenharmony_ci		if (portchange & USB_PORT_STAT_C_CONNECTION) {
3608c2ecf20Sopenharmony_ci			lvs_rh_clear_port_feature(hdev, i,
3618c2ecf20Sopenharmony_ci					USB_PORT_FEAT_C_CONNECTION);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			if (le16_to_cpu(port_status->wPortStatus) &
3648c2ecf20Sopenharmony_ci					USB_PORT_STAT_CONNECTION) {
3658c2ecf20Sopenharmony_ci				lvs->present = true;
3668c2ecf20Sopenharmony_ci				lvs->portnum = i;
3678c2ecf20Sopenharmony_ci				if (hcd->usb_phy)
3688c2ecf20Sopenharmony_ci					usb_phy_notify_connect(hcd->usb_phy,
3698c2ecf20Sopenharmony_ci							USB_SPEED_SUPER);
3708c2ecf20Sopenharmony_ci			} else {
3718c2ecf20Sopenharmony_ci				lvs->present = false;
3728c2ecf20Sopenharmony_ci				if (hcd->usb_phy)
3738c2ecf20Sopenharmony_ci					usb_phy_notify_disconnect(hcd->usb_phy,
3748c2ecf20Sopenharmony_ci							USB_SPEED_SUPER);
3758c2ecf20Sopenharmony_ci			}
3768c2ecf20Sopenharmony_ci			break;
3778c2ecf20Sopenharmony_ci		}
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
3818c2ecf20Sopenharmony_ci	if (ret != 0 && ret != -ENODEV && ret != -EPERM)
3828c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "urb resubmit error %d\n", ret);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic void lvs_rh_irq(struct urb *urb)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = urb->context;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	schedule_work(&lvs->rh_work);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int lvs_rh_probe(struct usb_interface *intf,
3938c2ecf20Sopenharmony_ci		const struct usb_device_id *id)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct usb_device *hdev;
3968c2ecf20Sopenharmony_ci	struct usb_host_interface *desc;
3978c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
3988c2ecf20Sopenharmony_ci	struct lvs_rh *lvs;
3998c2ecf20Sopenharmony_ci	unsigned int pipe;
4008c2ecf20Sopenharmony_ci	int ret, maxp;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	hdev = interface_to_usbdev(intf);
4038c2ecf20Sopenharmony_ci	desc = intf->cur_altsetting;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	ret = usb_find_int_in_endpoint(desc, &endpoint);
4068c2ecf20Sopenharmony_ci	if (ret)
4078c2ecf20Sopenharmony_ci		return ret;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* valid only for SS root hub */
4108c2ecf20Sopenharmony_ci	if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) {
4118c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n");
4128c2ecf20Sopenharmony_ci		return -EINVAL;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL);
4168c2ecf20Sopenharmony_ci	if (!lvs)
4178c2ecf20Sopenharmony_ci		return -ENOMEM;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	lvs->intf = intf;
4208c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, lvs);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* how many number of ports this root hub has */
4238c2ecf20Sopenharmony_ci	ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
4248c2ecf20Sopenharmony_ci			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
4258c2ecf20Sopenharmony_ci			USB_DT_SS_HUB << 8, 0, &lvs->descriptor,
4268c2ecf20Sopenharmony_ci			USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
4278c2ecf20Sopenharmony_ci	if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
4288c2ecf20Sopenharmony_ci		dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
4298c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EINVAL;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* submit urb to poll interrupt endpoint */
4338c2ecf20Sopenharmony_ci	lvs->urb = usb_alloc_urb(0, GFP_KERNEL);
4348c2ecf20Sopenharmony_ci	if (!lvs->urb)
4358c2ecf20Sopenharmony_ci		return -ENOMEM;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	INIT_WORK(&lvs->rh_work, lvs_rh_work);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
4408c2ecf20Sopenharmony_ci	maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
4418c2ecf20Sopenharmony_ci	usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp,
4428c2ecf20Sopenharmony_ci			lvs_rh_irq, lvs, endpoint->bInterval);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
4458c2ecf20Sopenharmony_ci	if (ret < 0) {
4468c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret);
4478c2ecf20Sopenharmony_ci		goto free_urb;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return ret;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cifree_urb:
4538c2ecf20Sopenharmony_ci	usb_free_urb(lvs->urb);
4548c2ecf20Sopenharmony_ci	return ret;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void lvs_rh_disconnect(struct usb_interface *intf)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct lvs_rh *lvs = usb_get_intfdata(intf);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	usb_poison_urb(lvs->urb); /* used in scheduled work */
4628c2ecf20Sopenharmony_ci	flush_work(&lvs->rh_work);
4638c2ecf20Sopenharmony_ci	usb_free_urb(lvs->urb);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic struct usb_driver lvs_driver = {
4678c2ecf20Sopenharmony_ci	.name =		"lvs",
4688c2ecf20Sopenharmony_ci	.probe =	lvs_rh_probe,
4698c2ecf20Sopenharmony_ci	.disconnect =	lvs_rh_disconnect,
4708c2ecf20Sopenharmony_ci	.dev_groups =	lvs_groups,
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cimodule_usb_driver(lvs_driver);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Link Layer Validation System Driver");
4768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
477