/* * USB2 Test Mode driver * Copyright (C) 2011, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ /* This driver supports USB Host Test Mode initiated by test device with * VID 0x1a0a for USB controller in high-speed. */ #include #include #include #include #include #include #include #include #include "../core/usb.h" struct usb_tm_dev { struct usb_device *udev; struct usb_hcd *hcd; #define TBUF_SIZE 256 u8 *buf; }; static int usb_tm_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_tm_dev *dev; int retval, port_num; dev_dbg(&intf->dev, "USB test mode is initiated.\n"); dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->buf = kmalloc(TBUF_SIZE, GFP_KERNEL); if (!dev->buf) { kfree(dev); return -ENOMEM; } dev->udev = usb_get_dev(interface_to_usbdev(intf)); dev->hcd = usb_get_hcd(bus_to_hcd(dev->udev->bus)); usb_set_intfdata(intf, dev); port_num = dev->udev->portnum & 0xff; dev_dbg(&intf->dev, "test mode PID 0x%04x\n", le16_to_cpu(dev->udev->descriptor.idProduct)); switch (le16_to_cpu(dev->udev->descriptor.idProduct)) { case 0x0101: /* TEST_SE0_NAK */ dev->hcd->driver->hub_control(dev->hcd, SetPortFeature, USB_PORT_FEAT_TEST, 0x300 + port_num, NULL, 0); break; case 0x0102: /* TEST_J */ dev->hcd->driver->hub_control(dev->hcd, SetPortFeature, USB_PORT_FEAT_TEST, 0x100 + port_num, NULL, 0); break; case 0x0103: /* TEST_K */ dev->hcd->driver->hub_control(dev->hcd, SetPortFeature, USB_PORT_FEAT_TEST, 0x200 + port_num, NULL, 0); break; case 0x0104: /* TEST_PACKET */ dev->hcd->driver->hub_control(dev->hcd, SetPortFeature, USB_PORT_FEAT_TEST, 0x400 + port_num, NULL, 0); break; case 0x0106: /* HS_HOST_PORT_SUSPEND_RESUME */ msleep(15000); dev->hcd->driver->hub_control(dev->hcd, SetPortFeature, USB_PORT_FEAT_SUSPEND, port_num, NULL, 0); msleep(15000); dev->hcd->driver->hub_control(dev->hcd, ClearPortFeature, USB_PORT_FEAT_SUSPEND, port_num, NULL, 0); break; case 0x0107: /* SINGLE_STEP_GET_DEV_DESC */ msleep(15000); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RECIP_DEVICE, cpu_to_le16(USB_DT_DEVICE << 8), 0, dev->buf, USB_DT_DEVICE_SIZE, USB_CTRL_GET_TIMEOUT); break; case 0x0108: /* SINGLE_STEP_SET_FEATURE */ /* FIXME */ /* set size = 0 to ignore DATA phase */ retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RECIP_DEVICE, cpu_to_le16(USB_DT_DEVICE << 8), 0, dev->buf, 0, USB_CTRL_GET_TIMEOUT); msleep(15000); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RECIP_DEVICE, cpu_to_le16(USB_DT_DEVICE << 8), 0, dev->buf, USB_DT_DEVICE_SIZE, USB_CTRL_GET_TIMEOUT); break; default: dev_info(&intf->dev, "unknown test mode with PID 0x%04x", id->idProduct); } return 0; } static void usb_tm_disconnect(struct usb_interface *intf) { struct usb_tm_dev *dev = usb_get_intfdata(intf); usb_put_hcd(dev->hcd); usb_put_dev(dev->udev); usb_set_intfdata(intf, NULL); dev_dbg(&intf->dev, "disconnect\n"); kfree(dev->buf); kfree(dev); } static const struct usb_device_id id_table[] = { /* USB Test Device */ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x1A0A, }, { } }; MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver usb_tm_driver = { .name = "usb_tm", .id_table = id_table, .probe = usb_tm_probe, .disconnect = usb_tm_disconnect, }; /*-------------------------------------------------------------------------*/ static int __init usb_tm_init(void) { int result; result = usb_register(&usb_tm_driver); if (result) pr_err("usb_tm: usb_register failed. error number %d", result); return result; } module_init(usb_tm_init); static void __exit usb_tm_exit(void) { usb_deregister(&usb_tm_driver); } module_exit(usb_tm_exit); MODULE_DESCRIPTION("USB Test Mode Driver"); MODULE_LICENSE("GPL");