15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 200a2430fSAndrzej Pietrasiewicz /* 300a2430fSAndrzej Pietrasiewicz * f_loopback.c - USB peripheral loopback configuration driver 400a2430fSAndrzej Pietrasiewicz * 500a2430fSAndrzej Pietrasiewicz * Copyright (C) 2003-2008 David Brownell 600a2430fSAndrzej Pietrasiewicz * Copyright (C) 2008 by Nokia Corporation 700a2430fSAndrzej Pietrasiewicz */ 800a2430fSAndrzej Pietrasiewicz 900a2430fSAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */ 1000a2430fSAndrzej Pietrasiewicz 1100a2430fSAndrzej Pietrasiewicz #include <linux/slab.h> 1200a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h> 1300a2430fSAndrzej Pietrasiewicz #include <linux/device.h> 1400a2430fSAndrzej Pietrasiewicz #include <linux/module.h> 1500a2430fSAndrzej Pietrasiewicz #include <linux/err.h> 1600a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h> 1700a2430fSAndrzej Pietrasiewicz 1800a2430fSAndrzej Pietrasiewicz #include "g_zero.h" 1900a2430fSAndrzej Pietrasiewicz #include "u_f.h" 2000a2430fSAndrzej Pietrasiewicz 2100a2430fSAndrzej Pietrasiewicz /* 2200a2430fSAndrzej Pietrasiewicz * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, 2300a2430fSAndrzej Pietrasiewicz * 2400a2430fSAndrzej Pietrasiewicz * This takes messages of various sizes written OUT to a device, and loops 2500a2430fSAndrzej Pietrasiewicz * them back so they can be read IN from it. It has been used by certain 2600a2430fSAndrzej Pietrasiewicz * test applications. It supports limited testing of data queueing logic. 2700a2430fSAndrzej Pietrasiewicz */ 2800a2430fSAndrzej Pietrasiewicz struct f_loopback { 2900a2430fSAndrzej Pietrasiewicz struct usb_function function; 3000a2430fSAndrzej Pietrasiewicz 3100a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep; 3200a2430fSAndrzej Pietrasiewicz struct usb_ep *out_ep; 333e9d992fSKrzysztof Opasiak 343e9d992fSKrzysztof Opasiak unsigned qlen; 353e9d992fSKrzysztof Opasiak unsigned buflen; 3600a2430fSAndrzej Pietrasiewicz }; 3700a2430fSAndrzej Pietrasiewicz 3800a2430fSAndrzej Pietrasiewicz static inline struct f_loopback *func_to_loop(struct usb_function *f) 3900a2430fSAndrzej Pietrasiewicz { 4000a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_loopback, function); 4100a2430fSAndrzej Pietrasiewicz } 4200a2430fSAndrzej Pietrasiewicz 4300a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 4400a2430fSAndrzej Pietrasiewicz 4500a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor loopback_intf = { 463e9d992fSKrzysztof Opasiak .bLength = sizeof(loopback_intf), 4700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 4800a2430fSAndrzej Pietrasiewicz 4900a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2, 5000a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 5100a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */ 5200a2430fSAndrzej Pietrasiewicz }; 5300a2430fSAndrzej Pietrasiewicz 5400a2430fSAndrzej Pietrasiewicz /* full speed support: */ 5500a2430fSAndrzej Pietrasiewicz 5600a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_source_desc = { 5700a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 5800a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 5900a2430fSAndrzej Pietrasiewicz 6000a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 6100a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 6200a2430fSAndrzej Pietrasiewicz }; 6300a2430fSAndrzej Pietrasiewicz 6400a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_sink_desc = { 6500a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 6600a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 6700a2430fSAndrzej Pietrasiewicz 6800a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 6900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 7000a2430fSAndrzej Pietrasiewicz }; 7100a2430fSAndrzej Pietrasiewicz 7200a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_loopback_descs[] = { 7300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 7400a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &fs_loop_sink_desc, 7500a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &fs_loop_source_desc, 7600a2430fSAndrzej Pietrasiewicz NULL, 7700a2430fSAndrzej Pietrasiewicz }; 7800a2430fSAndrzej Pietrasiewicz 7900a2430fSAndrzej Pietrasiewicz /* high speed support: */ 8000a2430fSAndrzej Pietrasiewicz 8100a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_source_desc = { 8200a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 8300a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 8400a2430fSAndrzej Pietrasiewicz 8500a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 8600a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512), 8700a2430fSAndrzej Pietrasiewicz }; 8800a2430fSAndrzej Pietrasiewicz 8900a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_sink_desc = { 9000a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 9100a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 9200a2430fSAndrzej Pietrasiewicz 9300a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 9400a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512), 9500a2430fSAndrzej Pietrasiewicz }; 9600a2430fSAndrzej Pietrasiewicz 9700a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_loopback_descs[] = { 9800a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 9900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &hs_loop_source_desc, 10000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &hs_loop_sink_desc, 10100a2430fSAndrzej Pietrasiewicz NULL, 10200a2430fSAndrzej Pietrasiewicz }; 10300a2430fSAndrzej Pietrasiewicz 10400a2430fSAndrzej Pietrasiewicz /* super speed support: */ 10500a2430fSAndrzej Pietrasiewicz 10600a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_source_desc = { 10700a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 10800a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 10900a2430fSAndrzej Pietrasiewicz 11000a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 11100a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024), 11200a2430fSAndrzej Pietrasiewicz }; 11300a2430fSAndrzej Pietrasiewicz 11400a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { 11500a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_SS_EP_COMP_SIZE, 11600a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 11700a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 11800a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 11900a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = 0, 12000a2430fSAndrzej Pietrasiewicz }; 12100a2430fSAndrzej Pietrasiewicz 12200a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_sink_desc = { 12300a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 12400a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 12500a2430fSAndrzej Pietrasiewicz 12600a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 12700a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024), 12800a2430fSAndrzej Pietrasiewicz }; 12900a2430fSAndrzej Pietrasiewicz 13000a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { 13100a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_SS_EP_COMP_SIZE, 13200a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 13300a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 13400a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 13500a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = 0, 13600a2430fSAndrzej Pietrasiewicz }; 13700a2430fSAndrzej Pietrasiewicz 13800a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ss_loopback_descs[] = { 13900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &loopback_intf, 14000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_source_desc, 14100a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_source_comp_desc, 14200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_sink_desc, 14300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &ss_loop_sink_comp_desc, 14400a2430fSAndrzej Pietrasiewicz NULL, 14500a2430fSAndrzej Pietrasiewicz }; 14600a2430fSAndrzej Pietrasiewicz 14700a2430fSAndrzej Pietrasiewicz /* function-specific strings: */ 14800a2430fSAndrzej Pietrasiewicz 14900a2430fSAndrzej Pietrasiewicz static struct usb_string strings_loopback[] = { 15000a2430fSAndrzej Pietrasiewicz [0].s = "loop input to output", 15100a2430fSAndrzej Pietrasiewicz { } /* end of list */ 15200a2430fSAndrzej Pietrasiewicz }; 15300a2430fSAndrzej Pietrasiewicz 15400a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_loop = { 15500a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-us */ 15600a2430fSAndrzej Pietrasiewicz .strings = strings_loopback, 15700a2430fSAndrzej Pietrasiewicz }; 15800a2430fSAndrzej Pietrasiewicz 15900a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *loopback_strings[] = { 16000a2430fSAndrzej Pietrasiewicz &stringtab_loop, 16100a2430fSAndrzej Pietrasiewicz NULL, 16200a2430fSAndrzej Pietrasiewicz }; 16300a2430fSAndrzej Pietrasiewicz 16400a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 16500a2430fSAndrzej Pietrasiewicz 16600a2430fSAndrzej Pietrasiewicz static int loopback_bind(struct usb_configuration *c, struct usb_function *f) 16700a2430fSAndrzej Pietrasiewicz { 16800a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev; 16900a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 17000a2430fSAndrzej Pietrasiewicz int id; 17100a2430fSAndrzej Pietrasiewicz int ret; 17200a2430fSAndrzej Pietrasiewicz 17300a2430fSAndrzej Pietrasiewicz /* allocate interface ID(s) */ 17400a2430fSAndrzej Pietrasiewicz id = usb_interface_id(c, f); 17500a2430fSAndrzej Pietrasiewicz if (id < 0) 17600a2430fSAndrzej Pietrasiewicz return id; 17700a2430fSAndrzej Pietrasiewicz loopback_intf.bInterfaceNumber = id; 17800a2430fSAndrzej Pietrasiewicz 17900a2430fSAndrzej Pietrasiewicz id = usb_string_id(cdev); 18000a2430fSAndrzej Pietrasiewicz if (id < 0) 18100a2430fSAndrzej Pietrasiewicz return id; 18200a2430fSAndrzej Pietrasiewicz strings_loopback[0].id = id; 18300a2430fSAndrzej Pietrasiewicz loopback_intf.iInterface = id; 18400a2430fSAndrzej Pietrasiewicz 18500a2430fSAndrzej Pietrasiewicz /* allocate endpoints */ 18600a2430fSAndrzej Pietrasiewicz 18700a2430fSAndrzej Pietrasiewicz loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc); 18800a2430fSAndrzej Pietrasiewicz if (!loop->in_ep) { 18900a2430fSAndrzej Pietrasiewicz autoconf_fail: 19000a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s: can't autoconfigure on %s\n", 19100a2430fSAndrzej Pietrasiewicz f->name, cdev->gadget->name); 19200a2430fSAndrzej Pietrasiewicz return -ENODEV; 19300a2430fSAndrzej Pietrasiewicz } 19400a2430fSAndrzej Pietrasiewicz 19500a2430fSAndrzej Pietrasiewicz loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc); 19600a2430fSAndrzej Pietrasiewicz if (!loop->out_ep) 19700a2430fSAndrzej Pietrasiewicz goto autoconf_fail; 19800a2430fSAndrzej Pietrasiewicz 19900a2430fSAndrzej Pietrasiewicz /* support high speed hardware */ 20000a2430fSAndrzej Pietrasiewicz hs_loop_source_desc.bEndpointAddress = 20100a2430fSAndrzej Pietrasiewicz fs_loop_source_desc.bEndpointAddress; 20200a2430fSAndrzej Pietrasiewicz hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; 20300a2430fSAndrzej Pietrasiewicz 20400a2430fSAndrzej Pietrasiewicz /* support super speed hardware */ 20500a2430fSAndrzej Pietrasiewicz ss_loop_source_desc.bEndpointAddress = 20600a2430fSAndrzej Pietrasiewicz fs_loop_source_desc.bEndpointAddress; 20700a2430fSAndrzej Pietrasiewicz ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; 20800a2430fSAndrzej Pietrasiewicz 20900a2430fSAndrzej Pietrasiewicz ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs, 21090c4d057SMaciej Żenczykowski ss_loopback_descs, ss_loopback_descs); 21100a2430fSAndrzej Pietrasiewicz if (ret) 21200a2430fSAndrzej Pietrasiewicz return ret; 21300a2430fSAndrzej Pietrasiewicz 214333ab99eSLinyu Yuan DBG(cdev, "%s: IN/%s, OUT/%s\n", 21500a2430fSAndrzej Pietrasiewicz f->name, loop->in_ep->name, loop->out_ep->name); 21600a2430fSAndrzej Pietrasiewicz return 0; 21700a2430fSAndrzej Pietrasiewicz } 21800a2430fSAndrzej Pietrasiewicz 21900a2430fSAndrzej Pietrasiewicz static void lb_free_func(struct usb_function *f) 22000a2430fSAndrzej Pietrasiewicz { 22100a2430fSAndrzej Pietrasiewicz struct f_lb_opts *opts; 22200a2430fSAndrzej Pietrasiewicz 22300a2430fSAndrzej Pietrasiewicz opts = container_of(f->fi, struct f_lb_opts, func_inst); 22400a2430fSAndrzej Pietrasiewicz 22500a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 22600a2430fSAndrzej Pietrasiewicz opts->refcnt--; 22700a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 22800a2430fSAndrzej Pietrasiewicz 22900a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 23000a2430fSAndrzej Pietrasiewicz kfree(func_to_loop(f)); 23100a2430fSAndrzej Pietrasiewicz } 23200a2430fSAndrzej Pietrasiewicz 23300a2430fSAndrzej Pietrasiewicz static void loopback_complete(struct usb_ep *ep, struct usb_request *req) 23400a2430fSAndrzej Pietrasiewicz { 23500a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = ep->driver_data; 23600a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = loop->function.config->cdev; 23700a2430fSAndrzej Pietrasiewicz int status = req->status; 23800a2430fSAndrzej Pietrasiewicz 23900a2430fSAndrzej Pietrasiewicz switch (status) { 24000a2430fSAndrzej Pietrasiewicz case 0: /* normal completion? */ 24100a2430fSAndrzej Pietrasiewicz if (ep == loop->out_ep) { 24291c42b0dSKrzysztof Opasiak /* 24391c42b0dSKrzysztof Opasiak * We received some data from the host so let's 24491c42b0dSKrzysztof Opasiak * queue it so host can read the from our in ep 24591c42b0dSKrzysztof Opasiak */ 24691c42b0dSKrzysztof Opasiak struct usb_request *in_req = req->context; 24791c42b0dSKrzysztof Opasiak 24891c42b0dSKrzysztof Opasiak in_req->zero = (req->actual < req->length); 24991c42b0dSKrzysztof Opasiak in_req->length = req->actual; 25091c42b0dSKrzysztof Opasiak ep = loop->in_ep; 25191c42b0dSKrzysztof Opasiak req = in_req; 25291c42b0dSKrzysztof Opasiak } else { 25391c42b0dSKrzysztof Opasiak /* 25491c42b0dSKrzysztof Opasiak * We have just looped back a bunch of data 25591c42b0dSKrzysztof Opasiak * to host. Now let's wait for some more data. 25691c42b0dSKrzysztof Opasiak */ 25791c42b0dSKrzysztof Opasiak req = req->context; 25891c42b0dSKrzysztof Opasiak ep = loop->out_ep; 25900a2430fSAndrzej Pietrasiewicz } 26000a2430fSAndrzej Pietrasiewicz 26191c42b0dSKrzysztof Opasiak /* queue the buffer back to host or for next bunch of data */ 262e0857ce5SFelipe Balbi status = usb_ep_queue(ep, req, GFP_ATOMIC); 26391c42b0dSKrzysztof Opasiak if (status == 0) { 26400a2430fSAndrzej Pietrasiewicz return; 26591c42b0dSKrzysztof Opasiak } else { 26691c42b0dSKrzysztof Opasiak ERROR(cdev, "Unable to loop back buffer to %s: %d\n", 26791c42b0dSKrzysztof Opasiak ep->name, status); 26891c42b0dSKrzysztof Opasiak goto free_req; 26991c42b0dSKrzysztof Opasiak } 27000a2430fSAndrzej Pietrasiewicz 27100a2430fSAndrzej Pietrasiewicz /* "should never get here" */ 27200a2430fSAndrzej Pietrasiewicz default: 27300a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, 27400a2430fSAndrzej Pietrasiewicz status, req->actual, req->length); 27593c747edSGustavo A. R. Silva fallthrough; 27600a2430fSAndrzej Pietrasiewicz 27700a2430fSAndrzej Pietrasiewicz /* NOTE: since this driver doesn't maintain an explicit record 27800a2430fSAndrzej Pietrasiewicz * of requests it submitted (just maintains qlen count), we 27900a2430fSAndrzej Pietrasiewicz * rely on the hardware driver to clean up on disconnect or 28000a2430fSAndrzej Pietrasiewicz * endpoint disable. 28100a2430fSAndrzej Pietrasiewicz */ 28200a2430fSAndrzej Pietrasiewicz case -ECONNABORTED: /* hardware forced ep reset */ 28300a2430fSAndrzej Pietrasiewicz case -ECONNRESET: /* request dequeued */ 28400a2430fSAndrzej Pietrasiewicz case -ESHUTDOWN: /* disconnect from host */ 28591c42b0dSKrzysztof Opasiak free_req: 28691c42b0dSKrzysztof Opasiak usb_ep_free_request(ep == loop->in_ep ? 28791c42b0dSKrzysztof Opasiak loop->out_ep : loop->in_ep, 28891c42b0dSKrzysztof Opasiak req->context); 28900a2430fSAndrzej Pietrasiewicz free_ep_req(ep, req); 29000a2430fSAndrzej Pietrasiewicz return; 29100a2430fSAndrzej Pietrasiewicz } 29200a2430fSAndrzej Pietrasiewicz } 29300a2430fSAndrzej Pietrasiewicz 29400a2430fSAndrzej Pietrasiewicz static void disable_loopback(struct f_loopback *loop) 29500a2430fSAndrzej Pietrasiewicz { 29600a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev; 29700a2430fSAndrzej Pietrasiewicz 29800a2430fSAndrzej Pietrasiewicz cdev = loop->function.config->cdev; 2992c247804SFelipe Balbi disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL); 30000a2430fSAndrzej Pietrasiewicz VDBG(cdev, "%s disabled\n", loop->function.name); 30100a2430fSAndrzej Pietrasiewicz } 30200a2430fSAndrzej Pietrasiewicz 30300a2430fSAndrzej Pietrasiewicz static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) 30400a2430fSAndrzej Pietrasiewicz { 305aadbe812SFelipe F. Tonello return alloc_ep_req(ep, len); 30600a2430fSAndrzej Pietrasiewicz } 30700a2430fSAndrzej Pietrasiewicz 30891c42b0dSKrzysztof Opasiak static int alloc_requests(struct usb_composite_dev *cdev, 30991c42b0dSKrzysztof Opasiak struct f_loopback *loop) 31000a2430fSAndrzej Pietrasiewicz { 31191c42b0dSKrzysztof Opasiak struct usb_request *in_req, *out_req; 31291c42b0dSKrzysztof Opasiak int i; 31391c42b0dSKrzysztof Opasiak int result = 0; 31400a2430fSAndrzej Pietrasiewicz 315e0857ce5SFelipe Balbi /* 316e0857ce5SFelipe Balbi * allocate a bunch of read buffers and queue them all at once. 31791c42b0dSKrzysztof Opasiak * we buffer at most 'qlen' transfers; We allocate buffers only 31891c42b0dSKrzysztof Opasiak * for out transfer and reuse them in IN transfers to implement 31991c42b0dSKrzysztof Opasiak * our loopback functionality 32000a2430fSAndrzej Pietrasiewicz */ 3213e9d992fSKrzysztof Opasiak for (i = 0; i < loop->qlen && result == 0; i++) { 32291c42b0dSKrzysztof Opasiak result = -ENOMEM; 323e0857ce5SFelipe Balbi 3245e216d54SPeter Chen in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC); 32591c42b0dSKrzysztof Opasiak if (!in_req) 32691c42b0dSKrzysztof Opasiak goto fail; 32791c42b0dSKrzysztof Opasiak 328aadbe812SFelipe F. Tonello out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen); 32991c42b0dSKrzysztof Opasiak if (!out_req) 33091c42b0dSKrzysztof Opasiak goto fail_in; 33191c42b0dSKrzysztof Opasiak 33291c42b0dSKrzysztof Opasiak in_req->complete = loopback_complete; 33391c42b0dSKrzysztof Opasiak out_req->complete = loopback_complete; 33491c42b0dSKrzysztof Opasiak 33591c42b0dSKrzysztof Opasiak in_req->buf = out_req->buf; 33691c42b0dSKrzysztof Opasiak /* length will be set in complete routine */ 33791c42b0dSKrzysztof Opasiak in_req->context = out_req; 33891c42b0dSKrzysztof Opasiak out_req->context = in_req; 33991c42b0dSKrzysztof Opasiak 34091c42b0dSKrzysztof Opasiak result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC); 341e0857ce5SFelipe Balbi if (result) { 34200a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s queue req --> %d\n", 34391c42b0dSKrzysztof Opasiak loop->out_ep->name, result); 34491c42b0dSKrzysztof Opasiak goto fail_out; 345e0857ce5SFelipe Balbi } 346e0857ce5SFelipe Balbi } 347e0857ce5SFelipe Balbi 348e0857ce5SFelipe Balbi return 0; 349e0857ce5SFelipe Balbi 35091c42b0dSKrzysztof Opasiak fail_out: 35191c42b0dSKrzysztof Opasiak free_ep_req(loop->out_ep, out_req); 35291c42b0dSKrzysztof Opasiak fail_in: 35391c42b0dSKrzysztof Opasiak usb_ep_free_request(loop->in_ep, in_req); 35491c42b0dSKrzysztof Opasiak fail: 35591c42b0dSKrzysztof Opasiak return result; 35691c42b0dSKrzysztof Opasiak } 357e0857ce5SFelipe Balbi 35891c42b0dSKrzysztof Opasiak static int enable_endpoint(struct usb_composite_dev *cdev, 35991c42b0dSKrzysztof Opasiak struct f_loopback *loop, struct usb_ep *ep) 36091c42b0dSKrzysztof Opasiak { 36191c42b0dSKrzysztof Opasiak int result; 36291c42b0dSKrzysztof Opasiak 36391c42b0dSKrzysztof Opasiak result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 36491c42b0dSKrzysztof Opasiak if (result) 36591c42b0dSKrzysztof Opasiak goto out; 36691c42b0dSKrzysztof Opasiak 36791c42b0dSKrzysztof Opasiak result = usb_ep_enable(ep); 36891c42b0dSKrzysztof Opasiak if (result < 0) 36991c42b0dSKrzysztof Opasiak goto out; 37091c42b0dSKrzysztof Opasiak ep->driver_data = loop; 37191c42b0dSKrzysztof Opasiak result = 0; 37291c42b0dSKrzysztof Opasiak 37391c42b0dSKrzysztof Opasiak out: 374e0857ce5SFelipe Balbi return result; 37500a2430fSAndrzej Pietrasiewicz } 376e0857ce5SFelipe Balbi 377e0857ce5SFelipe Balbi static int 378e0857ce5SFelipe Balbi enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) 379e0857ce5SFelipe Balbi { 380e0857ce5SFelipe Balbi int result = 0; 381e0857ce5SFelipe Balbi 382e0857ce5SFelipe Balbi result = enable_endpoint(cdev, loop, loop->in_ep); 383e0857ce5SFelipe Balbi if (result) 38491c42b0dSKrzysztof Opasiak goto out; 385e0857ce5SFelipe Balbi 386e0857ce5SFelipe Balbi result = enable_endpoint(cdev, loop, loop->out_ep); 387e0857ce5SFelipe Balbi if (result) 38891c42b0dSKrzysztof Opasiak goto disable_in; 38991c42b0dSKrzysztof Opasiak 39091c42b0dSKrzysztof Opasiak result = alloc_requests(cdev, loop); 39191c42b0dSKrzysztof Opasiak if (result) 39291c42b0dSKrzysztof Opasiak goto disable_out; 39300a2430fSAndrzej Pietrasiewicz 39400a2430fSAndrzej Pietrasiewicz DBG(cdev, "%s enabled\n", loop->function.name); 39591c42b0dSKrzysztof Opasiak return 0; 39691c42b0dSKrzysztof Opasiak 39791c42b0dSKrzysztof Opasiak disable_out: 39891c42b0dSKrzysztof Opasiak usb_ep_disable(loop->out_ep); 39991c42b0dSKrzysztof Opasiak disable_in: 40091c42b0dSKrzysztof Opasiak usb_ep_disable(loop->in_ep); 40191c42b0dSKrzysztof Opasiak out: 40200a2430fSAndrzej Pietrasiewicz return result; 40300a2430fSAndrzej Pietrasiewicz } 40400a2430fSAndrzej Pietrasiewicz 40500a2430fSAndrzej Pietrasiewicz static int loopback_set_alt(struct usb_function *f, 40600a2430fSAndrzej Pietrasiewicz unsigned intf, unsigned alt) 40700a2430fSAndrzej Pietrasiewicz { 40800a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 40900a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 41000a2430fSAndrzej Pietrasiewicz 41100a2430fSAndrzej Pietrasiewicz /* we know alt is zero */ 41200a2430fSAndrzej Pietrasiewicz disable_loopback(loop); 41300a2430fSAndrzej Pietrasiewicz return enable_loopback(cdev, loop); 41400a2430fSAndrzej Pietrasiewicz } 41500a2430fSAndrzej Pietrasiewicz 41600a2430fSAndrzej Pietrasiewicz static void loopback_disable(struct usb_function *f) 41700a2430fSAndrzej Pietrasiewicz { 41800a2430fSAndrzej Pietrasiewicz struct f_loopback *loop = func_to_loop(f); 41900a2430fSAndrzej Pietrasiewicz 42000a2430fSAndrzej Pietrasiewicz disable_loopback(loop); 42100a2430fSAndrzej Pietrasiewicz } 42200a2430fSAndrzej Pietrasiewicz 42300a2430fSAndrzej Pietrasiewicz static struct usb_function *loopback_alloc(struct usb_function_instance *fi) 42400a2430fSAndrzej Pietrasiewicz { 42500a2430fSAndrzej Pietrasiewicz struct f_loopback *loop; 42600a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 42700a2430fSAndrzej Pietrasiewicz 42800a2430fSAndrzej Pietrasiewicz loop = kzalloc(sizeof *loop, GFP_KERNEL); 42900a2430fSAndrzej Pietrasiewicz if (!loop) 43000a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 43100a2430fSAndrzej Pietrasiewicz 43200a2430fSAndrzej Pietrasiewicz lb_opts = container_of(fi, struct f_lb_opts, func_inst); 43300a2430fSAndrzej Pietrasiewicz 43400a2430fSAndrzej Pietrasiewicz mutex_lock(&lb_opts->lock); 43500a2430fSAndrzej Pietrasiewicz lb_opts->refcnt++; 43600a2430fSAndrzej Pietrasiewicz mutex_unlock(&lb_opts->lock); 43700a2430fSAndrzej Pietrasiewicz 4383e9d992fSKrzysztof Opasiak loop->buflen = lb_opts->bulk_buflen; 4393e9d992fSKrzysztof Opasiak loop->qlen = lb_opts->qlen; 4403e9d992fSKrzysztof Opasiak if (!loop->qlen) 4413e9d992fSKrzysztof Opasiak loop->qlen = 32; 44200a2430fSAndrzej Pietrasiewicz 44300a2430fSAndrzej Pietrasiewicz loop->function.name = "loopback"; 44400a2430fSAndrzej Pietrasiewicz loop->function.bind = loopback_bind; 44500a2430fSAndrzej Pietrasiewicz loop->function.set_alt = loopback_set_alt; 44600a2430fSAndrzej Pietrasiewicz loop->function.disable = loopback_disable; 44700a2430fSAndrzej Pietrasiewicz loop->function.strings = loopback_strings; 44800a2430fSAndrzej Pietrasiewicz 44900a2430fSAndrzej Pietrasiewicz loop->function.free_func = lb_free_func; 45000a2430fSAndrzej Pietrasiewicz 45100a2430fSAndrzej Pietrasiewicz return &loop->function; 45200a2430fSAndrzej Pietrasiewicz } 45300a2430fSAndrzej Pietrasiewicz 45400a2430fSAndrzej Pietrasiewicz static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item) 45500a2430fSAndrzej Pietrasiewicz { 45600a2430fSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_lb_opts, 45700a2430fSAndrzej Pietrasiewicz func_inst.group); 45800a2430fSAndrzej Pietrasiewicz } 45900a2430fSAndrzej Pietrasiewicz 46000a2430fSAndrzej Pietrasiewicz static void lb_attr_release(struct config_item *item) 46100a2430fSAndrzej Pietrasiewicz { 46200a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts = to_f_lb_opts(item); 46300a2430fSAndrzej Pietrasiewicz 46400a2430fSAndrzej Pietrasiewicz usb_put_function_instance(&lb_opts->func_inst); 46500a2430fSAndrzej Pietrasiewicz } 46600a2430fSAndrzej Pietrasiewicz 46700a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations lb_item_ops = { 46800a2430fSAndrzej Pietrasiewicz .release = lb_attr_release, 46900a2430fSAndrzej Pietrasiewicz }; 47000a2430fSAndrzej Pietrasiewicz 47175ab2256SChristoph Hellwig static ssize_t f_lb_opts_qlen_show(struct config_item *item, char *page) 47200a2430fSAndrzej Pietrasiewicz { 47375ab2256SChristoph Hellwig struct f_lb_opts *opts = to_f_lb_opts(item); 47400a2430fSAndrzej Pietrasiewicz int result; 47500a2430fSAndrzej Pietrasiewicz 47600a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 477902d03c7SKrzysztof Opasiak result = sprintf(page, "%d\n", opts->qlen); 47800a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 47900a2430fSAndrzej Pietrasiewicz 48000a2430fSAndrzej Pietrasiewicz return result; 48100a2430fSAndrzej Pietrasiewicz } 48200a2430fSAndrzej Pietrasiewicz 48375ab2256SChristoph Hellwig static ssize_t f_lb_opts_qlen_store(struct config_item *item, 48400a2430fSAndrzej Pietrasiewicz const char *page, size_t len) 48500a2430fSAndrzej Pietrasiewicz { 48675ab2256SChristoph Hellwig struct f_lb_opts *opts = to_f_lb_opts(item); 48700a2430fSAndrzej Pietrasiewicz int ret; 48800a2430fSAndrzej Pietrasiewicz u32 num; 48900a2430fSAndrzej Pietrasiewicz 49000a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 49100a2430fSAndrzej Pietrasiewicz if (opts->refcnt) { 49200a2430fSAndrzej Pietrasiewicz ret = -EBUSY; 49300a2430fSAndrzej Pietrasiewicz goto end; 49400a2430fSAndrzej Pietrasiewicz } 49500a2430fSAndrzej Pietrasiewicz 49600a2430fSAndrzej Pietrasiewicz ret = kstrtou32(page, 0, &num); 49700a2430fSAndrzej Pietrasiewicz if (ret) 49800a2430fSAndrzej Pietrasiewicz goto end; 49900a2430fSAndrzej Pietrasiewicz 50000a2430fSAndrzej Pietrasiewicz opts->qlen = num; 50100a2430fSAndrzej Pietrasiewicz ret = len; 50200a2430fSAndrzej Pietrasiewicz end: 50300a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 50400a2430fSAndrzej Pietrasiewicz return ret; 50500a2430fSAndrzej Pietrasiewicz } 50600a2430fSAndrzej Pietrasiewicz 50775ab2256SChristoph Hellwig CONFIGFS_ATTR(f_lb_opts_, qlen); 50800a2430fSAndrzej Pietrasiewicz 50975ab2256SChristoph Hellwig static ssize_t f_lb_opts_bulk_buflen_show(struct config_item *item, char *page) 51000a2430fSAndrzej Pietrasiewicz { 51175ab2256SChristoph Hellwig struct f_lb_opts *opts = to_f_lb_opts(item); 51200a2430fSAndrzej Pietrasiewicz int result; 51300a2430fSAndrzej Pietrasiewicz 51400a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 515902d03c7SKrzysztof Opasiak result = sprintf(page, "%d\n", opts->bulk_buflen); 51600a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 51700a2430fSAndrzej Pietrasiewicz 51800a2430fSAndrzej Pietrasiewicz return result; 51900a2430fSAndrzej Pietrasiewicz } 52000a2430fSAndrzej Pietrasiewicz 52175ab2256SChristoph Hellwig static ssize_t f_lb_opts_bulk_buflen_store(struct config_item *item, 52200a2430fSAndrzej Pietrasiewicz const char *page, size_t len) 52300a2430fSAndrzej Pietrasiewicz { 52475ab2256SChristoph Hellwig struct f_lb_opts *opts = to_f_lb_opts(item); 52500a2430fSAndrzej Pietrasiewicz int ret; 52600a2430fSAndrzej Pietrasiewicz u32 num; 52700a2430fSAndrzej Pietrasiewicz 52800a2430fSAndrzej Pietrasiewicz mutex_lock(&opts->lock); 52900a2430fSAndrzej Pietrasiewicz if (opts->refcnt) { 53000a2430fSAndrzej Pietrasiewicz ret = -EBUSY; 53100a2430fSAndrzej Pietrasiewicz goto end; 53200a2430fSAndrzej Pietrasiewicz } 53300a2430fSAndrzej Pietrasiewicz 53400a2430fSAndrzej Pietrasiewicz ret = kstrtou32(page, 0, &num); 53500a2430fSAndrzej Pietrasiewicz if (ret) 53600a2430fSAndrzej Pietrasiewicz goto end; 53700a2430fSAndrzej Pietrasiewicz 53800a2430fSAndrzej Pietrasiewicz opts->bulk_buflen = num; 53900a2430fSAndrzej Pietrasiewicz ret = len; 54000a2430fSAndrzej Pietrasiewicz end: 54100a2430fSAndrzej Pietrasiewicz mutex_unlock(&opts->lock); 54200a2430fSAndrzej Pietrasiewicz return ret; 54300a2430fSAndrzej Pietrasiewicz } 54400a2430fSAndrzej Pietrasiewicz 54575ab2256SChristoph Hellwig CONFIGFS_ATTR(f_lb_opts_, bulk_buflen); 54600a2430fSAndrzej Pietrasiewicz 54700a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *lb_attrs[] = { 54875ab2256SChristoph Hellwig &f_lb_opts_attr_qlen, 54975ab2256SChristoph Hellwig &f_lb_opts_attr_bulk_buflen, 55000a2430fSAndrzej Pietrasiewicz NULL, 55100a2430fSAndrzej Pietrasiewicz }; 55200a2430fSAndrzej Pietrasiewicz 55397363902SBhumika Goyal static const struct config_item_type lb_func_type = { 55400a2430fSAndrzej Pietrasiewicz .ct_item_ops = &lb_item_ops, 55500a2430fSAndrzej Pietrasiewicz .ct_attrs = lb_attrs, 55600a2430fSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 55700a2430fSAndrzej Pietrasiewicz }; 55800a2430fSAndrzej Pietrasiewicz 55900a2430fSAndrzej Pietrasiewicz static void lb_free_instance(struct usb_function_instance *fi) 56000a2430fSAndrzej Pietrasiewicz { 56100a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 56200a2430fSAndrzej Pietrasiewicz 56300a2430fSAndrzej Pietrasiewicz lb_opts = container_of(fi, struct f_lb_opts, func_inst); 56400a2430fSAndrzej Pietrasiewicz kfree(lb_opts); 56500a2430fSAndrzej Pietrasiewicz } 56600a2430fSAndrzej Pietrasiewicz 56700a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *loopback_alloc_instance(void) 56800a2430fSAndrzej Pietrasiewicz { 56900a2430fSAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 57000a2430fSAndrzej Pietrasiewicz 57100a2430fSAndrzej Pietrasiewicz lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL); 57200a2430fSAndrzej Pietrasiewicz if (!lb_opts) 57300a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 57400a2430fSAndrzej Pietrasiewicz mutex_init(&lb_opts->lock); 57500a2430fSAndrzej Pietrasiewicz lb_opts->func_inst.free_func_inst = lb_free_instance; 57600a2430fSAndrzej Pietrasiewicz lb_opts->bulk_buflen = GZERO_BULK_BUFLEN; 57700a2430fSAndrzej Pietrasiewicz lb_opts->qlen = GZERO_QLEN; 57800a2430fSAndrzej Pietrasiewicz 57900a2430fSAndrzej Pietrasiewicz config_group_init_type_name(&lb_opts->func_inst.group, "", 58000a2430fSAndrzej Pietrasiewicz &lb_func_type); 58100a2430fSAndrzej Pietrasiewicz 58200a2430fSAndrzej Pietrasiewicz return &lb_opts->func_inst; 58300a2430fSAndrzej Pietrasiewicz } 58400a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc); 58500a2430fSAndrzej Pietrasiewicz 58600a2430fSAndrzej Pietrasiewicz int __init lb_modinit(void) 58700a2430fSAndrzej Pietrasiewicz { 5885387c920SColin Ian King return usb_function_register(&Loopbackusb_func); 58900a2430fSAndrzej Pietrasiewicz } 5905387c920SColin Ian King 59100a2430fSAndrzej Pietrasiewicz void __exit lb_modexit(void) 59200a2430fSAndrzej Pietrasiewicz { 59300a2430fSAndrzej Pietrasiewicz usb_function_unregister(&Loopbackusb_func); 59400a2430fSAndrzej Pietrasiewicz } 59500a2430fSAndrzej Pietrasiewicz 596*1cb9ba5eSJeff Johnson MODULE_DESCRIPTION("USB peripheral loopback configuration driver"); 59700a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 598