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