For an xhci host's downstream port, Link Validation System (LVS) is a hardware which acts as device and used to run link layer tests. Most of the link layer tests only expects link to be in U0. If a host sends a setup packet, it is NAKed by LVS. However, there are some tests which ask host to send some control command like Get Descriptor etc. This patch adds a sysfs node to add a quirk flag to prevent enumeration of a device, and then to call a driver for this device which can further add options to generate test case specific traffic. Without this patch many Lecroy Link Layer tests such as TD.7.02 fail. Before a lecroy LVS device is connected to the root hub n, do following: echo 1 > /sys/bus/usb/devices/usbn/avoid_enum_quirk Now connect LVS device and run Link Layer tests from Lecroy USB Compliance Suite. To get back into normal working situation (to connect a general usb device to root hub n), do following: echo 0 > /sys/bus/usb/devices/usbn/avoid_enum_quirk Signed-off-by: Pratyush Anand <pratyush.anand@xxxxxx> --- Documentation/ABI/testing/sysfs-bus-usb | 10 +++ drivers/usb/core/hub.c | 105 +++++++++++++++++++++++++++++++- drivers/usb/core/hub.h | 2 + drivers/usb/core/sysfs.c | 34 +++++++++++ drivers/usb/core/usb.c | 1 - include/linux/usb/quirks.h | 3 + 6 files changed, 151 insertions(+), 4 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index f093e59..d7f6bfd 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -236,3 +236,13 @@ Description: This attribute is to expose these information to user space. The file will read "hotplug", "wired" and "not used" if the information is available, and "unknown" otherwise. + +What: /sys/bus/usb/device/.../avoid_enum_quirk +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@xxxxxx> +Description: + Only root hub allows to write. Writing to + other device will return error. Writing 1 to this file tells the + kernel that device connected to this root hub must not + be enumerated. Ports of this root hub will be used to + conncet Link Layer Validation System device. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5edcfb2..124b5c8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -27,6 +27,7 @@ #include <linux/freezer.h> #include <linux/random.h> #include <linux/pm_qos.h> +#include <linux/platform_device.h> #include <asm/uaccess.h> #include <asm/byteorder.h> @@ -4656,6 +4657,98 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, return connect_change; } +static void usb_destroy_lvs_device(struct usb_hub *hub, int port1) +{ + struct usb_device *hdev = hub->hdev; + struct platform_device *lvstestdev = hub->ports[port1 -1]->lvstestdev; + struct usb_device *udev = hub->ports[port1 - 1]->child; + + if (!lvstestdev) { + dev_err(&hdev->dev, "No Platform device at port %d\n", port1); + return; + } + + platform_device_unregister(lvstestdev); + + kfree(udev); + + hub->ports[port1 -1]->child = NULL; + hub->ports[port1 -1]->lvstestdev = NULL; + dev_info(&hdev->dev, "LVS device destroyed\n"); +} + +static int usb_create_lvs_device(struct usb_hub *hub, int port1) +{ + struct usb_device *hdev = hub->hdev; + struct usb_device *udev; + struct platform_device *lvstestdev = hub->ports[port1 -1]->lvstestdev; + int ret; + + if (lvstestdev) { + dev_err(&hdev->dev, + "Already a platform device at port %d\n", port1); + usb_destroy_lvs_device(hub, port1); + } + + udev = usb_alloc_dev(hdev, hdev->bus, port1); + if (!udev) { + dev_err (&hdev->dev, "couldn't allocate port %d usb_device\n", + port1); + return -ENOMEM; + } + + if (hub_is_superspeed(hdev)) { + udev->speed = USB_SPEED_SUPER; + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); + usb_set_device_state(udev, USB_STATE_DEFAULT); + ret = hub_enable_device(udev); + if (ret < 0) { + dev_err(&hdev->dev, "Failed to enable %d\n", ret); + goto free_udev; + } + } else { + dev_err(&hdev->dev, "Host not supported\n"); + ret = -EINVAL; + goto free_udev; + } + + lvstestdev = platform_device_alloc("lvstestdev", PLATFORM_DEVID_AUTO); + if (!lvstestdev) { + dev_err(&hdev->dev, "couldn't allocate lvstestdev device\n"); + ret = -ENOMEM; + goto free_udev; + } + + /* + * udev will be copied to platform_data and then memory of udev + * will be released. + */ + ret = platform_device_add_data(lvstestdev, &udev, sizeof(udev)); + if (ret) { + dev_err(&hdev->dev, "couldn't add udev data\n"); + goto free_lvstestdev; + } + + ret = platform_device_add(lvstestdev); + if (ret) { + dev_err(&hdev->dev, "failed to register lvstestdev device\n"); + goto free_lvstestdev; + } + + hub->ports[port1 -1]->lvstestdev = lvstestdev; + hub->ports[port1 -1]->child = udev; + + dev_info(&hdev->dev, "LVS device created\n"); + return 0; + +free_lvstestdev: + platform_device_put(lvstestdev); +free_udev: + kfree(udev); + + return ret; +} + static void hub_events(void) { struct list_head *tmp; @@ -4862,9 +4955,15 @@ static void hub_events(void) } } - if (connect_change) - hub_port_connect_change(hub, i, - portstatus, portchange); + if (connect_change) { + if (!(hdev->quirks & USB_QUIRK_ENUM)) + hub_port_connect_change(hub, i, + portstatus, portchange); + else if (portstatus & USB_PORT_STAT_ENABLE) + usb_create_lvs_device(hub, i); + else + usb_destroy_lvs_device(hub, i); + } } /* end for i */ /* deal with hub status changes */ diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index f608b39..fa25f62 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -85,6 +85,7 @@ struct usb_hub { * @portnum: port index num based one * @power_is_on: port's power state * @did_runtime_put: port has done pm_runtime_put(). + * @lvs_data: LVS platform device attached to this port */ struct usb_port { struct usb_device *child; @@ -94,6 +95,7 @@ struct usb_port { u8 portnum; unsigned power_is_on:1; unsigned did_runtime_put:1; + void *lvstestdev; }; #define to_usb_port(_dev) \ diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index aa38db4..0a4a5ab 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -234,6 +234,39 @@ static DEVICE_ATTR(avoid_reset_quirk, S_IRUGO | S_IWUSR, show_avoid_reset_quirk, set_avoid_reset_quirk); static ssize_t +show_avoid_enum_quirk(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_ENUM)); +} + +static ssize_t +set_avoid_enum_quirk(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int val; + + if (udev->parent) + return -EINVAL; + + if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1) + return -EINVAL; + usb_lock_device(udev); + if (val) + udev->quirks |= USB_QUIRK_ENUM; + else + udev->quirks &= ~USB_QUIRK_ENUM; + usb_unlock_device(udev); + return count; +} + +static DEVICE_ATTR(avoid_enum_quirk, S_IRUGO | S_IWUSR, + show_avoid_enum_quirk, set_avoid_enum_quirk); + +static ssize_t show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -668,6 +701,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_maxchild.attr, &dev_attr_quirks.attr, &dev_attr_avoid_reset_quirk.attr, + &dev_attr_avoid_enum_quirk.attr, &dev_attr_authorized.attr, &dev_attr_remove.attr, &dev_attr_removable.attr, diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b10da72..d708baa 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -345,7 +345,6 @@ static unsigned usb_bus_is_wusb(struct usb_bus *bus) return hcd->wireless; } - /** * usb_alloc_dev - usb device constructor (usbcore-internal) * @parent: hub to which device is connected; null to allocate a root hub diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 52f944d..2a1ac98 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -30,4 +30,7 @@ descriptor */ #define USB_QUIRK_DELAY_INIT 0x00000040 +/* device can't be ennumerated. This is a Lecroy Link Layer Validation System */ +#define USB_QUIRK_ENUM 0x00000080 + #endif /* __LINUX_USB_QUIRKS_H */ -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html