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