100a2430fSAndrzej Pietrasiewicz /* 200a2430fSAndrzej Pietrasiewicz * f_loopback.c - USB peripheral loopback configuration driver 300a2430fSAndrzej Pietrasiewicz * 400a2430fSAndrzej Pietrasiewicz * Copyright (C) 2003-2008 David Brownell 500a2430fSAndrzej Pietrasiewicz * Copyright (C) 2008 by Nokia Corporation 600a2430fSAndrzej Pietrasiewicz * 700a2430fSAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 800a2430fSAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 900a2430fSAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 1000a2430fSAndrzej Pietrasiewicz * (at your option) any later version. 1100a2430fSAndrzej Pietrasiewicz */ 1200a2430fSAndrzej Pietrasiewicz 1300a2430fSAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */ 1400a2430fSAndrzej Pietrasiewicz 1500a2430fSAndrzej Pietrasiewicz #include <linux/slab.h> 1600a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h> 1700a2430fSAndrzej Pietrasiewicz #include <linux/device.h> 1800a2430fSAndrzej Pietrasiewicz #include <linux/module.h> 1900a2430fSAndrzej Pietrasiewicz #include <linux/err.h> 2000a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h> 2100a2430fSAndrzej Pietrasiewicz 2200a2430fSAndrzej Pietrasiewicz #include "g_zero.h" 2300a2430fSAndrzej Pietrasiewicz #include "u_f.h" 2400a2430fSAndrzej Pietrasiewicz 2500a2430fSAndrzej Pietrasiewicz /* 2600a2430fSAndrzej Pietrasiewicz * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, 2700a2430fSAndrzej Pietrasiewicz * 2800a2430fSAndrzej Pietrasiewicz * This takes messages of various sizes written OUT to a device, and loops 2900a2430fSAndrzej Pietrasiewicz * them back so they can be read IN from it. It has been used by certain 3000a2430fSAndrzej Pietrasiewicz * test applications. It supports limited testing of data queueing logic. 3100a2430fSAndrzej Pietrasiewicz * 3200a2430fSAndrzej Pietrasiewicz * 3300a2430fSAndrzej Pietrasiewicz * This is currently packaged as a configuration driver, which can't be 3400a2430fSAndrzej Pietrasiewicz * combined with other functions to make composite devices. However, it 3500a2430fSAndrzej Pietrasiewicz * can be combined with other independent configurations. 3600a2430fSAndrzej Pietrasiewicz */ 3700a2430fSAndrzej Pietrasiewicz struct f_loopback { 3800a2430fSAndrzej Pietrasiewicz struct usb_function function; 3900a2430fSAndrzej Pietrasiewicz 4000a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep; 4100a2430fSAndrzej Pietrasiewicz struct usb_ep *out_ep; 4200a2430fSAndrzej Pietrasiewicz }; 4300a2430fSAndrzej Pietrasiewicz 4400a2430fSAndrzej Pietrasiewicz static inline struct f_loopback *func_to_loop(struct usb_function *f) 4500a2430fSAndrzej Pietrasiewicz { 4600a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_loopback, function); 4700a2430fSAndrzej Pietrasiewicz } 4800a2430fSAndrzej Pietrasiewicz 4900a2430fSAndrzej Pietrasiewicz static unsigned qlen; 5000a2430fSAndrzej Pietrasiewicz static unsigned buflen; 5100a2430fSAndrzej Pietrasiewicz 5200a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 5300a2430fSAndrzej Pietrasiewicz 5400a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor loopback_intf = { 5500a2430fSAndrzej Pietrasiewicz .bLength = sizeof loopback_intf, 5600a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 5700a2430fSAndrzej Pietrasiewicz 5800a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2, 5900a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 6000a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */ 6100a2430fSAndrzej Pietrasiewicz }; 6200a2430fSAndrzej Pietrasiewicz 6300a2430fSAndrzej Pietrasiewicz /* full speed support: */ 6400a2430fSAndrzej Pietrasiewicz 6500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_source_desc = { 6600a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 6700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 6800a2430fSAndrzej Pietrasiewicz 6900a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 7000a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 7100a2430fSAndrzej Pietrasiewicz }; 7200a2430fSAndrzej Pietrasiewicz 7300a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_sink_desc = { 7400a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 7500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 7600a2430fSAndrzej Pietrasiewicz 7700a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 7800a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 7900a2430fSAndrzej Pietrasiewicz }; 8000a2430fSAndrzej Pietrasiewicz 8100a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_loopback_descs[] = { 8200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 8300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &fs_loop_sink_desc, 8400a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &fs_loop_source_desc, 8500a2430fSAndrzej Pietrasiewicz NULL, 8600a2430fSAndrzej Pietrasiewicz }; 8700a2430fSAndrzej Pietrasiewicz 8800a2430fSAndrzej Pietrasiewicz /* high speed support: */ 8900a2430fSAndrzej Pietrasiewicz 9000a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_source_desc = { 9100a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 9200a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 9300a2430fSAndrzej Pietrasiewicz 9400a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 9500a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512), 9600a2430fSAndrzej Pietrasiewicz }; 9700a2430fSAndrzej Pietrasiewicz 9800a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_sink_desc = { 9900a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 10000a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 10100a2430fSAndrzej Pietrasiewicz 10200a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 10300a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512), 10400a2430fSAndrzej Pietrasiewicz }; 10500a2430fSAndrzej Pietrasiewicz 10600a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_loopback_descs[] = { 10700a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 10800a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &hs_loop_source_desc, 10900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &hs_loop_sink_desc, 11000a2430fSAndrzej Pietrasiewicz NULL, 11100a2430fSAndrzej Pietrasiewicz }; 11200a2430fSAndrzej Pietrasiewicz 11300a2430fSAndrzej Pietrasiewicz /* super speed support: */ 11400a2430fSAndrzej Pietrasiewicz 11500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_source_desc = { 11600a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 11700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 11800a2430fSAndrzej Pietrasiewicz 11900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 12000a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024), 12100a2430fSAndrzej Pietrasiewicz }; 12200a2430fSAndrzej Pietrasiewicz 12300a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { 12400a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_SS_EP_COMP_SIZE, 12500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 12600a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 12700a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 12800a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = 0, 12900a2430fSAndrzej Pietrasiewicz }; 13000a2430fSAndrzej Pietrasiewicz 13100a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_sink_desc = { 13200a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 13300a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 13400a2430fSAndrzej Pietrasiewicz 13500a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 13600a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024), 13700a2430fSAndrzej Pietrasiewicz }; 13800a2430fSAndrzej Pietrasiewicz 13900a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { 14000a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_SS_EP_COMP_SIZE, 14100a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 14200a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 14300a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 14400a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = 0, 14500a2430fSAndrzej Pietrasiewicz }; 14600a2430fSAndrzej Pietrasiewicz 14700a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ss_loopback_descs[] = { 14800a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 14900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_source_desc, 15000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_source_comp_desc, 15100a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_sink_desc, 15200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_sink_comp_desc, 15300a2430fSAndrzej Pietrasiewicz NULL, 15400a2430fSAndrzej Pietrasiewicz }; 15500a2430fSAndrzej Pietrasiewicz 15600a2430fSAndrzej Pietrasiewicz /* function-specific strings: */ 15700a2430fSAndrzej Pietrasiewicz 15800a2430fSAndrzej Pietrasiewicz static struct usb_string strings_loopback[] = { 15900a2430fSAndrzej Pietrasiewicz [0].s = "loop input to output", 16000a2430fSAndrzej Pietrasiewicz { } /* end of list */ 16100a2430fSAndrzej Pietrasiewicz }; 16200a2430fSAndrzej Pietrasiewicz 16300a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_loop = { 16400a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-us */ 16500a2430fSAndrzej Pietrasiewicz .strings = strings_loopback, 16600a2430fSAndrzej Pietrasiewicz }; 16700a2430fSAndrzej Pietrasiewicz 16800a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *loopback_strings[] = { 16900a2430fSAndrzej Pietrasiewicz &stringtab_loop, 17000a2430fSAndrzej Pietrasiewicz NULL, 17100a2430fSAndrzej Pietrasiewicz }; 17200a2430fSAndrzej Pietrasiewicz 17300a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 17400a2430fSAndrzej Pietrasiewicz 17500a2430fSAndrzej Pietrasiewicz static int loopback_bind(struct usb_configuration *c, struct usb_function *f) 17600a2430fSAndrzej Pietrasiewicz { 17700a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev; 17800a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 17900a2430fSAndrzej Pietrasiewicz int id; 18000a2430fSAndrzej Pietrasiewicz int ret; 18100a2430fSAndrzej Pietrasiewicz 18200a2430fSAndrzej Pietrasiewicz /* allocate interface ID(s) */ 18300a2430fSAndrzej Pietrasiewicz id = usb_interface_id(c, f); 18400a2430fSAndrzej Pietrasiewicz if (id < 0) 18500a2430fSAndrzej Pietrasiewicz return id; 18600a2430fSAndrzej Pietrasiewicz loopback_intf.bInterfaceNumber = id; 18700a2430fSAndrzej Pietrasiewicz 18800a2430fSAndrzej Pietrasiewicz id = usb_string_id(cdev); 18900a2430fSAndrzej Pietrasiewicz if (id < 0) 19000a2430fSAndrzej Pietrasiewicz return id; 19100a2430fSAndrzej Pietrasiewicz strings_loopback[0].id = id; 19200a2430fSAndrzej Pietrasiewicz loopback_intf.iInterface = id; 19300a2430fSAndrzej Pietrasiewicz 19400a2430fSAndrzej Pietrasiewicz /* allocate endpoints */ 19500a2430fSAndrzej Pietrasiewicz 19600a2430fSAndrzej Pietrasiewicz loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc); 19700a2430fSAndrzej Pietrasiewicz if (!loop->in_ep) { 19800a2430fSAndrzej Pietrasiewicz autoconf_fail: 19900a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s: can't autoconfigure on %s\n", 20000a2430fSAndrzej Pietrasiewicz f->name, cdev->gadget->name); 20100a2430fSAndrzej Pietrasiewicz return -ENODEV; 20200a2430fSAndrzej Pietrasiewicz } 20300a2430fSAndrzej Pietrasiewicz loop->in_ep->driver_data = cdev; /* claim */ 20400a2430fSAndrzej Pietrasiewicz 20500a2430fSAndrzej Pietrasiewicz loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc); 20600a2430fSAndrzej Pietrasiewicz if (!loop->out_ep) 20700a2430fSAndrzej Pietrasiewicz goto autoconf_fail; 20800a2430fSAndrzej Pietrasiewicz loop->out_ep->driver_data = cdev; /* claim */ 20900a2430fSAndrzej Pietrasiewicz 21000a2430fSAndrzej Pietrasiewicz /* support high speed hardware */ 21100a2430fSAndrzej Pietrasiewicz hs_loop_source_desc.bEndpointAddress = 21200a2430fSAndrzej Pietrasiewicz fs_loop_source_desc.bEndpointAddress; 21300a2430fSAndrzej Pietrasiewicz hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; 21400a2430fSAndrzej Pietrasiewicz 21500a2430fSAndrzej Pietrasiewicz /* support super speed hardware */ 21600a2430fSAndrzej Pietrasiewicz ss_loop_source_desc.bEndpointAddress = 21700a2430fSAndrzej Pietrasiewicz fs_loop_source_desc.bEndpointAddress; 21800a2430fSAndrzej Pietrasiewicz ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; 21900a2430fSAndrzej Pietrasiewicz 22000a2430fSAndrzej Pietrasiewicz ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs, 22100a2430fSAndrzej Pietrasiewicz ss_loopback_descs); 22200a2430fSAndrzej Pietrasiewicz if (ret) 22300a2430fSAndrzej Pietrasiewicz return ret; 22400a2430fSAndrzej Pietrasiewicz 22500a2430fSAndrzej Pietrasiewicz DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", 22600a2430fSAndrzej Pietrasiewicz (gadget_is_superspeed(c->cdev->gadget) ? "super" : 22700a2430fSAndrzej Pietrasiewicz (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), 22800a2430fSAndrzej Pietrasiewicz f->name, loop->in_ep->name, loop->out_ep->name); 22900a2430fSAndrzej Pietrasiewicz return 0; 23000a2430fSAndrzej Pietrasiewicz } 23100a2430fSAndrzej Pietrasiewicz 23200a2430fSAndrzej Pietrasiewicz static void lb_free_func(struct usb_function *f) 23300a2430fSAndrzej Pietrasiewicz { 23400a2430fSAndrzej Pietrasiewicz struct f_lb_opts *opts; 23500a2430fSAndrzej Pietrasiewicz 23600a2430fSAndrzej Pietrasiewicz opts = container_of(f->fi, struct f_lb_opts, func_inst); 23700a2430fSAndrzej Pietrasiewicz 23800a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 23900a2430fSAndrzej Pietrasiewicz opts->refcnt--; 24000a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 24100a2430fSAndrzej Pietrasiewicz 24200a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 24300a2430fSAndrzej Pietrasiewicz kfree(func_to_loop(f)); 24400a2430fSAndrzej Pietrasiewicz } 24500a2430fSAndrzej Pietrasiewicz 24600a2430fSAndrzej Pietrasiewicz static void loopback_complete(struct usb_ep *ep, struct usb_request *req) 24700a2430fSAndrzej Pietrasiewicz { 24800a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = ep->driver_data; 24900a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = loop->function.config->cdev; 25000a2430fSAndrzej Pietrasiewicz int status = req->status; 25100a2430fSAndrzej Pietrasiewicz 25200a2430fSAndrzej Pietrasiewicz switch (status) { 25300a2430fSAndrzej Pietrasiewicz 25400a2430fSAndrzej Pietrasiewicz case 0: /* normal completion? */ 25500a2430fSAndrzej Pietrasiewicz if (ep == loop->out_ep) { 25600a2430fSAndrzej Pietrasiewicz req->zero = (req->actual < req->length); 25700a2430fSAndrzej Pietrasiewicz req->length = req->actual; 25800a2430fSAndrzej Pietrasiewicz } 25900a2430fSAndrzej Pietrasiewicz 26000a2430fSAndrzej Pietrasiewicz /* queue the buffer for some later OUT packet */ 26100a2430fSAndrzej Pietrasiewicz req->length = buflen; 262*e0857ce5SFelipe Balbi status = usb_ep_queue(ep, req, GFP_ATOMIC); 26300a2430fSAndrzej Pietrasiewicz if (status == 0) 26400a2430fSAndrzej Pietrasiewicz return; 26500a2430fSAndrzej Pietrasiewicz 26600a2430fSAndrzej Pietrasiewicz /* "should never get here" */ 26700a2430fSAndrzej Pietrasiewicz /* FALLTHROUGH */ 26800a2430fSAndrzej Pietrasiewicz 26900a2430fSAndrzej Pietrasiewicz default: 27000a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, 27100a2430fSAndrzej Pietrasiewicz status, req->actual, req->length); 27200a2430fSAndrzej Pietrasiewicz /* FALLTHROUGH */ 27300a2430fSAndrzej Pietrasiewicz 27400a2430fSAndrzej Pietrasiewicz /* NOTE: since this driver doesn't maintain an explicit record 27500a2430fSAndrzej Pietrasiewicz * of requests it submitted (just maintains qlen count), we 27600a2430fSAndrzej Pietrasiewicz * rely on the hardware driver to clean up on disconnect or 27700a2430fSAndrzej Pietrasiewicz * endpoint disable. 27800a2430fSAndrzej Pietrasiewicz */ 27900a2430fSAndrzej Pietrasiewicz case -ECONNABORTED: /* hardware forced ep reset */ 28000a2430fSAndrzej Pietrasiewicz case -ECONNRESET: /* request dequeued */ 28100a2430fSAndrzej Pietrasiewicz case -ESHUTDOWN: /* disconnect from host */ 28200a2430fSAndrzej Pietrasiewicz free_ep_req(ep, req); 28300a2430fSAndrzej Pietrasiewicz return; 28400a2430fSAndrzej Pietrasiewicz } 28500a2430fSAndrzej Pietrasiewicz } 28600a2430fSAndrzej Pietrasiewicz 28700a2430fSAndrzej Pietrasiewicz static void disable_loopback(struct f_loopback *loop) 28800a2430fSAndrzej Pietrasiewicz { 28900a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev; 29000a2430fSAndrzej Pietrasiewicz 29100a2430fSAndrzej Pietrasiewicz cdev = loop->function.config->cdev; 292ef11982dSAmit Virdi disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL, 293ef11982dSAmit Virdi NULL); 29400a2430fSAndrzej Pietrasiewicz VDBG(cdev, "%s disabled\n", loop->function.name); 29500a2430fSAndrzej Pietrasiewicz } 29600a2430fSAndrzej Pietrasiewicz 29700a2430fSAndrzej Pietrasiewicz static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) 29800a2430fSAndrzej Pietrasiewicz { 29900a2430fSAndrzej Pietrasiewicz return alloc_ep_req(ep, len, buflen); 30000a2430fSAndrzej Pietrasiewicz } 30100a2430fSAndrzej Pietrasiewicz 302*e0857ce5SFelipe Balbi static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop, 303*e0857ce5SFelipe Balbi struct usb_ep *ep) 30400a2430fSAndrzej Pietrasiewicz { 30500a2430fSAndrzej Pietrasiewicz struct usb_request *req; 30600a2430fSAndrzej Pietrasiewicz unsigned i; 307*e0857ce5SFelipe Balbi int result; 30800a2430fSAndrzej Pietrasiewicz 309*e0857ce5SFelipe Balbi /* 310*e0857ce5SFelipe Balbi * one endpoint writes data back IN to the host while another endpoint 311*e0857ce5SFelipe Balbi * just reads OUT packets 312*e0857ce5SFelipe Balbi */ 31300a2430fSAndrzej Pietrasiewicz result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 31400a2430fSAndrzej Pietrasiewicz if (result) 31500a2430fSAndrzej Pietrasiewicz goto fail0; 31600a2430fSAndrzej Pietrasiewicz result = usb_ep_enable(ep); 317*e0857ce5SFelipe Balbi if (result < 0) 318*e0857ce5SFelipe Balbi goto fail0; 31900a2430fSAndrzej Pietrasiewicz ep->driver_data = loop; 32000a2430fSAndrzej Pietrasiewicz 321*e0857ce5SFelipe Balbi /* 322*e0857ce5SFelipe Balbi * allocate a bunch of read buffers and queue them all at once. 32300a2430fSAndrzej Pietrasiewicz * we buffer at most 'qlen' transfers; fewer if any need more 32400a2430fSAndrzej Pietrasiewicz * than 'buflen' bytes each. 32500a2430fSAndrzej Pietrasiewicz */ 32600a2430fSAndrzej Pietrasiewicz for (i = 0; i < qlen && result == 0; i++) { 32700a2430fSAndrzej Pietrasiewicz req = lb_alloc_ep_req(ep, 0); 328*e0857ce5SFelipe Balbi if (!req) 329*e0857ce5SFelipe Balbi goto fail1; 330*e0857ce5SFelipe Balbi 33100a2430fSAndrzej Pietrasiewicz req->complete = loopback_complete; 33200a2430fSAndrzej Pietrasiewicz result = usb_ep_queue(ep, req, GFP_ATOMIC); 333*e0857ce5SFelipe Balbi if (result) { 33400a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s queue req --> %d\n", 33500a2430fSAndrzej Pietrasiewicz ep->name, result); 336*e0857ce5SFelipe Balbi goto fail1; 337*e0857ce5SFelipe Balbi } 338*e0857ce5SFelipe Balbi } 339*e0857ce5SFelipe Balbi 340*e0857ce5SFelipe Balbi return 0; 341*e0857ce5SFelipe Balbi 342*e0857ce5SFelipe Balbi fail1: 34300a2430fSAndrzej Pietrasiewicz usb_ep_disable(ep); 344*e0857ce5SFelipe Balbi 345*e0857ce5SFelipe Balbi fail0: 346*e0857ce5SFelipe Balbi return result; 34700a2430fSAndrzej Pietrasiewicz } 348*e0857ce5SFelipe Balbi 349*e0857ce5SFelipe Balbi static int 350*e0857ce5SFelipe Balbi enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) 351*e0857ce5SFelipe Balbi { 352*e0857ce5SFelipe Balbi int result = 0; 353*e0857ce5SFelipe Balbi 354*e0857ce5SFelipe Balbi result = enable_endpoint(cdev, loop, loop->in_ep); 355*e0857ce5SFelipe Balbi if (result) 356*e0857ce5SFelipe Balbi return result; 357*e0857ce5SFelipe Balbi 358*e0857ce5SFelipe Balbi result = enable_endpoint(cdev, loop, loop->out_ep); 359*e0857ce5SFelipe Balbi if (result) 360*e0857ce5SFelipe Balbi return result; 36100a2430fSAndrzej Pietrasiewicz 36200a2430fSAndrzej Pietrasiewicz DBG(cdev, "%s enabled\n", loop->function.name); 36300a2430fSAndrzej Pietrasiewicz return result; 36400a2430fSAndrzej Pietrasiewicz } 36500a2430fSAndrzej Pietrasiewicz 36600a2430fSAndrzej Pietrasiewicz static int loopback_set_alt(struct usb_function *f, 36700a2430fSAndrzej Pietrasiewicz unsigned intf, unsigned alt) 36800a2430fSAndrzej Pietrasiewicz { 36900a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 37000a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 37100a2430fSAndrzej Pietrasiewicz 37200a2430fSAndrzej Pietrasiewicz /* we know alt is zero */ 37300a2430fSAndrzej Pietrasiewicz if (loop->in_ep->driver_data) 37400a2430fSAndrzej Pietrasiewicz disable_loopback(loop); 37500a2430fSAndrzej Pietrasiewicz return enable_loopback(cdev, loop); 37600a2430fSAndrzej Pietrasiewicz } 37700a2430fSAndrzej Pietrasiewicz 37800a2430fSAndrzej Pietrasiewicz static void loopback_disable(struct usb_function *f) 37900a2430fSAndrzej Pietrasiewicz { 38000a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 38100a2430fSAndrzej Pietrasiewicz 38200a2430fSAndrzej Pietrasiewicz disable_loopback(loop); 38300a2430fSAndrzej Pietrasiewicz } 38400a2430fSAndrzej Pietrasiewicz 38500a2430fSAndrzej Pietrasiewicz static struct usb_function *loopback_alloc(struct usb_function_instance *fi) 38600a2430fSAndrzej Pietrasiewicz { 38700a2430fSAndrzej Pietrasiewicz struct f_loopback *loop; 38800a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 38900a2430fSAndrzej Pietrasiewicz 39000a2430fSAndrzej Pietrasiewicz loop = kzalloc(sizeof *loop, GFP_KERNEL); 39100a2430fSAndrzej Pietrasiewicz if (!loop) 39200a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 39300a2430fSAndrzej Pietrasiewicz 39400a2430fSAndrzej Pietrasiewicz lb_opts = container_of(fi, struct f_lb_opts, func_inst); 39500a2430fSAndrzej Pietrasiewicz 39600a2430fSAndrzej Pietrasiewicz mutex_lock(&lb_opts->lock); 39700a2430fSAndrzej Pietrasiewicz lb_opts->refcnt++; 39800a2430fSAndrzej Pietrasiewicz mutex_unlock(&lb_opts->lock); 39900a2430fSAndrzej Pietrasiewicz 40000a2430fSAndrzej Pietrasiewicz buflen = lb_opts->bulk_buflen; 40100a2430fSAndrzej Pietrasiewicz qlen = lb_opts->qlen; 40200a2430fSAndrzej Pietrasiewicz if (!qlen) 40300a2430fSAndrzej Pietrasiewicz qlen = 32; 40400a2430fSAndrzej Pietrasiewicz 40500a2430fSAndrzej Pietrasiewicz loop->function.name = "loopback"; 40600a2430fSAndrzej Pietrasiewicz loop->function.bind = loopback_bind; 40700a2430fSAndrzej Pietrasiewicz loop->function.set_alt = loopback_set_alt; 40800a2430fSAndrzej Pietrasiewicz loop->function.disable = loopback_disable; 40900a2430fSAndrzej Pietrasiewicz loop->function.strings = loopback_strings; 41000a2430fSAndrzej Pietrasiewicz 41100a2430fSAndrzej Pietrasiewicz loop->function.free_func = lb_free_func; 41200a2430fSAndrzej Pietrasiewicz 41300a2430fSAndrzej Pietrasiewicz return &loop->function; 41400a2430fSAndrzej Pietrasiewicz } 41500a2430fSAndrzej Pietrasiewicz 41600a2430fSAndrzej Pietrasiewicz static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item) 41700a2430fSAndrzej Pietrasiewicz { 41800a2430fSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_lb_opts, 41900a2430fSAndrzej Pietrasiewicz func_inst.group); 42000a2430fSAndrzej Pietrasiewicz } 42100a2430fSAndrzej Pietrasiewicz 42200a2430fSAndrzej Pietrasiewicz CONFIGFS_ATTR_STRUCT(f_lb_opts); 42300a2430fSAndrzej Pietrasiewicz CONFIGFS_ATTR_OPS(f_lb_opts); 42400a2430fSAndrzej Pietrasiewicz 42500a2430fSAndrzej Pietrasiewicz static void lb_attr_release(struct config_item *item) 42600a2430fSAndrzej Pietrasiewicz { 42700a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts = to_f_lb_opts(item); 42800a2430fSAndrzej Pietrasiewicz 42900a2430fSAndrzej Pietrasiewicz usb_put_function_instance(&lb_opts->func_inst); 43000a2430fSAndrzej Pietrasiewicz } 43100a2430fSAndrzej Pietrasiewicz 43200a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations lb_item_ops = { 43300a2430fSAndrzej Pietrasiewicz .release = lb_attr_release, 43400a2430fSAndrzej Pietrasiewicz .show_attribute = f_lb_opts_attr_show, 43500a2430fSAndrzej Pietrasiewicz .store_attribute = f_lb_opts_attr_store, 43600a2430fSAndrzej Pietrasiewicz }; 43700a2430fSAndrzej Pietrasiewicz 43800a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page) 43900a2430fSAndrzej Pietrasiewicz { 44000a2430fSAndrzej Pietrasiewicz int result; 44100a2430fSAndrzej Pietrasiewicz 44200a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 44300a2430fSAndrzej Pietrasiewicz result = sprintf(page, "%d", opts->qlen); 44400a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 44500a2430fSAndrzej Pietrasiewicz 44600a2430fSAndrzej Pietrasiewicz return result; 44700a2430fSAndrzej Pietrasiewicz } 44800a2430fSAndrzej Pietrasiewicz 44900a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts, 45000a2430fSAndrzej Pietrasiewicz const char *page, size_t len) 45100a2430fSAndrzej Pietrasiewicz { 45200a2430fSAndrzej Pietrasiewicz int ret; 45300a2430fSAndrzej Pietrasiewicz u32 num; 45400a2430fSAndrzej Pietrasiewicz 45500a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 45600a2430fSAndrzej Pietrasiewicz if (opts->refcnt) { 45700a2430fSAndrzej Pietrasiewicz ret = -EBUSY; 45800a2430fSAndrzej Pietrasiewicz goto end; 45900a2430fSAndrzej Pietrasiewicz } 46000a2430fSAndrzej Pietrasiewicz 46100a2430fSAndrzej Pietrasiewicz ret = kstrtou32(page, 0, &num); 46200a2430fSAndrzej Pietrasiewicz if (ret) 46300a2430fSAndrzej Pietrasiewicz goto end; 46400a2430fSAndrzej Pietrasiewicz 46500a2430fSAndrzej Pietrasiewicz opts->qlen = num; 46600a2430fSAndrzej Pietrasiewicz ret = len; 46700a2430fSAndrzej Pietrasiewicz end: 46800a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 46900a2430fSAndrzej Pietrasiewicz return ret; 47000a2430fSAndrzej Pietrasiewicz } 47100a2430fSAndrzej Pietrasiewicz 47200a2430fSAndrzej Pietrasiewicz static struct f_lb_opts_attribute f_lb_opts_qlen = 47300a2430fSAndrzej Pietrasiewicz __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR, 47400a2430fSAndrzej Pietrasiewicz f_lb_opts_qlen_show, 47500a2430fSAndrzej Pietrasiewicz f_lb_opts_qlen_store); 47600a2430fSAndrzej Pietrasiewicz 47700a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page) 47800a2430fSAndrzej Pietrasiewicz { 47900a2430fSAndrzej Pietrasiewicz int result; 48000a2430fSAndrzej Pietrasiewicz 48100a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 48200a2430fSAndrzej Pietrasiewicz result = sprintf(page, "%d", opts->bulk_buflen); 48300a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 48400a2430fSAndrzej Pietrasiewicz 48500a2430fSAndrzej Pietrasiewicz return result; 48600a2430fSAndrzej Pietrasiewicz } 48700a2430fSAndrzej Pietrasiewicz 48800a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts, 48900a2430fSAndrzej Pietrasiewicz const char *page, size_t len) 49000a2430fSAndrzej Pietrasiewicz { 49100a2430fSAndrzej Pietrasiewicz int ret; 49200a2430fSAndrzej Pietrasiewicz u32 num; 49300a2430fSAndrzej Pietrasiewicz 49400a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 49500a2430fSAndrzej Pietrasiewicz if (opts->refcnt) { 49600a2430fSAndrzej Pietrasiewicz ret = -EBUSY; 49700a2430fSAndrzej Pietrasiewicz goto end; 49800a2430fSAndrzej Pietrasiewicz } 49900a2430fSAndrzej Pietrasiewicz 50000a2430fSAndrzej Pietrasiewicz ret = kstrtou32(page, 0, &num); 50100a2430fSAndrzej Pietrasiewicz if (ret) 50200a2430fSAndrzej Pietrasiewicz goto end; 50300a2430fSAndrzej Pietrasiewicz 50400a2430fSAndrzej Pietrasiewicz opts->bulk_buflen = num; 50500a2430fSAndrzej Pietrasiewicz ret = len; 50600a2430fSAndrzej Pietrasiewicz end: 50700a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 50800a2430fSAndrzej Pietrasiewicz return ret; 50900a2430fSAndrzej Pietrasiewicz } 51000a2430fSAndrzej Pietrasiewicz 51100a2430fSAndrzej Pietrasiewicz static struct f_lb_opts_attribute f_lb_opts_bulk_buflen = 51200a2430fSAndrzej Pietrasiewicz __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, 51300a2430fSAndrzej Pietrasiewicz f_lb_opts_bulk_buflen_show, 51400a2430fSAndrzej Pietrasiewicz f_lb_opts_bulk_buflen_store); 51500a2430fSAndrzej Pietrasiewicz 51600a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *lb_attrs[] = { 51700a2430fSAndrzej Pietrasiewicz &f_lb_opts_qlen.attr, 51800a2430fSAndrzej Pietrasiewicz &f_lb_opts_bulk_buflen.attr, 51900a2430fSAndrzej Pietrasiewicz NULL, 52000a2430fSAndrzej Pietrasiewicz }; 52100a2430fSAndrzej Pietrasiewicz 52200a2430fSAndrzej Pietrasiewicz static struct config_item_type lb_func_type = { 52300a2430fSAndrzej Pietrasiewicz .ct_item_ops = &lb_item_ops, 52400a2430fSAndrzej Pietrasiewicz .ct_attrs = lb_attrs, 52500a2430fSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 52600a2430fSAndrzej Pietrasiewicz }; 52700a2430fSAndrzej Pietrasiewicz 52800a2430fSAndrzej Pietrasiewicz static void lb_free_instance(struct usb_function_instance *fi) 52900a2430fSAndrzej Pietrasiewicz { 53000a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 53100a2430fSAndrzej Pietrasiewicz 53200a2430fSAndrzej Pietrasiewicz lb_opts = container_of(fi, struct f_lb_opts, func_inst); 53300a2430fSAndrzej Pietrasiewicz kfree(lb_opts); 53400a2430fSAndrzej Pietrasiewicz } 53500a2430fSAndrzej Pietrasiewicz 53600a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *loopback_alloc_instance(void) 53700a2430fSAndrzej Pietrasiewicz { 53800a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 53900a2430fSAndrzej Pietrasiewicz 54000a2430fSAndrzej Pietrasiewicz lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL); 54100a2430fSAndrzej Pietrasiewicz if (!lb_opts) 54200a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 54300a2430fSAndrzej Pietrasiewicz mutex_init(&lb_opts->lock); 54400a2430fSAndrzej Pietrasiewicz lb_opts->func_inst.free_func_inst = lb_free_instance; 54500a2430fSAndrzej Pietrasiewicz lb_opts->bulk_buflen = GZERO_BULK_BUFLEN; 54600a2430fSAndrzej Pietrasiewicz lb_opts->qlen = GZERO_QLEN; 54700a2430fSAndrzej Pietrasiewicz 54800a2430fSAndrzej Pietrasiewicz config_group_init_type_name(&lb_opts->func_inst.group, "", 54900a2430fSAndrzej Pietrasiewicz &lb_func_type); 55000a2430fSAndrzej Pietrasiewicz 55100a2430fSAndrzej Pietrasiewicz return &lb_opts->func_inst; 55200a2430fSAndrzej Pietrasiewicz } 55300a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc); 55400a2430fSAndrzej Pietrasiewicz 55500a2430fSAndrzej Pietrasiewicz int __init lb_modinit(void) 55600a2430fSAndrzej Pietrasiewicz { 55700a2430fSAndrzej Pietrasiewicz int ret; 55800a2430fSAndrzej Pietrasiewicz 55900a2430fSAndrzej Pietrasiewicz ret = usb_function_register(&Loopbackusb_func); 56000a2430fSAndrzej Pietrasiewicz if (ret) 56100a2430fSAndrzej Pietrasiewicz return ret; 56200a2430fSAndrzej Pietrasiewicz return ret; 56300a2430fSAndrzej Pietrasiewicz } 56400a2430fSAndrzej Pietrasiewicz void __exit lb_modexit(void) 56500a2430fSAndrzej Pietrasiewicz { 56600a2430fSAndrzej Pietrasiewicz usb_function_unregister(&Loopbackusb_func); 56700a2430fSAndrzej Pietrasiewicz } 56800a2430fSAndrzej Pietrasiewicz 56900a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 570