1e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/* 2e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * f_loopback.c - USB peripheral loopback configuration driver 3e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * 4e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * Copyright (C) 2003-2008 David Brownell 5e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * Copyright (C) 2008 by Nokia Corporation 6e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * 7e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * This program is free software; you can redistribute it and/or modify 8e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * it under the terms of the GNU General Public License as published by 9e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * the Free Software Foundation; either version 2 of the License, or 10e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * (at your option) any later version. 11e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell */ 12e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 13e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/* #define VERBOSE_DEBUG */ 14e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 16e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell#include <linux/kernel.h> 17e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell#include <linux/device.h> 18e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 19e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell#include "g_zero.h" 20e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell#include "gadget_chips.h" 21e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 22e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 23e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/* 24e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, 25e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * 26e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * This takes messages of various sizes written OUT to a device, and loops 27e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * them back so they can be read IN from it. It has been used by certain 28e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * test applications. It supports limited testing of data queueing logic. 29e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * 30e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * 31e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * This is currently packaged as a configuration driver, which can't be 32e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * combined with other functions to make composite devices. However, it 33e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * can be combined with other independent configurations. 34e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell */ 35e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstruct f_loopback { 36e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_function function; 37e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 38e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_ep *in_ep; 39e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_ep *out_ep; 40e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 41e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 42e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic inline struct f_loopback *func_to_loop(struct usb_function *f) 43e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 44e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return container_of(f, struct f_loopback, function); 45e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 46e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 47e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic unsigned qlen = 32; 48e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellmodule_param(qlen, uint, 0); 49e5760fdac8c8aeca060d1afc8a233ea3d7a46720David BrownellMODULE_PARM_DESC(qlenn, "depth of loopback queue"); 50e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 51e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/*-------------------------------------------------------------------------*/ 52e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 53e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic struct usb_interface_descriptor loopback_intf = { 54e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bLength = sizeof loopback_intf, 55e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bDescriptorType = USB_DT_INTERFACE, 56e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 57e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bNumEndpoints = 2, 58e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 59e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* .iInterface = DYNAMIC */ 60e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 61e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 62e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/* full speed support: */ 63e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 647e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownellstatic struct usb_endpoint_descriptor fs_loop_source_desc = { 65e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 66e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bDescriptorType = USB_DT_ENDPOINT, 67e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 68e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bEndpointAddress = USB_DIR_IN, 69e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 70e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 71e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 727e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownellstatic struct usb_endpoint_descriptor fs_loop_sink_desc = { 73e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 74e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bDescriptorType = USB_DT_ENDPOINT, 75e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 76e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bEndpointAddress = USB_DIR_OUT, 77e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 78e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 79e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 80e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic struct usb_descriptor_header *fs_loopback_descs[] = { 81e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell (struct usb_descriptor_header *) &loopback_intf, 827e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell (struct usb_descriptor_header *) &fs_loop_sink_desc, 837e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell (struct usb_descriptor_header *) &fs_loop_source_desc, 84e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell NULL, 85e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 86e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 87e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/* high speed support: */ 88e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 897e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownellstatic struct usb_endpoint_descriptor hs_loop_source_desc = { 90e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 91e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bDescriptorType = USB_DT_ENDPOINT, 92e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 93e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 94551509d267905705f6d723e51ec706916f06b859Harvey Harrison .wMaxPacketSize = cpu_to_le16(512), 95e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 96e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 977e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownellstatic struct usb_endpoint_descriptor hs_loop_sink_desc = { 98e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 99e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bDescriptorType = USB_DT_ENDPOINT, 100e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 101e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 102551509d267905705f6d723e51ec706916f06b859Harvey Harrison .wMaxPacketSize = cpu_to_le16(512), 103e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 104e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 105e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic struct usb_descriptor_header *hs_loopback_descs[] = { 106e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell (struct usb_descriptor_header *) &loopback_intf, 1077e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell (struct usb_descriptor_header *) &hs_loop_source_desc, 1087e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell (struct usb_descriptor_header *) &hs_loop_sink_desc, 109e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell NULL, 110e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 111e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 11257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay/* super speed support: */ 11357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 11457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystatic struct usb_endpoint_descriptor ss_loop_source_desc = { 11557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bLength = USB_DT_ENDPOINT_SIZE, 11657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bDescriptorType = USB_DT_ENDPOINT, 11757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 11857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bmAttributes = USB_ENDPOINT_XFER_BULK, 11957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .wMaxPacketSize = cpu_to_le16(1024), 12057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay}; 12157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 12257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystruct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { 12357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bLength = USB_DT_SS_EP_COMP_SIZE, 12457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 12557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bMaxBurst = 0, 12657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bmAttributes = 0, 12757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .wBytesPerInterval = 0, 12857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay}; 12957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 13057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystatic struct usb_endpoint_descriptor ss_loop_sink_desc = { 13157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bLength = USB_DT_ENDPOINT_SIZE, 13257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bDescriptorType = USB_DT_ENDPOINT, 13357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 13457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bmAttributes = USB_ENDPOINT_XFER_BULK, 13557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .wMaxPacketSize = cpu_to_le16(1024), 13657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay}; 13757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 13857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystruct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { 13957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bLength = USB_DT_SS_EP_COMP_SIZE, 14057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 14157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bMaxBurst = 0, 14257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .bmAttributes = 0, 14357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay .wBytesPerInterval = 0, 14457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay}; 14557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 14657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystatic struct usb_descriptor_header *ss_loopback_descs[] = { 14757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (struct usb_descriptor_header *) &loopback_intf, 14857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (struct usb_descriptor_header *) &ss_loop_source_desc, 14957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (struct usb_descriptor_header *) &ss_loop_source_comp_desc, 15057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (struct usb_descriptor_header *) &ss_loop_sink_desc, 15157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (struct usb_descriptor_header *) &ss_loop_sink_comp_desc, 15257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay NULL, 15357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay}; 15457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 155e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/* function-specific strings: */ 156e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 157e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic struct usb_string strings_loopback[] = { 158e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell [0].s = "loop input to output", 159e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell { } /* end of list */ 160e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 161e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 162e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic struct usb_gadget_strings stringtab_loop = { 163e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .language = 0x0409, /* en-us */ 164e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .strings = strings_loopback, 165e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 166e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 167e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic struct usb_gadget_strings *loopback_strings[] = { 168e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell &stringtab_loop, 169e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell NULL, 170e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 171e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 172e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/*-------------------------------------------------------------------------*/ 173e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 174e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic int __init 175e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellloopback_bind(struct usb_configuration *c, struct usb_function *f) 176e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 177e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_composite_dev *cdev = c->cdev; 178e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct f_loopback *loop = func_to_loop(f); 179e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell int id; 180e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 181e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* allocate interface ID(s) */ 182e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell id = usb_interface_id(c, f); 183e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (id < 0) 184e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return id; 185e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loopback_intf.bInterfaceNumber = id; 186e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 187e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* allocate endpoints */ 188e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 1897e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc); 190e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (!loop->in_ep) { 191e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellautoconf_fail: 192e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ERROR(cdev, "%s: can't autoconfigure on %s\n", 193e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell f->name, cdev->gadget->name); 194e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return -ENODEV; 195e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 196e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->in_ep->driver_data = cdev; /* claim */ 197e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 1987e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc); 199e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (!loop->out_ep) 200e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell goto autoconf_fail; 201e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->out_ep->driver_data = cdev; /* claim */ 202e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 203e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* support high speed hardware */ 204e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (gadget_is_dualspeed(c->cdev->gadget)) { 2057e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell hs_loop_source_desc.bEndpointAddress = 2067e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell fs_loop_source_desc.bEndpointAddress; 2077e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell hs_loop_sink_desc.bEndpointAddress = 2087e75bc0f9006e995a0fa25f0a285addc3d5fd5cbDavid Brownell fs_loop_sink_desc.bEndpointAddress; 209e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell f->hs_descriptors = hs_loopback_descs; 210e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 211e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 21257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay /* support super speed hardware */ 21357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay if (gadget_is_superspeed(c->cdev->gadget)) { 21457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay ss_loop_source_desc.bEndpointAddress = 21557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay fs_loop_source_desc.bEndpointAddress; 21657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay ss_loop_sink_desc.bEndpointAddress = 21757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay fs_loop_sink_desc.bEndpointAddress; 21857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay f->ss_descriptors = ss_loopback_descs; 21957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay } 22057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay 221e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", 22257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (gadget_is_superspeed(c->cdev->gadget) ? "super" : 22357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), 224e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell f->name, loop->in_ep->name, loop->out_ep->name); 225e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return 0; 226e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 227e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 228e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic void 229e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellloopback_unbind(struct usb_configuration *c, struct usb_function *f) 230e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 231e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell kfree(func_to_loop(f)); 232e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 233e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 234e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic void loopback_complete(struct usb_ep *ep, struct usb_request *req) 235e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 236e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct f_loopback *loop = ep->driver_data; 237e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_composite_dev *cdev = loop->function.config->cdev; 238e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell int status = req->status; 239e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 240e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell switch (status) { 241e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 242e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell case 0: /* normal completion? */ 243e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (ep == loop->out_ep) { 244e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* loop this OUT packet back IN to the host */ 245e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell req->zero = (req->actual < req->length); 246e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell req->length = req->actual; 247e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); 248e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (status == 0) 249e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return; 250e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 251e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* "should never get here" */ 252e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ERROR(cdev, "can't loop %s to %s: %d\n", 253e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep->name, loop->in_ep->name, 254e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell status); 255e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 256e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 257e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* queue the buffer for some later OUT packet */ 258e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell req->length = buflen; 259e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); 260e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (status == 0) 261e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return; 262e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 263e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* "should never get here" */ 264e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* FALLTHROUGH */ 265e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 266e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell default: 267e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, 268e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell status, req->actual, req->length); 269e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* FALLTHROUGH */ 270e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 271e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* NOTE: since this driver doesn't maintain an explicit record 272e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * of requests it submitted (just maintains qlen count), we 273e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * rely on the hardware driver to clean up on disconnect or 274e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * endpoint disable. 275e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell */ 276e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell case -ECONNABORTED: /* hardware forced ep reset */ 277e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell case -ECONNRESET: /* request dequeued */ 278e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell case -ESHUTDOWN: /* disconnect from host */ 279e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell free_ep_req(ep, req); 280e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return; 281e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 282e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 283e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 284e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic void disable_loopback(struct f_loopback *loop) 285e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 286e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_composite_dev *cdev; 287e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 288e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell cdev = loop->function.config->cdev; 289e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell disable_endpoints(cdev, loop->in_ep, loop->out_ep); 290e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell VDBG(cdev, "%s disabled\n", loop->function.name); 291e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 292e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 293e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic int 294e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellenable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) 295e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 296e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell int result = 0; 297e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_ep *ep; 298e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_request *req; 299e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell unsigned i; 300e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 301e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* one endpoint writes data back IN to the host */ 302e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep = loop->in_ep; 303ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 304ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman if (result) 305ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman return result; 30672c973dd2b01b212a159faa330a2bc641a3ed809Tatyana Brokhman result = usb_ep_enable(ep); 307e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (result < 0) 308e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return result; 309e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep->driver_data = loop; 310e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 311e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* one endpoint just reads OUT packets */ 312e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep = loop->out_ep; 313ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 314ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman if (result) 315ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman goto fail0; 316ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman 31772c973dd2b01b212a159faa330a2bc641a3ed809Tatyana Brokhman result = usb_ep_enable(ep); 318e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (result < 0) { 319e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellfail0: 320e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep = loop->in_ep; 321e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell usb_ep_disable(ep); 322e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep->driver_data = NULL; 323e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return result; 324e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 325e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep->driver_data = loop; 326e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 327e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* allocate a bunch of read buffers and queue them all at once. 328e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * we buffer at most 'qlen' transfers; fewer if any need more 329e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * than 'buflen' bytes each. 330e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell */ 331e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell for (i = 0; i < qlen && result == 0; i++) { 332e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell req = alloc_ep_req(ep); 333e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (req) { 334e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell req->complete = loopback_complete; 335e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell result = usb_ep_queue(ep, req, GFP_ATOMIC); 336e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (result) 337e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ERROR(cdev, "%s queue req --> %d\n", 338e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep->name, result); 339e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } else { 340e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell usb_ep_disable(ep); 341e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell ep->driver_data = NULL; 342e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell result = -ENOMEM; 343e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell goto fail0; 344e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 345e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 346e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 347e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell DBG(cdev, "%s enabled\n", loop->function.name); 348e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return result; 349e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 350e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 351e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic int loopback_set_alt(struct usb_function *f, 352e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell unsigned intf, unsigned alt) 353e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 354e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct f_loopback *loop = func_to_loop(f); 355e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct usb_composite_dev *cdev = f->config->cdev; 356e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 357e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* we know alt is zero */ 358e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (loop->in_ep->driver_data) 359e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell disable_loopback(loop); 360e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return enable_loopback(cdev, loop); 361e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 362e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 363e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownellstatic void loopback_disable(struct usb_function *f) 364e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 365e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct f_loopback *loop = func_to_loop(f); 366e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 367e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell disable_loopback(loop); 368e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 369e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 370e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/*-------------------------------------------------------------------------*/ 371e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 372e12995ec8f8d99f2a339541fc28998af2d60af0fMichal Nazarewiczstatic int __init loopback_bind_config(struct usb_configuration *c) 373e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 374e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell struct f_loopback *loop; 375e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell int status; 376e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 377e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop = kzalloc(sizeof *loop, GFP_KERNEL); 378e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (!loop) 379e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return -ENOMEM; 380e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 381e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->function.name = "loopback"; 382e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->function.descriptors = fs_loopback_descs; 383e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->function.bind = loopback_bind; 384e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->function.unbind = loopback_unbind; 385e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->function.set_alt = loopback_set_alt; 386e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loop->function.disable = loopback_disable; 387e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 388e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell status = usb_add_function(c, &loop->function); 389e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (status) 390e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell kfree(loop); 391e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return status; 392e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 393e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 394e12995ec8f8d99f2a339541fc28998af2d60af0fMichal Nazarewiczstatic struct usb_configuration loopback_driver = { 395e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .label = "loopback", 396e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .strings = loopback_strings, 397e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bConfigurationValue = 2, 398e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 399e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* .iConfiguration = DYNAMIC */ 400e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell}; 401e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 402e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell/** 403e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * loopback_add - add a loopback testing configuration to a device 404e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell * @cdev: the device to support the loopback configuration 405e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell */ 406ab943a2e125b098489ccaa0166c2c52f8266d9edDavid Brownellint __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) 407e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell{ 408e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell int id; 409e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 410e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* allocate string ID(s) */ 411e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell id = usb_string_id(cdev); 412e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (id < 0) 413e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell return id; 414e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell strings_loopback[0].id = id; 415e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 416e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loopback_intf.iInterface = id; 417e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loopback_driver.iConfiguration = id; 418e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 419ab943a2e125b098489ccaa0166c2c52f8266d9edDavid Brownell /* support autoresume for remote wakeup testing */ 420ab943a2e125b098489ccaa0166c2c52f8266d9edDavid Brownell if (autoresume) 421683da59d7b8ae04891636d4b59893cd4e9b0b7e5Timo Juhani Lindfors loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 422ab943a2e125b098489ccaa0166c2c52f8266d9edDavid Brownell 423e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell /* support OTG systems */ 424e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell if (gadget_is_otg(cdev->gadget)) { 425e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loopback_driver.descriptors = otg_desc; 426e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 427e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell } 428e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell 429c9bfff9c98671ad50e4abbfe1ab606a9957f7539Uwe Kleine-König return usb_add_config(cdev, &loopback_driver, loopback_bind_config); 430e5760fdac8c8aeca060d1afc8a233ea3d7a46720David Brownell} 431