linux/drivers/usb/gadget/f_loopback.c
<<
>>
Prefs
   1/*
   2 * f_loopback.c - USB peripheral loopback configuration driver
   3 *
   4 * Copyright (C) 2003-2008 David Brownell
   5 * Copyright (C) 2008 by Nokia Corporation
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22/* #define VERBOSE_DEBUG */
  23
  24#include <linux/kernel.h>
  25#include <linux/device.h>
  26
  27#include "g_zero.h"
  28#include "gadget_chips.h"
  29
  30
  31/*
  32 * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
  33 *
  34 * This takes messages of various sizes written OUT to a device, and loops
  35 * them back so they can be read IN from it.  It has been used by certain
  36 * test applications.  It supports limited testing of data queueing logic.
  37 *
  38 *
  39 * This is currently packaged as a configuration driver, which can't be
  40 * combined with other functions to make composite devices.  However, it
  41 * can be combined with other independent configurations.
  42 */
  43struct f_loopback {
  44        struct usb_function     function;
  45
  46        struct usb_ep           *in_ep;
  47        struct usb_ep           *out_ep;
  48};
  49
  50static inline struct f_loopback *func_to_loop(struct usb_function *f)
  51{
  52        return container_of(f, struct f_loopback, function);
  53}
  54
  55static unsigned qlen = 32;
  56module_param(qlen, uint, 0);
  57MODULE_PARM_DESC(qlenn, "depth of loopback queue");
  58
  59/*-------------------------------------------------------------------------*/
  60
  61static struct usb_interface_descriptor loopback_intf = {
  62        .bLength =              sizeof loopback_intf,
  63        .bDescriptorType =      USB_DT_INTERFACE,
  64
  65        .bNumEndpoints =        2,
  66        .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
  67        /* .iInterface = DYNAMIC */
  68};
  69
  70/* full speed support: */
  71
  72static struct usb_endpoint_descriptor fs_loop_source_desc = {
  73        .bLength =              USB_DT_ENDPOINT_SIZE,
  74        .bDescriptorType =      USB_DT_ENDPOINT,
  75
  76        .bEndpointAddress =     USB_DIR_IN,
  77        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
  78};
  79
  80static struct usb_endpoint_descriptor fs_loop_sink_desc = {
  81        .bLength =              USB_DT_ENDPOINT_SIZE,
  82        .bDescriptorType =      USB_DT_ENDPOINT,
  83
  84        .bEndpointAddress =     USB_DIR_OUT,
  85        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
  86};
  87
  88static struct usb_descriptor_header *fs_loopback_descs[] = {
  89        (struct usb_descriptor_header *) &loopback_intf,
  90        (struct usb_descriptor_header *) &fs_loop_sink_desc,
  91        (struct usb_descriptor_header *) &fs_loop_source_desc,
  92        NULL,
  93};
  94
  95/* high speed support: */
  96
  97static struct usb_endpoint_descriptor hs_loop_source_desc = {
  98        .bLength =              USB_DT_ENDPOINT_SIZE,
  99        .bDescriptorType =      USB_DT_ENDPOINT,
 100
 101        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
 102        .wMaxPacketSize =       cpu_to_le16(512),
 103};
 104
 105static struct usb_endpoint_descriptor hs_loop_sink_desc = {
 106        .bLength =              USB_DT_ENDPOINT_SIZE,
 107        .bDescriptorType =      USB_DT_ENDPOINT,
 108
 109        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
 110        .wMaxPacketSize =       cpu_to_le16(512),
 111};
 112
 113static struct usb_descriptor_header *hs_loopback_descs[] = {
 114        (struct usb_descriptor_header *) &loopback_intf,
 115        (struct usb_descriptor_header *) &hs_loop_source_desc,
 116        (struct usb_descriptor_header *) &hs_loop_sink_desc,
 117        NULL,
 118};
 119
 120/* function-specific strings: */
 121
 122static struct usb_string strings_loopback[] = {
 123        [0].s = "loop input to output",
 124        {  }                    /* end of list */
 125};
 126
 127static struct usb_gadget_strings stringtab_loop = {
 128        .language       = 0x0409,       /* en-us */
 129        .strings        = strings_loopback,
 130};
 131
 132static struct usb_gadget_strings *loopback_strings[] = {
 133        &stringtab_loop,
 134        NULL,
 135};
 136
 137/*-------------------------------------------------------------------------*/
 138
 139static int __init
 140loopback_bind(struct usb_configuration *c, struct usb_function *f)
 141{
 142        struct usb_composite_dev *cdev = c->cdev;
 143        struct f_loopback       *loop = func_to_loop(f);
 144        int                     id;
 145
 146        /* allocate interface ID(s) */
 147        id = usb_interface_id(c, f);
 148        if (id < 0)
 149                return id;
 150        loopback_intf.bInterfaceNumber = id;
 151
 152        /* allocate endpoints */
 153
 154        loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
 155        if (!loop->in_ep) {
 156autoconf_fail:
 157                ERROR(cdev, "%s: can't autoconfigure on %s\n",
 158                        f->name, cdev->gadget->name);
 159                return -ENODEV;
 160        }
 161        loop->in_ep->driver_data = cdev;        /* claim */
 162
 163        loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
 164        if (!loop->out_ep)
 165                goto autoconf_fail;
 166        loop->out_ep->driver_data = cdev;       /* claim */
 167
 168        /* support high speed hardware */
 169        if (gadget_is_dualspeed(c->cdev->gadget)) {
 170                hs_loop_source_desc.bEndpointAddress =
 171                                fs_loop_source_desc.bEndpointAddress;
 172                hs_loop_sink_desc.bEndpointAddress =
 173                                fs_loop_sink_desc.bEndpointAddress;
 174                f->hs_descriptors = hs_loopback_descs;
 175        }
 176
 177        DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
 178                        gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
 179                        f->name, loop->in_ep->name, loop->out_ep->name);
 180        return 0;
 181}
 182
 183static void
 184loopback_unbind(struct usb_configuration *c, struct usb_function *f)
 185{
 186        kfree(func_to_loop(f));
 187}
 188
 189static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
 190{
 191        struct f_loopback       *loop = ep->driver_data;
 192        struct usb_composite_dev *cdev = loop->function.config->cdev;
 193        int                     status = req->status;
 194
 195        switch (status) {
 196
 197        case 0:                         /* normal completion? */
 198                if (ep == loop->out_ep) {
 199                        /* loop this OUT packet back IN to the host */
 200                        req->zero = (req->actual < req->length);
 201                        req->length = req->actual;
 202                        status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
 203                        if (status == 0)
 204                                return;
 205
 206                        /* "should never get here" */
 207                        ERROR(cdev, "can't loop %s to %s: %d\n",
 208                                ep->name, loop->in_ep->name,
 209                                status);
 210                }
 211
 212                /* queue the buffer for some later OUT packet */
 213                req->length = buflen;
 214                status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
 215                if (status == 0)
 216                        return;
 217
 218                /* "should never get here" */
 219                /* FALLTHROUGH */
 220
 221        default:
 222                ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
 223                                status, req->actual, req->length);
 224                /* FALLTHROUGH */
 225
 226        /* NOTE:  since this driver doesn't maintain an explicit record
 227         * of requests it submitted (just maintains qlen count), we
 228         * rely on the hardware driver to clean up on disconnect or
 229         * endpoint disable.
 230         */
 231        case -ECONNABORTED:             /* hardware forced ep reset */
 232        case -ECONNRESET:               /* request dequeued */
 233        case -ESHUTDOWN:                /* disconnect from host */
 234                free_ep_req(ep, req);
 235                return;
 236        }
 237}
 238
 239static void disable_loopback(struct f_loopback *loop)
 240{
 241        struct usb_composite_dev        *cdev;
 242
 243        cdev = loop->function.config->cdev;
 244        disable_endpoints(cdev, loop->in_ep, loop->out_ep);
 245        VDBG(cdev, "%s disabled\n", loop->function.name);
 246}
 247
 248static int
 249enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
 250{
 251        int                                     result = 0;
 252        const struct usb_endpoint_descriptor    *src, *sink;
 253        struct usb_ep                           *ep;
 254        struct usb_request                      *req;
 255        unsigned                                i;
 256
 257        src = ep_choose(cdev->gadget,
 258                        &hs_loop_source_desc, &fs_loop_source_desc);
 259        sink = ep_choose(cdev->gadget,
 260                        &hs_loop_sink_desc, &fs_loop_sink_desc);
 261
 262        /* one endpoint writes data back IN to the host */
 263        ep = loop->in_ep;
 264        result = usb_ep_enable(ep, src);
 265        if (result < 0)
 266                return result;
 267        ep->driver_data = loop;
 268
 269        /* one endpoint just reads OUT packets */
 270        ep = loop->out_ep;
 271        result = usb_ep_enable(ep, sink);
 272        if (result < 0) {
 273fail0:
 274                ep = loop->in_ep;
 275                usb_ep_disable(ep);
 276                ep->driver_data = NULL;
 277                return result;
 278        }
 279        ep->driver_data = loop;
 280
 281        /* allocate a bunch of read buffers and queue them all at once.
 282         * we buffer at most 'qlen' transfers; fewer if any need more
 283         * than 'buflen' bytes each.
 284         */
 285        for (i = 0; i < qlen && result == 0; i++) {
 286                req = alloc_ep_req(ep);
 287                if (req) {
 288                        req->complete = loopback_complete;
 289                        result = usb_ep_queue(ep, req, GFP_ATOMIC);
 290                        if (result)
 291                                ERROR(cdev, "%s queue req --> %d\n",
 292                                                ep->name, result);
 293                } else {
 294                        usb_ep_disable(ep);
 295                        ep->driver_data = NULL;
 296                        result = -ENOMEM;
 297                        goto fail0;
 298                }
 299        }
 300
 301        DBG(cdev, "%s enabled\n", loop->function.name);
 302        return result;
 303}
 304
 305static int loopback_set_alt(struct usb_function *f,
 306                unsigned intf, unsigned alt)
 307{
 308        struct f_loopback       *loop = func_to_loop(f);
 309        struct usb_composite_dev *cdev = f->config->cdev;
 310
 311        /* we know alt is zero */
 312        if (loop->in_ep->driver_data)
 313                disable_loopback(loop);
 314        return enable_loopback(cdev, loop);
 315}
 316
 317static void loopback_disable(struct usb_function *f)
 318{
 319        struct f_loopback       *loop = func_to_loop(f);
 320
 321        disable_loopback(loop);
 322}
 323
 324/*-------------------------------------------------------------------------*/
 325
 326static int __init loopback_bind_config(struct usb_configuration *c)
 327{
 328        struct f_loopback       *loop;
 329        int                     status;
 330
 331        loop = kzalloc(sizeof *loop, GFP_KERNEL);
 332        if (!loop)
 333                return -ENOMEM;
 334
 335        loop->function.name = "loopback";
 336        loop->function.descriptors = fs_loopback_descs;
 337        loop->function.bind = loopback_bind;
 338        loop->function.unbind = loopback_unbind;
 339        loop->function.set_alt = loopback_set_alt;
 340        loop->function.disable = loopback_disable;
 341
 342        status = usb_add_function(c, &loop->function);
 343        if (status)
 344                kfree(loop);
 345        return status;
 346}
 347
 348static struct usb_configuration loopback_driver = {
 349        .label          = "loopback",
 350        .strings        = loopback_strings,
 351        .bind           = loopback_bind_config,
 352        .bConfigurationValue = 2,
 353        .bmAttributes   = USB_CONFIG_ATT_SELFPOWER,
 354        /* .iConfiguration = DYNAMIC */
 355};
 356
 357/**
 358 * loopback_add - add a loopback testing configuration to a device
 359 * @cdev: the device to support the loopback configuration
 360 */
 361int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
 362{
 363        int id;
 364
 365        /* allocate string ID(s) */
 366        id = usb_string_id(cdev);
 367        if (id < 0)
 368                return id;
 369        strings_loopback[0].id = id;
 370
 371        loopback_intf.iInterface = id;
 372        loopback_driver.iConfiguration = id;
 373
 374        /* support autoresume for remote wakeup testing */
 375        if (autoresume)
 376                sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
 377
 378        /* support OTG systems */
 379        if (gadget_is_otg(cdev->gadget)) {
 380                loopback_driver.descriptors = otg_desc;
 381                loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
 382        }
 383
 384        return usb_add_config(cdev, &loopback_driver);
 385}
 386