1*8443f2d2SAndrzej Pietrasiewicz /* 2*8443f2d2SAndrzej Pietrasiewicz * zero.c -- Gadget Zero, for USB development 3*8443f2d2SAndrzej Pietrasiewicz * 4*8443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2003-2008 David Brownell 5*8443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2008 by Nokia Corporation 6*8443f2d2SAndrzej Pietrasiewicz * 7*8443f2d2SAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 8*8443f2d2SAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 9*8443f2d2SAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 10*8443f2d2SAndrzej Pietrasiewicz * (at your option) any later version. 11*8443f2d2SAndrzej Pietrasiewicz */ 12*8443f2d2SAndrzej Pietrasiewicz 13*8443f2d2SAndrzej Pietrasiewicz /* 14*8443f2d2SAndrzej Pietrasiewicz * Gadget Zero only needs two bulk endpoints, and is an example of how you 15*8443f2d2SAndrzej Pietrasiewicz * can write a hardware-agnostic gadget driver running inside a USB device. 16*8443f2d2SAndrzej Pietrasiewicz * Some hardware details are visible, but don't affect most of the driver. 17*8443f2d2SAndrzej Pietrasiewicz * 18*8443f2d2SAndrzej Pietrasiewicz * Use it with the Linux host/master side "usbtest" driver to get a basic 19*8443f2d2SAndrzej Pietrasiewicz * functional test of your device-side usb stack, or with "usb-skeleton". 20*8443f2d2SAndrzej Pietrasiewicz * 21*8443f2d2SAndrzej Pietrasiewicz * It supports two similar configurations. One sinks whatever the usb host 22*8443f2d2SAndrzej Pietrasiewicz * writes, and in return sources zeroes. The other loops whatever the host 23*8443f2d2SAndrzej Pietrasiewicz * writes back, so the host can read it. 24*8443f2d2SAndrzej Pietrasiewicz * 25*8443f2d2SAndrzej Pietrasiewicz * Many drivers will only have one configuration, letting them be much 26*8443f2d2SAndrzej Pietrasiewicz * simpler if they also don't support high speed operation (like this 27*8443f2d2SAndrzej Pietrasiewicz * driver does). 28*8443f2d2SAndrzej Pietrasiewicz * 29*8443f2d2SAndrzej Pietrasiewicz * Why is *this* driver using two configurations, rather than setting up 30*8443f2d2SAndrzej Pietrasiewicz * two interfaces with different functions? To help verify that multiple 31*8443f2d2SAndrzej Pietrasiewicz * configuration infrastucture is working correctly; also, so that it can 32*8443f2d2SAndrzej Pietrasiewicz * work with low capability USB controllers without four bulk endpoints. 33*8443f2d2SAndrzej Pietrasiewicz */ 34*8443f2d2SAndrzej Pietrasiewicz 35*8443f2d2SAndrzej Pietrasiewicz /* 36*8443f2d2SAndrzej Pietrasiewicz * driver assumes self-powered hardware, and 37*8443f2d2SAndrzej Pietrasiewicz * has no way for users to trigger remote wakeup. 38*8443f2d2SAndrzej Pietrasiewicz */ 39*8443f2d2SAndrzej Pietrasiewicz 40*8443f2d2SAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */ 41*8443f2d2SAndrzej Pietrasiewicz 42*8443f2d2SAndrzej Pietrasiewicz #include <linux/kernel.h> 43*8443f2d2SAndrzej Pietrasiewicz #include <linux/slab.h> 44*8443f2d2SAndrzej Pietrasiewicz #include <linux/device.h> 45*8443f2d2SAndrzej Pietrasiewicz #include <linux/module.h> 46*8443f2d2SAndrzej Pietrasiewicz #include <linux/err.h> 47*8443f2d2SAndrzej Pietrasiewicz #include <linux/usb/composite.h> 48*8443f2d2SAndrzej Pietrasiewicz 49*8443f2d2SAndrzej Pietrasiewicz #include "g_zero.h" 50*8443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 51*8443f2d2SAndrzej Pietrasiewicz USB_GADGET_COMPOSITE_OPTIONS(); 52*8443f2d2SAndrzej Pietrasiewicz 53*8443f2d2SAndrzej Pietrasiewicz #define DRIVER_VERSION "Cinco de Mayo 2008" 54*8443f2d2SAndrzej Pietrasiewicz 55*8443f2d2SAndrzej Pietrasiewicz static const char longname[] = "Gadget Zero"; 56*8443f2d2SAndrzej Pietrasiewicz 57*8443f2d2SAndrzej Pietrasiewicz /* 58*8443f2d2SAndrzej Pietrasiewicz * Normally the "loopback" configuration is second (index 1) so 59*8443f2d2SAndrzej Pietrasiewicz * it's not the default. Here's where to change that order, to 60*8443f2d2SAndrzej Pietrasiewicz * work better with hosts where config changes are problematic or 61*8443f2d2SAndrzej Pietrasiewicz * controllers (like original superh) that only support one config. 62*8443f2d2SAndrzej Pietrasiewicz */ 63*8443f2d2SAndrzej Pietrasiewicz static bool loopdefault = 0; 64*8443f2d2SAndrzej Pietrasiewicz module_param(loopdefault, bool, S_IRUGO|S_IWUSR); 65*8443f2d2SAndrzej Pietrasiewicz 66*8443f2d2SAndrzej Pietrasiewicz static struct usb_zero_options gzero_options = { 67*8443f2d2SAndrzej Pietrasiewicz .isoc_interval = GZERO_ISOC_INTERVAL, 68*8443f2d2SAndrzej Pietrasiewicz .isoc_maxpacket = GZERO_ISOC_MAXPACKET, 69*8443f2d2SAndrzej Pietrasiewicz .bulk_buflen = GZERO_BULK_BUFLEN, 70*8443f2d2SAndrzej Pietrasiewicz .qlen = GZERO_QLEN, 71*8443f2d2SAndrzej Pietrasiewicz }; 72*8443f2d2SAndrzej Pietrasiewicz 73*8443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 74*8443f2d2SAndrzej Pietrasiewicz 75*8443f2d2SAndrzej Pietrasiewicz /* Thanks to NetChip Technologies for donating this product ID. 76*8443f2d2SAndrzej Pietrasiewicz * 77*8443f2d2SAndrzej Pietrasiewicz * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! 78*8443f2d2SAndrzej Pietrasiewicz * Instead: allocate your own, using normal USB-IF procedures. 79*8443f2d2SAndrzej Pietrasiewicz */ 80*8443f2d2SAndrzej Pietrasiewicz #ifndef CONFIG_USB_ZERO_HNPTEST 81*8443f2d2SAndrzej Pietrasiewicz #define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ 82*8443f2d2SAndrzej Pietrasiewicz #define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */ 83*8443f2d2SAndrzej Pietrasiewicz #define DEFAULT_AUTORESUME 0 84*8443f2d2SAndrzej Pietrasiewicz #else 85*8443f2d2SAndrzej Pietrasiewicz #define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */ 86*8443f2d2SAndrzej Pietrasiewicz #define DRIVER_PRODUCT_NUM 0xbadd 87*8443f2d2SAndrzej Pietrasiewicz #define DEFAULT_AUTORESUME 5 88*8443f2d2SAndrzej Pietrasiewicz #endif 89*8443f2d2SAndrzej Pietrasiewicz 90*8443f2d2SAndrzej Pietrasiewicz /* If the optional "autoresume" mode is enabled, it provides good 91*8443f2d2SAndrzej Pietrasiewicz * functional coverage for the "USBCV" test harness from USB-IF. 92*8443f2d2SAndrzej Pietrasiewicz * It's always set if OTG mode is enabled. 93*8443f2d2SAndrzej Pietrasiewicz */ 94*8443f2d2SAndrzej Pietrasiewicz static unsigned autoresume = DEFAULT_AUTORESUME; 95*8443f2d2SAndrzej Pietrasiewicz module_param(autoresume, uint, S_IRUGO); 96*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); 97*8443f2d2SAndrzej Pietrasiewicz 98*8443f2d2SAndrzej Pietrasiewicz /* Maximum Autoresume time */ 99*8443f2d2SAndrzej Pietrasiewicz static unsigned max_autoresume; 100*8443f2d2SAndrzej Pietrasiewicz module_param(max_autoresume, uint, S_IRUGO); 101*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup"); 102*8443f2d2SAndrzej Pietrasiewicz 103*8443f2d2SAndrzej Pietrasiewicz /* Interval between two remote wakeups */ 104*8443f2d2SAndrzej Pietrasiewicz static unsigned autoresume_interval_ms; 105*8443f2d2SAndrzej Pietrasiewicz module_param(autoresume_interval_ms, uint, S_IRUGO); 106*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(autoresume_interval_ms, 107*8443f2d2SAndrzej Pietrasiewicz "milliseconds to increase successive wakeup delays"); 108*8443f2d2SAndrzej Pietrasiewicz 109*8443f2d2SAndrzej Pietrasiewicz static unsigned autoresume_step_ms; 110*8443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 111*8443f2d2SAndrzej Pietrasiewicz 112*8443f2d2SAndrzej Pietrasiewicz static struct usb_device_descriptor device_desc = { 113*8443f2d2SAndrzej Pietrasiewicz .bLength = sizeof device_desc, 114*8443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_DEVICE, 115*8443f2d2SAndrzej Pietrasiewicz 116*8443f2d2SAndrzej Pietrasiewicz .bcdUSB = cpu_to_le16(0x0200), 117*8443f2d2SAndrzej Pietrasiewicz .bDeviceClass = USB_CLASS_VENDOR_SPEC, 118*8443f2d2SAndrzej Pietrasiewicz 119*8443f2d2SAndrzej Pietrasiewicz .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), 120*8443f2d2SAndrzej Pietrasiewicz .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), 121*8443f2d2SAndrzej Pietrasiewicz .bNumConfigurations = 2, 122*8443f2d2SAndrzej Pietrasiewicz }; 123*8443f2d2SAndrzej Pietrasiewicz 124*8443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_OTG 125*8443f2d2SAndrzej Pietrasiewicz static struct usb_otg_descriptor otg_descriptor = { 126*8443f2d2SAndrzej Pietrasiewicz .bLength = sizeof otg_descriptor, 127*8443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_OTG, 128*8443f2d2SAndrzej Pietrasiewicz 129*8443f2d2SAndrzej Pietrasiewicz /* REVISIT SRP-only hardware is possible, although 130*8443f2d2SAndrzej Pietrasiewicz * it would not be called "OTG" ... 131*8443f2d2SAndrzej Pietrasiewicz */ 132*8443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, 133*8443f2d2SAndrzej Pietrasiewicz }; 134*8443f2d2SAndrzej Pietrasiewicz 135*8443f2d2SAndrzej Pietrasiewicz static const struct usb_descriptor_header *otg_desc[] = { 136*8443f2d2SAndrzej Pietrasiewicz (struct usb_descriptor_header *) &otg_descriptor, 137*8443f2d2SAndrzej Pietrasiewicz NULL, 138*8443f2d2SAndrzej Pietrasiewicz }; 139*8443f2d2SAndrzej Pietrasiewicz #else 140*8443f2d2SAndrzej Pietrasiewicz #define otg_desc NULL 141*8443f2d2SAndrzej Pietrasiewicz #endif 142*8443f2d2SAndrzej Pietrasiewicz 143*8443f2d2SAndrzej Pietrasiewicz /* string IDs are assigned dynamically */ 144*8443f2d2SAndrzej Pietrasiewicz /* default serial number takes at least two packets */ 145*8443f2d2SAndrzej Pietrasiewicz static char serial[] = "0123456789.0123456789.0123456789"; 146*8443f2d2SAndrzej Pietrasiewicz 147*8443f2d2SAndrzej Pietrasiewicz #define USB_GZERO_SS_DESC (USB_GADGET_FIRST_AVAIL_IDX + 0) 148*8443f2d2SAndrzej Pietrasiewicz #define USB_GZERO_LB_DESC (USB_GADGET_FIRST_AVAIL_IDX + 1) 149*8443f2d2SAndrzej Pietrasiewicz 150*8443f2d2SAndrzej Pietrasiewicz static struct usb_string strings_dev[] = { 151*8443f2d2SAndrzej Pietrasiewicz [USB_GADGET_MANUFACTURER_IDX].s = "", 152*8443f2d2SAndrzej Pietrasiewicz [USB_GADGET_PRODUCT_IDX].s = longname, 153*8443f2d2SAndrzej Pietrasiewicz [USB_GADGET_SERIAL_IDX].s = serial, 154*8443f2d2SAndrzej Pietrasiewicz [USB_GZERO_SS_DESC].s = "source and sink data", 155*8443f2d2SAndrzej Pietrasiewicz [USB_GZERO_LB_DESC].s = "loop input to output", 156*8443f2d2SAndrzej Pietrasiewicz { } /* end of list */ 157*8443f2d2SAndrzej Pietrasiewicz }; 158*8443f2d2SAndrzej Pietrasiewicz 159*8443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_dev = { 160*8443f2d2SAndrzej Pietrasiewicz .language = 0x0409, /* en-us */ 161*8443f2d2SAndrzej Pietrasiewicz .strings = strings_dev, 162*8443f2d2SAndrzej Pietrasiewicz }; 163*8443f2d2SAndrzej Pietrasiewicz 164*8443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings *dev_strings[] = { 165*8443f2d2SAndrzej Pietrasiewicz &stringtab_dev, 166*8443f2d2SAndrzej Pietrasiewicz NULL, 167*8443f2d2SAndrzej Pietrasiewicz }; 168*8443f2d2SAndrzej Pietrasiewicz 169*8443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 170*8443f2d2SAndrzej Pietrasiewicz 171*8443f2d2SAndrzej Pietrasiewicz static struct timer_list autoresume_timer; 172*8443f2d2SAndrzej Pietrasiewicz 173*8443f2d2SAndrzej Pietrasiewicz static void zero_autoresume(unsigned long _c) 174*8443f2d2SAndrzej Pietrasiewicz { 175*8443f2d2SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = (void *)_c; 176*8443f2d2SAndrzej Pietrasiewicz struct usb_gadget *g = cdev->gadget; 177*8443f2d2SAndrzej Pietrasiewicz 178*8443f2d2SAndrzej Pietrasiewicz /* unconfigured devices can't issue wakeups */ 179*8443f2d2SAndrzej Pietrasiewicz if (!cdev->config) 180*8443f2d2SAndrzej Pietrasiewicz return; 181*8443f2d2SAndrzej Pietrasiewicz 182*8443f2d2SAndrzej Pietrasiewicz /* Normally the host would be woken up for something 183*8443f2d2SAndrzej Pietrasiewicz * more significant than just a timer firing; likely 184*8443f2d2SAndrzej Pietrasiewicz * because of some direct user request. 185*8443f2d2SAndrzej Pietrasiewicz */ 186*8443f2d2SAndrzej Pietrasiewicz if (g->speed != USB_SPEED_UNKNOWN) { 187*8443f2d2SAndrzej Pietrasiewicz int status = usb_gadget_wakeup(g); 188*8443f2d2SAndrzej Pietrasiewicz INFO(cdev, "%s --> %d\n", __func__, status); 189*8443f2d2SAndrzej Pietrasiewicz } 190*8443f2d2SAndrzej Pietrasiewicz } 191*8443f2d2SAndrzej Pietrasiewicz 192*8443f2d2SAndrzej Pietrasiewicz static void zero_suspend(struct usb_composite_dev *cdev) 193*8443f2d2SAndrzej Pietrasiewicz { 194*8443f2d2SAndrzej Pietrasiewicz if (cdev->gadget->speed == USB_SPEED_UNKNOWN) 195*8443f2d2SAndrzej Pietrasiewicz return; 196*8443f2d2SAndrzej Pietrasiewicz 197*8443f2d2SAndrzej Pietrasiewicz if (autoresume) { 198*8443f2d2SAndrzej Pietrasiewicz if (max_autoresume && 199*8443f2d2SAndrzej Pietrasiewicz (autoresume_step_ms > max_autoresume * 1000)) 200*8443f2d2SAndrzej Pietrasiewicz autoresume_step_ms = autoresume * 1000; 201*8443f2d2SAndrzej Pietrasiewicz 202*8443f2d2SAndrzej Pietrasiewicz mod_timer(&autoresume_timer, jiffies + 203*8443f2d2SAndrzej Pietrasiewicz msecs_to_jiffies(autoresume_step_ms)); 204*8443f2d2SAndrzej Pietrasiewicz DBG(cdev, "suspend, wakeup in %d milliseconds\n", 205*8443f2d2SAndrzej Pietrasiewicz autoresume_step_ms); 206*8443f2d2SAndrzej Pietrasiewicz 207*8443f2d2SAndrzej Pietrasiewicz autoresume_step_ms += autoresume_interval_ms; 208*8443f2d2SAndrzej Pietrasiewicz } else 209*8443f2d2SAndrzej Pietrasiewicz DBG(cdev, "%s\n", __func__); 210*8443f2d2SAndrzej Pietrasiewicz } 211*8443f2d2SAndrzej Pietrasiewicz 212*8443f2d2SAndrzej Pietrasiewicz static void zero_resume(struct usb_composite_dev *cdev) 213*8443f2d2SAndrzej Pietrasiewicz { 214*8443f2d2SAndrzej Pietrasiewicz DBG(cdev, "%s\n", __func__); 215*8443f2d2SAndrzej Pietrasiewicz del_timer(&autoresume_timer); 216*8443f2d2SAndrzej Pietrasiewicz } 217*8443f2d2SAndrzej Pietrasiewicz 218*8443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 219*8443f2d2SAndrzej Pietrasiewicz 220*8443f2d2SAndrzej Pietrasiewicz static struct usb_configuration loopback_driver = { 221*8443f2d2SAndrzej Pietrasiewicz .label = "loopback", 222*8443f2d2SAndrzej Pietrasiewicz .bConfigurationValue = 2, 223*8443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 224*8443f2d2SAndrzej Pietrasiewicz /* .iConfiguration = DYNAMIC */ 225*8443f2d2SAndrzej Pietrasiewicz }; 226*8443f2d2SAndrzej Pietrasiewicz 227*8443f2d2SAndrzej Pietrasiewicz static struct usb_function *func_ss; 228*8443f2d2SAndrzej Pietrasiewicz static struct usb_function_instance *func_inst_ss; 229*8443f2d2SAndrzej Pietrasiewicz 230*8443f2d2SAndrzej Pietrasiewicz static int ss_config_setup(struct usb_configuration *c, 231*8443f2d2SAndrzej Pietrasiewicz const struct usb_ctrlrequest *ctrl) 232*8443f2d2SAndrzej Pietrasiewicz { 233*8443f2d2SAndrzej Pietrasiewicz switch (ctrl->bRequest) { 234*8443f2d2SAndrzej Pietrasiewicz case 0x5b: 235*8443f2d2SAndrzej Pietrasiewicz case 0x5c: 236*8443f2d2SAndrzej Pietrasiewicz return func_ss->setup(func_ss, ctrl); 237*8443f2d2SAndrzej Pietrasiewicz default: 238*8443f2d2SAndrzej Pietrasiewicz return -EOPNOTSUPP; 239*8443f2d2SAndrzej Pietrasiewicz } 240*8443f2d2SAndrzej Pietrasiewicz } 241*8443f2d2SAndrzej Pietrasiewicz 242*8443f2d2SAndrzej Pietrasiewicz static struct usb_configuration sourcesink_driver = { 243*8443f2d2SAndrzej Pietrasiewicz .label = "source/sink", 244*8443f2d2SAndrzej Pietrasiewicz .setup = ss_config_setup, 245*8443f2d2SAndrzej Pietrasiewicz .bConfigurationValue = 3, 246*8443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 247*8443f2d2SAndrzej Pietrasiewicz /* .iConfiguration = DYNAMIC */ 248*8443f2d2SAndrzej Pietrasiewicz }; 249*8443f2d2SAndrzej Pietrasiewicz 250*8443f2d2SAndrzej Pietrasiewicz module_param_named(buflen, gzero_options.bulk_buflen, uint, 0); 251*8443f2d2SAndrzej Pietrasiewicz module_param_named(pattern, gzero_options.pattern, uint, S_IRUGO|S_IWUSR); 252*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none"); 253*8443f2d2SAndrzej Pietrasiewicz 254*8443f2d2SAndrzej Pietrasiewicz module_param_named(isoc_interval, gzero_options.isoc_interval, uint, 255*8443f2d2SAndrzej Pietrasiewicz S_IRUGO|S_IWUSR); 256*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(isoc_interval, "1 - 16"); 257*8443f2d2SAndrzej Pietrasiewicz 258*8443f2d2SAndrzej Pietrasiewicz module_param_named(isoc_maxpacket, gzero_options.isoc_maxpacket, uint, 259*8443f2d2SAndrzej Pietrasiewicz S_IRUGO|S_IWUSR); 260*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); 261*8443f2d2SAndrzej Pietrasiewicz 262*8443f2d2SAndrzej Pietrasiewicz module_param_named(isoc_mult, gzero_options.isoc_mult, uint, S_IRUGO|S_IWUSR); 263*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)"); 264*8443f2d2SAndrzej Pietrasiewicz 265*8443f2d2SAndrzej Pietrasiewicz module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, 266*8443f2d2SAndrzej Pietrasiewicz S_IRUGO|S_IWUSR); 267*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); 268*8443f2d2SAndrzej Pietrasiewicz 269*8443f2d2SAndrzej Pietrasiewicz static struct usb_function *func_lb; 270*8443f2d2SAndrzej Pietrasiewicz static struct usb_function_instance *func_inst_lb; 271*8443f2d2SAndrzej Pietrasiewicz 272*8443f2d2SAndrzej Pietrasiewicz module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); 273*8443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(qlen, "depth of loopback queue"); 274*8443f2d2SAndrzej Pietrasiewicz 275*8443f2d2SAndrzej Pietrasiewicz static int __init zero_bind(struct usb_composite_dev *cdev) 276*8443f2d2SAndrzej Pietrasiewicz { 277*8443f2d2SAndrzej Pietrasiewicz struct f_ss_opts *ss_opts; 278*8443f2d2SAndrzej Pietrasiewicz struct f_lb_opts *lb_opts; 279*8443f2d2SAndrzej Pietrasiewicz int status; 280*8443f2d2SAndrzej Pietrasiewicz 281*8443f2d2SAndrzej Pietrasiewicz /* Allocate string descriptor numbers ... note that string 282*8443f2d2SAndrzej Pietrasiewicz * contents can be overridden by the composite_dev glue. 283*8443f2d2SAndrzej Pietrasiewicz */ 284*8443f2d2SAndrzej Pietrasiewicz status = usb_string_ids_tab(cdev, strings_dev); 285*8443f2d2SAndrzej Pietrasiewicz if (status < 0) 286*8443f2d2SAndrzej Pietrasiewicz return status; 287*8443f2d2SAndrzej Pietrasiewicz 288*8443f2d2SAndrzej Pietrasiewicz device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; 289*8443f2d2SAndrzej Pietrasiewicz device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; 290*8443f2d2SAndrzej Pietrasiewicz device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id; 291*8443f2d2SAndrzej Pietrasiewicz 292*8443f2d2SAndrzej Pietrasiewicz setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev); 293*8443f2d2SAndrzej Pietrasiewicz 294*8443f2d2SAndrzej Pietrasiewicz func_inst_ss = usb_get_function_instance("SourceSink"); 295*8443f2d2SAndrzej Pietrasiewicz if (IS_ERR(func_inst_ss)) 296*8443f2d2SAndrzej Pietrasiewicz return PTR_ERR(func_inst_ss); 297*8443f2d2SAndrzej Pietrasiewicz 298*8443f2d2SAndrzej Pietrasiewicz ss_opts = container_of(func_inst_ss, struct f_ss_opts, func_inst); 299*8443f2d2SAndrzej Pietrasiewicz ss_opts->pattern = gzero_options.pattern; 300*8443f2d2SAndrzej Pietrasiewicz ss_opts->isoc_interval = gzero_options.isoc_interval; 301*8443f2d2SAndrzej Pietrasiewicz ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; 302*8443f2d2SAndrzej Pietrasiewicz ss_opts->isoc_mult = gzero_options.isoc_mult; 303*8443f2d2SAndrzej Pietrasiewicz ss_opts->isoc_maxburst = gzero_options.isoc_maxburst; 304*8443f2d2SAndrzej Pietrasiewicz ss_opts->bulk_buflen = gzero_options.bulk_buflen; 305*8443f2d2SAndrzej Pietrasiewicz 306*8443f2d2SAndrzej Pietrasiewicz func_ss = usb_get_function(func_inst_ss); 307*8443f2d2SAndrzej Pietrasiewicz if (IS_ERR(func_ss)) { 308*8443f2d2SAndrzej Pietrasiewicz status = PTR_ERR(func_ss); 309*8443f2d2SAndrzej Pietrasiewicz goto err_put_func_inst_ss; 310*8443f2d2SAndrzej Pietrasiewicz } 311*8443f2d2SAndrzej Pietrasiewicz 312*8443f2d2SAndrzej Pietrasiewicz func_inst_lb = usb_get_function_instance("Loopback"); 313*8443f2d2SAndrzej Pietrasiewicz if (IS_ERR(func_inst_lb)) { 314*8443f2d2SAndrzej Pietrasiewicz status = PTR_ERR(func_inst_lb); 315*8443f2d2SAndrzej Pietrasiewicz goto err_put_func_ss; 316*8443f2d2SAndrzej Pietrasiewicz } 317*8443f2d2SAndrzej Pietrasiewicz 318*8443f2d2SAndrzej Pietrasiewicz lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst); 319*8443f2d2SAndrzej Pietrasiewicz lb_opts->bulk_buflen = gzero_options.bulk_buflen; 320*8443f2d2SAndrzej Pietrasiewicz lb_opts->qlen = gzero_options.qlen; 321*8443f2d2SAndrzej Pietrasiewicz 322*8443f2d2SAndrzej Pietrasiewicz func_lb = usb_get_function(func_inst_lb); 323*8443f2d2SAndrzej Pietrasiewicz if (IS_ERR(func_lb)) { 324*8443f2d2SAndrzej Pietrasiewicz status = PTR_ERR(func_lb); 325*8443f2d2SAndrzej Pietrasiewicz goto err_put_func_inst_lb; 326*8443f2d2SAndrzej Pietrasiewicz } 327*8443f2d2SAndrzej Pietrasiewicz 328*8443f2d2SAndrzej Pietrasiewicz sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id; 329*8443f2d2SAndrzej Pietrasiewicz loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id; 330*8443f2d2SAndrzej Pietrasiewicz 331*8443f2d2SAndrzej Pietrasiewicz /* support autoresume for remote wakeup testing */ 332*8443f2d2SAndrzej Pietrasiewicz sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; 333*8443f2d2SAndrzej Pietrasiewicz loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; 334*8443f2d2SAndrzej Pietrasiewicz sourcesink_driver.descriptors = NULL; 335*8443f2d2SAndrzej Pietrasiewicz loopback_driver.descriptors = NULL; 336*8443f2d2SAndrzej Pietrasiewicz if (autoresume) { 337*8443f2d2SAndrzej Pietrasiewicz sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 338*8443f2d2SAndrzej Pietrasiewicz loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 339*8443f2d2SAndrzej Pietrasiewicz autoresume_step_ms = autoresume * 1000; 340*8443f2d2SAndrzej Pietrasiewicz } 341*8443f2d2SAndrzej Pietrasiewicz 342*8443f2d2SAndrzej Pietrasiewicz /* support OTG systems */ 343*8443f2d2SAndrzej Pietrasiewicz if (gadget_is_otg(cdev->gadget)) { 344*8443f2d2SAndrzej Pietrasiewicz sourcesink_driver.descriptors = otg_desc; 345*8443f2d2SAndrzej Pietrasiewicz sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 346*8443f2d2SAndrzej Pietrasiewicz loopback_driver.descriptors = otg_desc; 347*8443f2d2SAndrzej Pietrasiewicz loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 348*8443f2d2SAndrzej Pietrasiewicz } 349*8443f2d2SAndrzej Pietrasiewicz 350*8443f2d2SAndrzej Pietrasiewicz /* Register primary, then secondary configuration. Note that 351*8443f2d2SAndrzej Pietrasiewicz * SH3 only allows one config... 352*8443f2d2SAndrzej Pietrasiewicz */ 353*8443f2d2SAndrzej Pietrasiewicz if (loopdefault) { 354*8443f2d2SAndrzej Pietrasiewicz usb_add_config_only(cdev, &loopback_driver); 355*8443f2d2SAndrzej Pietrasiewicz usb_add_config_only(cdev, &sourcesink_driver); 356*8443f2d2SAndrzej Pietrasiewicz } else { 357*8443f2d2SAndrzej Pietrasiewicz usb_add_config_only(cdev, &sourcesink_driver); 358*8443f2d2SAndrzej Pietrasiewicz usb_add_config_only(cdev, &loopback_driver); 359*8443f2d2SAndrzej Pietrasiewicz } 360*8443f2d2SAndrzej Pietrasiewicz status = usb_add_function(&sourcesink_driver, func_ss); 361*8443f2d2SAndrzej Pietrasiewicz if (status) 362*8443f2d2SAndrzej Pietrasiewicz goto err_conf_flb; 363*8443f2d2SAndrzej Pietrasiewicz 364*8443f2d2SAndrzej Pietrasiewicz usb_ep_autoconfig_reset(cdev->gadget); 365*8443f2d2SAndrzej Pietrasiewicz status = usb_add_function(&loopback_driver, func_lb); 366*8443f2d2SAndrzej Pietrasiewicz if (status) 367*8443f2d2SAndrzej Pietrasiewicz goto err_conf_flb; 368*8443f2d2SAndrzej Pietrasiewicz 369*8443f2d2SAndrzej Pietrasiewicz usb_ep_autoconfig_reset(cdev->gadget); 370*8443f2d2SAndrzej Pietrasiewicz usb_composite_overwrite_options(cdev, &coverwrite); 371*8443f2d2SAndrzej Pietrasiewicz 372*8443f2d2SAndrzej Pietrasiewicz INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); 373*8443f2d2SAndrzej Pietrasiewicz 374*8443f2d2SAndrzej Pietrasiewicz return 0; 375*8443f2d2SAndrzej Pietrasiewicz 376*8443f2d2SAndrzej Pietrasiewicz err_conf_flb: 377*8443f2d2SAndrzej Pietrasiewicz usb_put_function(func_lb); 378*8443f2d2SAndrzej Pietrasiewicz func_lb = NULL; 379*8443f2d2SAndrzej Pietrasiewicz err_put_func_inst_lb: 380*8443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(func_inst_lb); 381*8443f2d2SAndrzej Pietrasiewicz func_inst_lb = NULL; 382*8443f2d2SAndrzej Pietrasiewicz err_put_func_ss: 383*8443f2d2SAndrzej Pietrasiewicz usb_put_function(func_ss); 384*8443f2d2SAndrzej Pietrasiewicz func_ss = NULL; 385*8443f2d2SAndrzej Pietrasiewicz err_put_func_inst_ss: 386*8443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(func_inst_ss); 387*8443f2d2SAndrzej Pietrasiewicz func_inst_ss = NULL; 388*8443f2d2SAndrzej Pietrasiewicz return status; 389*8443f2d2SAndrzej Pietrasiewicz } 390*8443f2d2SAndrzej Pietrasiewicz 391*8443f2d2SAndrzej Pietrasiewicz static int zero_unbind(struct usb_composite_dev *cdev) 392*8443f2d2SAndrzej Pietrasiewicz { 393*8443f2d2SAndrzej Pietrasiewicz del_timer_sync(&autoresume_timer); 394*8443f2d2SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(func_ss)) 395*8443f2d2SAndrzej Pietrasiewicz usb_put_function(func_ss); 396*8443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(func_inst_ss); 397*8443f2d2SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(func_lb)) 398*8443f2d2SAndrzej Pietrasiewicz usb_put_function(func_lb); 399*8443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(func_inst_lb); 400*8443f2d2SAndrzej Pietrasiewicz return 0; 401*8443f2d2SAndrzej Pietrasiewicz } 402*8443f2d2SAndrzej Pietrasiewicz 403*8443f2d2SAndrzej Pietrasiewicz static __refdata struct usb_composite_driver zero_driver = { 404*8443f2d2SAndrzej Pietrasiewicz .name = "zero", 405*8443f2d2SAndrzej Pietrasiewicz .dev = &device_desc, 406*8443f2d2SAndrzej Pietrasiewicz .strings = dev_strings, 407*8443f2d2SAndrzej Pietrasiewicz .max_speed = USB_SPEED_SUPER, 408*8443f2d2SAndrzej Pietrasiewicz .bind = zero_bind, 409*8443f2d2SAndrzej Pietrasiewicz .unbind = zero_unbind, 410*8443f2d2SAndrzej Pietrasiewicz .suspend = zero_suspend, 411*8443f2d2SAndrzej Pietrasiewicz .resume = zero_resume, 412*8443f2d2SAndrzej Pietrasiewicz }; 413*8443f2d2SAndrzej Pietrasiewicz 414*8443f2d2SAndrzej Pietrasiewicz module_usb_composite_driver(zero_driver); 415*8443f2d2SAndrzej Pietrasiewicz 416*8443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("David Brownell"); 417*8443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 418