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