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