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