1*00a2430fSAndrzej Pietrasiewicz /* 2*00a2430fSAndrzej Pietrasiewicz * f_loopback.c - USB peripheral loopback configuration driver 3*00a2430fSAndrzej Pietrasiewicz * 4*00a2430fSAndrzej Pietrasiewicz * Copyright (C) 2003-2008 David Brownell 5*00a2430fSAndrzej Pietrasiewicz * Copyright (C) 2008 by Nokia Corporation 6*00a2430fSAndrzej Pietrasiewicz * 7*00a2430fSAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 8*00a2430fSAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 9*00a2430fSAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 10*00a2430fSAndrzej Pietrasiewicz * (at your option) any later version. 11*00a2430fSAndrzej Pietrasiewicz */ 12*00a2430fSAndrzej Pietrasiewicz 13*00a2430fSAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */ 14*00a2430fSAndrzej Pietrasiewicz 15*00a2430fSAndrzej Pietrasiewicz #include <linux/slab.h> 16*00a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h> 17*00a2430fSAndrzej Pietrasiewicz #include <linux/device.h> 18*00a2430fSAndrzej Pietrasiewicz #include <linux/module.h> 19*00a2430fSAndrzej Pietrasiewicz #include <linux/err.h> 20*00a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h> 21*00a2430fSAndrzej Pietrasiewicz 22*00a2430fSAndrzej Pietrasiewicz #include "g_zero.h" 23*00a2430fSAndrzej Pietrasiewicz #include "u_f.h" 24*00a2430fSAndrzej Pietrasiewicz 25*00a2430fSAndrzej Pietrasiewicz /* 26*00a2430fSAndrzej Pietrasiewicz * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, 27*00a2430fSAndrzej Pietrasiewicz * 28*00a2430fSAndrzej Pietrasiewicz * This takes messages of various sizes written OUT to a device, and loops 29*00a2430fSAndrzej Pietrasiewicz * them back so they can be read IN from it. It has been used by certain 30*00a2430fSAndrzej Pietrasiewicz * test applications. It supports limited testing of data queueing logic. 31*00a2430fSAndrzej Pietrasiewicz * 32*00a2430fSAndrzej Pietrasiewicz * 33*00a2430fSAndrzej Pietrasiewicz * This is currently packaged as a configuration driver, which can't be 34*00a2430fSAndrzej Pietrasiewicz * combined with other functions to make composite devices. However, it 35*00a2430fSAndrzej Pietrasiewicz * can be combined with other independent configurations. 36*00a2430fSAndrzej Pietrasiewicz */ 37*00a2430fSAndrzej Pietrasiewicz struct f_loopback { 38*00a2430fSAndrzej Pietrasiewicz struct usb_function function; 39*00a2430fSAndrzej Pietrasiewicz 40*00a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep; 41*00a2430fSAndrzej Pietrasiewicz struct usb_ep *out_ep; 42*00a2430fSAndrzej Pietrasiewicz }; 43*00a2430fSAndrzej Pietrasiewicz 44*00a2430fSAndrzej Pietrasiewicz static inline struct f_loopback *func_to_loop(struct usb_function *f) 45*00a2430fSAndrzej Pietrasiewicz { 46*00a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_loopback, function); 47*00a2430fSAndrzej Pietrasiewicz } 48*00a2430fSAndrzej Pietrasiewicz 49*00a2430fSAndrzej Pietrasiewicz static unsigned qlen; 50*00a2430fSAndrzej Pietrasiewicz static unsigned buflen; 51*00a2430fSAndrzej Pietrasiewicz 52*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 53*00a2430fSAndrzej Pietrasiewicz 54*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor loopback_intf = { 55*00a2430fSAndrzej Pietrasiewicz .bLength = sizeof loopback_intf, 56*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 57*00a2430fSAndrzej Pietrasiewicz 58*00a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2, 59*00a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 60*00a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */ 61*00a2430fSAndrzej Pietrasiewicz }; 62*00a2430fSAndrzej Pietrasiewicz 63*00a2430fSAndrzej Pietrasiewicz /* full speed support: */ 64*00a2430fSAndrzej Pietrasiewicz 65*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_source_desc = { 66*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 67*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 68*00a2430fSAndrzej Pietrasiewicz 69*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 70*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 71*00a2430fSAndrzej Pietrasiewicz }; 72*00a2430fSAndrzej Pietrasiewicz 73*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_sink_desc = { 74*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 75*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 76*00a2430fSAndrzej Pietrasiewicz 77*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 78*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 79*00a2430fSAndrzej Pietrasiewicz }; 80*00a2430fSAndrzej Pietrasiewicz 81*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_loopback_descs[] = { 82*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 83*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &fs_loop_sink_desc, 84*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &fs_loop_source_desc, 85*00a2430fSAndrzej Pietrasiewicz NULL, 86*00a2430fSAndrzej Pietrasiewicz }; 87*00a2430fSAndrzej Pietrasiewicz 88*00a2430fSAndrzej Pietrasiewicz /* high speed support: */ 89*00a2430fSAndrzej Pietrasiewicz 90*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_source_desc = { 91*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 92*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 93*00a2430fSAndrzej Pietrasiewicz 94*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 95*00a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512), 96*00a2430fSAndrzej Pietrasiewicz }; 97*00a2430fSAndrzej Pietrasiewicz 98*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_sink_desc = { 99*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 100*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 101*00a2430fSAndrzej Pietrasiewicz 102*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 103*00a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512), 104*00a2430fSAndrzej Pietrasiewicz }; 105*00a2430fSAndrzej Pietrasiewicz 106*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_loopback_descs[] = { 107*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 108*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &hs_loop_source_desc, 109*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &hs_loop_sink_desc, 110*00a2430fSAndrzej Pietrasiewicz NULL, 111*00a2430fSAndrzej Pietrasiewicz }; 112*00a2430fSAndrzej Pietrasiewicz 113*00a2430fSAndrzej Pietrasiewicz /* super speed support: */ 114*00a2430fSAndrzej Pietrasiewicz 115*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_source_desc = { 116*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 117*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 118*00a2430fSAndrzej Pietrasiewicz 119*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 120*00a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024), 121*00a2430fSAndrzej Pietrasiewicz }; 122*00a2430fSAndrzej Pietrasiewicz 123*00a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { 124*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_SS_EP_COMP_SIZE, 125*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 126*00a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 127*00a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 128*00a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = 0, 129*00a2430fSAndrzej Pietrasiewicz }; 130*00a2430fSAndrzej Pietrasiewicz 131*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_sink_desc = { 132*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 133*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 134*00a2430fSAndrzej Pietrasiewicz 135*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 136*00a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024), 137*00a2430fSAndrzej Pietrasiewicz }; 138*00a2430fSAndrzej Pietrasiewicz 139*00a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { 140*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_SS_EP_COMP_SIZE, 141*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 142*00a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 143*00a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 144*00a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = 0, 145*00a2430fSAndrzej Pietrasiewicz }; 146*00a2430fSAndrzej Pietrasiewicz 147*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ss_loopback_descs[] = { 148*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 149*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_source_desc, 150*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_source_comp_desc, 151*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_sink_desc, 152*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_sink_comp_desc, 153*00a2430fSAndrzej Pietrasiewicz NULL, 154*00a2430fSAndrzej Pietrasiewicz }; 155*00a2430fSAndrzej Pietrasiewicz 156*00a2430fSAndrzej Pietrasiewicz /* function-specific strings: */ 157*00a2430fSAndrzej Pietrasiewicz 158*00a2430fSAndrzej Pietrasiewicz static struct usb_string strings_loopback[] = { 159*00a2430fSAndrzej Pietrasiewicz [0].s = "loop input to output", 160*00a2430fSAndrzej Pietrasiewicz { } /* end of list */ 161*00a2430fSAndrzej Pietrasiewicz }; 162*00a2430fSAndrzej Pietrasiewicz 163*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_loop = { 164*00a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-us */ 165*00a2430fSAndrzej Pietrasiewicz .strings = strings_loopback, 166*00a2430fSAndrzej Pietrasiewicz }; 167*00a2430fSAndrzej Pietrasiewicz 168*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *loopback_strings[] = { 169*00a2430fSAndrzej Pietrasiewicz &stringtab_loop, 170*00a2430fSAndrzej Pietrasiewicz NULL, 171*00a2430fSAndrzej Pietrasiewicz }; 172*00a2430fSAndrzej Pietrasiewicz 173*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 174*00a2430fSAndrzej Pietrasiewicz 175*00a2430fSAndrzej Pietrasiewicz static int loopback_bind(struct usb_configuration *c, struct usb_function *f) 176*00a2430fSAndrzej Pietrasiewicz { 177*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev; 178*00a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 179*00a2430fSAndrzej Pietrasiewicz int id; 180*00a2430fSAndrzej Pietrasiewicz int ret; 181*00a2430fSAndrzej Pietrasiewicz 182*00a2430fSAndrzej Pietrasiewicz /* allocate interface ID(s) */ 183*00a2430fSAndrzej Pietrasiewicz id = usb_interface_id(c, f); 184*00a2430fSAndrzej Pietrasiewicz if (id < 0) 185*00a2430fSAndrzej Pietrasiewicz return id; 186*00a2430fSAndrzej Pietrasiewicz loopback_intf.bInterfaceNumber = id; 187*00a2430fSAndrzej Pietrasiewicz 188*00a2430fSAndrzej Pietrasiewicz id = usb_string_id(cdev); 189*00a2430fSAndrzej Pietrasiewicz if (id < 0) 190*00a2430fSAndrzej Pietrasiewicz return id; 191*00a2430fSAndrzej Pietrasiewicz strings_loopback[0].id = id; 192*00a2430fSAndrzej Pietrasiewicz loopback_intf.iInterface = id; 193*00a2430fSAndrzej Pietrasiewicz 194*00a2430fSAndrzej Pietrasiewicz /* allocate endpoints */ 195*00a2430fSAndrzej Pietrasiewicz 196*00a2430fSAndrzej Pietrasiewicz loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc); 197*00a2430fSAndrzej Pietrasiewicz if (!loop->in_ep) { 198*00a2430fSAndrzej Pietrasiewicz autoconf_fail: 199*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s: can't autoconfigure on %s\n", 200*00a2430fSAndrzej Pietrasiewicz f->name, cdev->gadget->name); 201*00a2430fSAndrzej Pietrasiewicz return -ENODEV; 202*00a2430fSAndrzej Pietrasiewicz } 203*00a2430fSAndrzej Pietrasiewicz loop->in_ep->driver_data = cdev; /* claim */ 204*00a2430fSAndrzej Pietrasiewicz 205*00a2430fSAndrzej Pietrasiewicz loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc); 206*00a2430fSAndrzej Pietrasiewicz if (!loop->out_ep) 207*00a2430fSAndrzej Pietrasiewicz goto autoconf_fail; 208*00a2430fSAndrzej Pietrasiewicz loop->out_ep->driver_data = cdev; /* claim */ 209*00a2430fSAndrzej Pietrasiewicz 210*00a2430fSAndrzej Pietrasiewicz /* support high speed hardware */ 211*00a2430fSAndrzej Pietrasiewicz hs_loop_source_desc.bEndpointAddress = 212*00a2430fSAndrzej Pietrasiewicz fs_loop_source_desc.bEndpointAddress; 213*00a2430fSAndrzej Pietrasiewicz hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; 214*00a2430fSAndrzej Pietrasiewicz 215*00a2430fSAndrzej Pietrasiewicz /* support super speed hardware */ 216*00a2430fSAndrzej Pietrasiewicz ss_loop_source_desc.bEndpointAddress = 217*00a2430fSAndrzej Pietrasiewicz fs_loop_source_desc.bEndpointAddress; 218*00a2430fSAndrzej Pietrasiewicz ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; 219*00a2430fSAndrzej Pietrasiewicz 220*00a2430fSAndrzej Pietrasiewicz ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs, 221*00a2430fSAndrzej Pietrasiewicz ss_loopback_descs); 222*00a2430fSAndrzej Pietrasiewicz if (ret) 223*00a2430fSAndrzej Pietrasiewicz return ret; 224*00a2430fSAndrzej Pietrasiewicz 225*00a2430fSAndrzej Pietrasiewicz DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", 226*00a2430fSAndrzej Pietrasiewicz (gadget_is_superspeed(c->cdev->gadget) ? "super" : 227*00a2430fSAndrzej Pietrasiewicz (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), 228*00a2430fSAndrzej Pietrasiewicz f->name, loop->in_ep->name, loop->out_ep->name); 229*00a2430fSAndrzej Pietrasiewicz return 0; 230*00a2430fSAndrzej Pietrasiewicz } 231*00a2430fSAndrzej Pietrasiewicz 232*00a2430fSAndrzej Pietrasiewicz static void lb_free_func(struct usb_function *f) 233*00a2430fSAndrzej Pietrasiewicz { 234*00a2430fSAndrzej Pietrasiewicz struct f_lb_opts *opts; 235*00a2430fSAndrzej Pietrasiewicz 236*00a2430fSAndrzej Pietrasiewicz opts = container_of(f->fi, struct f_lb_opts, func_inst); 237*00a2430fSAndrzej Pietrasiewicz 238*00a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 239*00a2430fSAndrzej Pietrasiewicz opts->refcnt--; 240*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 241*00a2430fSAndrzej Pietrasiewicz 242*00a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 243*00a2430fSAndrzej Pietrasiewicz kfree(func_to_loop(f)); 244*00a2430fSAndrzej Pietrasiewicz } 245*00a2430fSAndrzej Pietrasiewicz 246*00a2430fSAndrzej Pietrasiewicz static void loopback_complete(struct usb_ep *ep, struct usb_request *req) 247*00a2430fSAndrzej Pietrasiewicz { 248*00a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = ep->driver_data; 249*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = loop->function.config->cdev; 250*00a2430fSAndrzej Pietrasiewicz int status = req->status; 251*00a2430fSAndrzej Pietrasiewicz 252*00a2430fSAndrzej Pietrasiewicz switch (status) { 253*00a2430fSAndrzej Pietrasiewicz 254*00a2430fSAndrzej Pietrasiewicz case 0: /* normal completion? */ 255*00a2430fSAndrzej Pietrasiewicz if (ep == loop->out_ep) { 256*00a2430fSAndrzej Pietrasiewicz /* loop this OUT packet back IN to the host */ 257*00a2430fSAndrzej Pietrasiewicz req->zero = (req->actual < req->length); 258*00a2430fSAndrzej Pietrasiewicz req->length = req->actual; 259*00a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); 260*00a2430fSAndrzej Pietrasiewicz if (status == 0) 261*00a2430fSAndrzej Pietrasiewicz return; 262*00a2430fSAndrzej Pietrasiewicz 263*00a2430fSAndrzej Pietrasiewicz /* "should never get here" */ 264*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "can't loop %s to %s: %d\n", 265*00a2430fSAndrzej Pietrasiewicz ep->name, loop->in_ep->name, 266*00a2430fSAndrzej Pietrasiewicz status); 267*00a2430fSAndrzej Pietrasiewicz } 268*00a2430fSAndrzej Pietrasiewicz 269*00a2430fSAndrzej Pietrasiewicz /* queue the buffer for some later OUT packet */ 270*00a2430fSAndrzej Pietrasiewicz req->length = buflen; 271*00a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); 272*00a2430fSAndrzej Pietrasiewicz if (status == 0) 273*00a2430fSAndrzej Pietrasiewicz return; 274*00a2430fSAndrzej Pietrasiewicz 275*00a2430fSAndrzej Pietrasiewicz /* "should never get here" */ 276*00a2430fSAndrzej Pietrasiewicz /* FALLTHROUGH */ 277*00a2430fSAndrzej Pietrasiewicz 278*00a2430fSAndrzej Pietrasiewicz default: 279*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, 280*00a2430fSAndrzej Pietrasiewicz status, req->actual, req->length); 281*00a2430fSAndrzej Pietrasiewicz /* FALLTHROUGH */ 282*00a2430fSAndrzej Pietrasiewicz 283*00a2430fSAndrzej Pietrasiewicz /* NOTE: since this driver doesn't maintain an explicit record 284*00a2430fSAndrzej Pietrasiewicz * of requests it submitted (just maintains qlen count), we 285*00a2430fSAndrzej Pietrasiewicz * rely on the hardware driver to clean up on disconnect or 286*00a2430fSAndrzej Pietrasiewicz * endpoint disable. 287*00a2430fSAndrzej Pietrasiewicz */ 288*00a2430fSAndrzej Pietrasiewicz case -ECONNABORTED: /* hardware forced ep reset */ 289*00a2430fSAndrzej Pietrasiewicz case -ECONNRESET: /* request dequeued */ 290*00a2430fSAndrzej Pietrasiewicz case -ESHUTDOWN: /* disconnect from host */ 291*00a2430fSAndrzej Pietrasiewicz free_ep_req(ep, req); 292*00a2430fSAndrzej Pietrasiewicz return; 293*00a2430fSAndrzej Pietrasiewicz } 294*00a2430fSAndrzej Pietrasiewicz } 295*00a2430fSAndrzej Pietrasiewicz 296*00a2430fSAndrzej Pietrasiewicz static void disable_loopback(struct f_loopback *loop) 297*00a2430fSAndrzej Pietrasiewicz { 298*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev; 299*00a2430fSAndrzej Pietrasiewicz 300*00a2430fSAndrzej Pietrasiewicz cdev = loop->function.config->cdev; 301*00a2430fSAndrzej Pietrasiewicz disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL); 302*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "%s disabled\n", loop->function.name); 303*00a2430fSAndrzej Pietrasiewicz } 304*00a2430fSAndrzej Pietrasiewicz 305*00a2430fSAndrzej Pietrasiewicz static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) 306*00a2430fSAndrzej Pietrasiewicz { 307*00a2430fSAndrzej Pietrasiewicz return alloc_ep_req(ep, len, buflen); 308*00a2430fSAndrzej Pietrasiewicz } 309*00a2430fSAndrzej Pietrasiewicz 310*00a2430fSAndrzej Pietrasiewicz static int 311*00a2430fSAndrzej Pietrasiewicz enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) 312*00a2430fSAndrzej Pietrasiewicz { 313*00a2430fSAndrzej Pietrasiewicz int result = 0; 314*00a2430fSAndrzej Pietrasiewicz struct usb_ep *ep; 315*00a2430fSAndrzej Pietrasiewicz struct usb_request *req; 316*00a2430fSAndrzej Pietrasiewicz unsigned i; 317*00a2430fSAndrzej Pietrasiewicz 318*00a2430fSAndrzej Pietrasiewicz /* one endpoint writes data back IN to the host */ 319*00a2430fSAndrzej Pietrasiewicz ep = loop->in_ep; 320*00a2430fSAndrzej Pietrasiewicz result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 321*00a2430fSAndrzej Pietrasiewicz if (result) 322*00a2430fSAndrzej Pietrasiewicz return result; 323*00a2430fSAndrzej Pietrasiewicz result = usb_ep_enable(ep); 324*00a2430fSAndrzej Pietrasiewicz if (result < 0) 325*00a2430fSAndrzej Pietrasiewicz return result; 326*00a2430fSAndrzej Pietrasiewicz ep->driver_data = loop; 327*00a2430fSAndrzej Pietrasiewicz 328*00a2430fSAndrzej Pietrasiewicz /* one endpoint just reads OUT packets */ 329*00a2430fSAndrzej Pietrasiewicz ep = loop->out_ep; 330*00a2430fSAndrzej Pietrasiewicz result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 331*00a2430fSAndrzej Pietrasiewicz if (result) 332*00a2430fSAndrzej Pietrasiewicz goto fail0; 333*00a2430fSAndrzej Pietrasiewicz 334*00a2430fSAndrzej Pietrasiewicz result = usb_ep_enable(ep); 335*00a2430fSAndrzej Pietrasiewicz if (result < 0) { 336*00a2430fSAndrzej Pietrasiewicz fail0: 337*00a2430fSAndrzej Pietrasiewicz ep = loop->in_ep; 338*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(ep); 339*00a2430fSAndrzej Pietrasiewicz ep->driver_data = NULL; 340*00a2430fSAndrzej Pietrasiewicz return result; 341*00a2430fSAndrzej Pietrasiewicz } 342*00a2430fSAndrzej Pietrasiewicz ep->driver_data = loop; 343*00a2430fSAndrzej Pietrasiewicz 344*00a2430fSAndrzej Pietrasiewicz /* allocate a bunch of read buffers and queue them all at once. 345*00a2430fSAndrzej Pietrasiewicz * we buffer at most 'qlen' transfers; fewer if any need more 346*00a2430fSAndrzej Pietrasiewicz * than 'buflen' bytes each. 347*00a2430fSAndrzej Pietrasiewicz */ 348*00a2430fSAndrzej Pietrasiewicz for (i = 0; i < qlen && result == 0; i++) { 349*00a2430fSAndrzej Pietrasiewicz req = lb_alloc_ep_req(ep, 0); 350*00a2430fSAndrzej Pietrasiewicz if (req) { 351*00a2430fSAndrzej Pietrasiewicz req->complete = loopback_complete; 352*00a2430fSAndrzej Pietrasiewicz result = usb_ep_queue(ep, req, GFP_ATOMIC); 353*00a2430fSAndrzej Pietrasiewicz if (result) 354*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s queue req --> %d\n", 355*00a2430fSAndrzej Pietrasiewicz ep->name, result); 356*00a2430fSAndrzej Pietrasiewicz } else { 357*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(ep); 358*00a2430fSAndrzej Pietrasiewicz ep->driver_data = NULL; 359*00a2430fSAndrzej Pietrasiewicz result = -ENOMEM; 360*00a2430fSAndrzej Pietrasiewicz goto fail0; 361*00a2430fSAndrzej Pietrasiewicz } 362*00a2430fSAndrzej Pietrasiewicz } 363*00a2430fSAndrzej Pietrasiewicz 364*00a2430fSAndrzej Pietrasiewicz DBG(cdev, "%s enabled\n", loop->function.name); 365*00a2430fSAndrzej Pietrasiewicz return result; 366*00a2430fSAndrzej Pietrasiewicz } 367*00a2430fSAndrzej Pietrasiewicz 368*00a2430fSAndrzej Pietrasiewicz static int loopback_set_alt(struct usb_function *f, 369*00a2430fSAndrzej Pietrasiewicz unsigned intf, unsigned alt) 370*00a2430fSAndrzej Pietrasiewicz { 371*00a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 372*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 373*00a2430fSAndrzej Pietrasiewicz 374*00a2430fSAndrzej Pietrasiewicz /* we know alt is zero */ 375*00a2430fSAndrzej Pietrasiewicz if (loop->in_ep->driver_data) 376*00a2430fSAndrzej Pietrasiewicz disable_loopback(loop); 377*00a2430fSAndrzej Pietrasiewicz return enable_loopback(cdev, loop); 378*00a2430fSAndrzej Pietrasiewicz } 379*00a2430fSAndrzej Pietrasiewicz 380*00a2430fSAndrzej Pietrasiewicz static void loopback_disable(struct usb_function *f) 381*00a2430fSAndrzej Pietrasiewicz { 382*00a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 383*00a2430fSAndrzej Pietrasiewicz 384*00a2430fSAndrzej Pietrasiewicz disable_loopback(loop); 385*00a2430fSAndrzej Pietrasiewicz } 386*00a2430fSAndrzej Pietrasiewicz 387*00a2430fSAndrzej Pietrasiewicz static struct usb_function *loopback_alloc(struct usb_function_instance *fi) 388*00a2430fSAndrzej Pietrasiewicz { 389*00a2430fSAndrzej Pietrasiewicz struct f_loopback *loop; 390*00a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 391*00a2430fSAndrzej Pietrasiewicz 392*00a2430fSAndrzej Pietrasiewicz loop = kzalloc(sizeof *loop, GFP_KERNEL); 393*00a2430fSAndrzej Pietrasiewicz if (!loop) 394*00a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 395*00a2430fSAndrzej Pietrasiewicz 396*00a2430fSAndrzej Pietrasiewicz lb_opts = container_of(fi, struct f_lb_opts, func_inst); 397*00a2430fSAndrzej Pietrasiewicz 398*00a2430fSAndrzej Pietrasiewicz mutex_lock(&lb_opts->lock); 399*00a2430fSAndrzej Pietrasiewicz lb_opts->refcnt++; 400*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&lb_opts->lock); 401*00a2430fSAndrzej Pietrasiewicz 402*00a2430fSAndrzej Pietrasiewicz buflen = lb_opts->bulk_buflen; 403*00a2430fSAndrzej Pietrasiewicz qlen = lb_opts->qlen; 404*00a2430fSAndrzej Pietrasiewicz if (!qlen) 405*00a2430fSAndrzej Pietrasiewicz qlen = 32; 406*00a2430fSAndrzej Pietrasiewicz 407*00a2430fSAndrzej Pietrasiewicz loop->function.name = "loopback"; 408*00a2430fSAndrzej Pietrasiewicz loop->function.bind = loopback_bind; 409*00a2430fSAndrzej Pietrasiewicz loop->function.set_alt = loopback_set_alt; 410*00a2430fSAndrzej Pietrasiewicz loop->function.disable = loopback_disable; 411*00a2430fSAndrzej Pietrasiewicz loop->function.strings = loopback_strings; 412*00a2430fSAndrzej Pietrasiewicz 413*00a2430fSAndrzej Pietrasiewicz loop->function.free_func = lb_free_func; 414*00a2430fSAndrzej Pietrasiewicz 415*00a2430fSAndrzej Pietrasiewicz return &loop->function; 416*00a2430fSAndrzej Pietrasiewicz } 417*00a2430fSAndrzej Pietrasiewicz 418*00a2430fSAndrzej Pietrasiewicz static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item) 419*00a2430fSAndrzej Pietrasiewicz { 420*00a2430fSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_lb_opts, 421*00a2430fSAndrzej Pietrasiewicz func_inst.group); 422*00a2430fSAndrzej Pietrasiewicz } 423*00a2430fSAndrzej Pietrasiewicz 424*00a2430fSAndrzej Pietrasiewicz CONFIGFS_ATTR_STRUCT(f_lb_opts); 425*00a2430fSAndrzej Pietrasiewicz CONFIGFS_ATTR_OPS(f_lb_opts); 426*00a2430fSAndrzej Pietrasiewicz 427*00a2430fSAndrzej Pietrasiewicz static void lb_attr_release(struct config_item *item) 428*00a2430fSAndrzej Pietrasiewicz { 429*00a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts = to_f_lb_opts(item); 430*00a2430fSAndrzej Pietrasiewicz 431*00a2430fSAndrzej Pietrasiewicz usb_put_function_instance(&lb_opts->func_inst); 432*00a2430fSAndrzej Pietrasiewicz } 433*00a2430fSAndrzej Pietrasiewicz 434*00a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations lb_item_ops = { 435*00a2430fSAndrzej Pietrasiewicz .release = lb_attr_release, 436*00a2430fSAndrzej Pietrasiewicz .show_attribute = f_lb_opts_attr_show, 437*00a2430fSAndrzej Pietrasiewicz .store_attribute = f_lb_opts_attr_store, 438*00a2430fSAndrzej Pietrasiewicz }; 439*00a2430fSAndrzej Pietrasiewicz 440*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page) 441*00a2430fSAndrzej Pietrasiewicz { 442*00a2430fSAndrzej Pietrasiewicz int result; 443*00a2430fSAndrzej Pietrasiewicz 444*00a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 445*00a2430fSAndrzej Pietrasiewicz result = sprintf(page, "%d", opts->qlen); 446*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 447*00a2430fSAndrzej Pietrasiewicz 448*00a2430fSAndrzej Pietrasiewicz return result; 449*00a2430fSAndrzej Pietrasiewicz } 450*00a2430fSAndrzej Pietrasiewicz 451*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts, 452*00a2430fSAndrzej Pietrasiewicz const char *page, size_t len) 453*00a2430fSAndrzej Pietrasiewicz { 454*00a2430fSAndrzej Pietrasiewicz int ret; 455*00a2430fSAndrzej Pietrasiewicz u32 num; 456*00a2430fSAndrzej Pietrasiewicz 457*00a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 458*00a2430fSAndrzej Pietrasiewicz if (opts->refcnt) { 459*00a2430fSAndrzej Pietrasiewicz ret = -EBUSY; 460*00a2430fSAndrzej Pietrasiewicz goto end; 461*00a2430fSAndrzej Pietrasiewicz } 462*00a2430fSAndrzej Pietrasiewicz 463*00a2430fSAndrzej Pietrasiewicz ret = kstrtou32(page, 0, &num); 464*00a2430fSAndrzej Pietrasiewicz if (ret) 465*00a2430fSAndrzej Pietrasiewicz goto end; 466*00a2430fSAndrzej Pietrasiewicz 467*00a2430fSAndrzej Pietrasiewicz opts->qlen = num; 468*00a2430fSAndrzej Pietrasiewicz ret = len; 469*00a2430fSAndrzej Pietrasiewicz end: 470*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 471*00a2430fSAndrzej Pietrasiewicz return ret; 472*00a2430fSAndrzej Pietrasiewicz } 473*00a2430fSAndrzej Pietrasiewicz 474*00a2430fSAndrzej Pietrasiewicz static struct f_lb_opts_attribute f_lb_opts_qlen = 475*00a2430fSAndrzej Pietrasiewicz __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR, 476*00a2430fSAndrzej Pietrasiewicz f_lb_opts_qlen_show, 477*00a2430fSAndrzej Pietrasiewicz f_lb_opts_qlen_store); 478*00a2430fSAndrzej Pietrasiewicz 479*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page) 480*00a2430fSAndrzej Pietrasiewicz { 481*00a2430fSAndrzej Pietrasiewicz int result; 482*00a2430fSAndrzej Pietrasiewicz 483*00a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 484*00a2430fSAndrzej Pietrasiewicz result = sprintf(page, "%d", opts->bulk_buflen); 485*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 486*00a2430fSAndrzej Pietrasiewicz 487*00a2430fSAndrzej Pietrasiewicz return result; 488*00a2430fSAndrzej Pietrasiewicz } 489*00a2430fSAndrzej Pietrasiewicz 490*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts, 491*00a2430fSAndrzej Pietrasiewicz const char *page, size_t len) 492*00a2430fSAndrzej Pietrasiewicz { 493*00a2430fSAndrzej Pietrasiewicz int ret; 494*00a2430fSAndrzej Pietrasiewicz u32 num; 495*00a2430fSAndrzej Pietrasiewicz 496*00a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 497*00a2430fSAndrzej Pietrasiewicz if (opts->refcnt) { 498*00a2430fSAndrzej Pietrasiewicz ret = -EBUSY; 499*00a2430fSAndrzej Pietrasiewicz goto end; 500*00a2430fSAndrzej Pietrasiewicz } 501*00a2430fSAndrzej Pietrasiewicz 502*00a2430fSAndrzej Pietrasiewicz ret = kstrtou32(page, 0, &num); 503*00a2430fSAndrzej Pietrasiewicz if (ret) 504*00a2430fSAndrzej Pietrasiewicz goto end; 505*00a2430fSAndrzej Pietrasiewicz 506*00a2430fSAndrzej Pietrasiewicz opts->bulk_buflen = num; 507*00a2430fSAndrzej Pietrasiewicz ret = len; 508*00a2430fSAndrzej Pietrasiewicz end: 509*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 510*00a2430fSAndrzej Pietrasiewicz return ret; 511*00a2430fSAndrzej Pietrasiewicz } 512*00a2430fSAndrzej Pietrasiewicz 513*00a2430fSAndrzej Pietrasiewicz static struct f_lb_opts_attribute f_lb_opts_bulk_buflen = 514*00a2430fSAndrzej Pietrasiewicz __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, 515*00a2430fSAndrzej Pietrasiewicz f_lb_opts_bulk_buflen_show, 516*00a2430fSAndrzej Pietrasiewicz f_lb_opts_bulk_buflen_store); 517*00a2430fSAndrzej Pietrasiewicz 518*00a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *lb_attrs[] = { 519*00a2430fSAndrzej Pietrasiewicz &f_lb_opts_qlen.attr, 520*00a2430fSAndrzej Pietrasiewicz &f_lb_opts_bulk_buflen.attr, 521*00a2430fSAndrzej Pietrasiewicz NULL, 522*00a2430fSAndrzej Pietrasiewicz }; 523*00a2430fSAndrzej Pietrasiewicz 524*00a2430fSAndrzej Pietrasiewicz static struct config_item_type lb_func_type = { 525*00a2430fSAndrzej Pietrasiewicz .ct_item_ops = &lb_item_ops, 526*00a2430fSAndrzej Pietrasiewicz .ct_attrs = lb_attrs, 527*00a2430fSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 528*00a2430fSAndrzej Pietrasiewicz }; 529*00a2430fSAndrzej Pietrasiewicz 530*00a2430fSAndrzej Pietrasiewicz static void lb_free_instance(struct usb_function_instance *fi) 531*00a2430fSAndrzej Pietrasiewicz { 532*00a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 533*00a2430fSAndrzej Pietrasiewicz 534*00a2430fSAndrzej Pietrasiewicz lb_opts = container_of(fi, struct f_lb_opts, func_inst); 535*00a2430fSAndrzej Pietrasiewicz kfree(lb_opts); 536*00a2430fSAndrzej Pietrasiewicz } 537*00a2430fSAndrzej Pietrasiewicz 538*00a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *loopback_alloc_instance(void) 539*00a2430fSAndrzej Pietrasiewicz { 540*00a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 541*00a2430fSAndrzej Pietrasiewicz 542*00a2430fSAndrzej Pietrasiewicz lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL); 543*00a2430fSAndrzej Pietrasiewicz if (!lb_opts) 544*00a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 545*00a2430fSAndrzej Pietrasiewicz mutex_init(&lb_opts->lock); 546*00a2430fSAndrzej Pietrasiewicz lb_opts->func_inst.free_func_inst = lb_free_instance; 547*00a2430fSAndrzej Pietrasiewicz lb_opts->bulk_buflen = GZERO_BULK_BUFLEN; 548*00a2430fSAndrzej Pietrasiewicz lb_opts->qlen = GZERO_QLEN; 549*00a2430fSAndrzej Pietrasiewicz 550*00a2430fSAndrzej Pietrasiewicz config_group_init_type_name(&lb_opts->func_inst.group, "", 551*00a2430fSAndrzej Pietrasiewicz &lb_func_type); 552*00a2430fSAndrzej Pietrasiewicz 553*00a2430fSAndrzej Pietrasiewicz return &lb_opts->func_inst; 554*00a2430fSAndrzej Pietrasiewicz } 555*00a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc); 556*00a2430fSAndrzej Pietrasiewicz 557*00a2430fSAndrzej Pietrasiewicz int __init lb_modinit(void) 558*00a2430fSAndrzej Pietrasiewicz { 559*00a2430fSAndrzej Pietrasiewicz int ret; 560*00a2430fSAndrzej Pietrasiewicz 561*00a2430fSAndrzej Pietrasiewicz ret = usb_function_register(&Loopbackusb_func); 562*00a2430fSAndrzej Pietrasiewicz if (ret) 563*00a2430fSAndrzej Pietrasiewicz return ret; 564*00a2430fSAndrzej Pietrasiewicz return ret; 565*00a2430fSAndrzej Pietrasiewicz } 566*00a2430fSAndrzej Pietrasiewicz void __exit lb_modexit(void) 567*00a2430fSAndrzej Pietrasiewicz { 568*00a2430fSAndrzej Pietrasiewicz usb_function_unregister(&Loopbackusb_func); 569*00a2430fSAndrzej Pietrasiewicz } 570*00a2430fSAndrzej Pietrasiewicz 571*00a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 572