18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * zero.c -- Gadget Zero, for USB development 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2003-2008 David Brownell 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 by Nokia Corporation 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * Gadget Zero only needs two bulk endpoints, and is an example of how you 118c2ecf20Sopenharmony_ci * can write a hardware-agnostic gadget driver running inside a USB device. 128c2ecf20Sopenharmony_ci * Some hardware details are visible, but don't affect most of the driver. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Use it with the Linux host side "usbtest" driver to get a basic functional 158c2ecf20Sopenharmony_ci * test of your device-side usb stack, or with "usb-skeleton". 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * It supports two similar configurations. One sinks whatever the usb host 188c2ecf20Sopenharmony_ci * writes, and in return sources zeroes. The other loops whatever the host 198c2ecf20Sopenharmony_ci * writes back, so the host can read it. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Many drivers will only have one configuration, letting them be much 228c2ecf20Sopenharmony_ci * simpler if they also don't support high speed operation (like this 238c2ecf20Sopenharmony_ci * driver does). 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Why is *this* driver using two configurations, rather than setting up 268c2ecf20Sopenharmony_ci * two interfaces with different functions? To help verify that multiple 278c2ecf20Sopenharmony_ci * configuration infrastructure is working correctly; also, so that it can 288c2ecf20Sopenharmony_ci * work with low capability USB controllers without four bulk endpoints. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * driver assumes self-powered hardware, and 338c2ecf20Sopenharmony_ci * has no way for users to trigger remote wakeup. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* #define VERBOSE_DEBUG */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <linux/kernel.h> 398c2ecf20Sopenharmony_ci#include <linux/slab.h> 408c2ecf20Sopenharmony_ci#include <linux/device.h> 418c2ecf20Sopenharmony_ci#include <linux/module.h> 428c2ecf20Sopenharmony_ci#include <linux/err.h> 438c2ecf20Sopenharmony_ci#include <linux/usb/composite.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include "g_zero.h" 468c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 478c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS(); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define DRIVER_VERSION "Cinco de Mayo 2008" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const char longname[] = "Gadget Zero"; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Normally the "loopback" configuration is second (index 1) so 558c2ecf20Sopenharmony_ci * it's not the default. Here's where to change that order, to 568c2ecf20Sopenharmony_ci * work better with hosts where config changes are problematic or 578c2ecf20Sopenharmony_ci * controllers (like original superh) that only support one config. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic bool loopdefault = 0; 608c2ecf20Sopenharmony_cimodule_param(loopdefault, bool, S_IRUGO|S_IWUSR); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct usb_zero_options gzero_options = { 638c2ecf20Sopenharmony_ci .isoc_interval = GZERO_ISOC_INTERVAL, 648c2ecf20Sopenharmony_ci .isoc_maxpacket = GZERO_ISOC_MAXPACKET, 658c2ecf20Sopenharmony_ci .bulk_buflen = GZERO_BULK_BUFLEN, 668c2ecf20Sopenharmony_ci .qlen = GZERO_QLEN, 678c2ecf20Sopenharmony_ci .ss_bulk_qlen = GZERO_SS_BULK_QLEN, 688c2ecf20Sopenharmony_ci .ss_iso_qlen = GZERO_SS_ISO_QLEN, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Thanks to NetChip Technologies for donating this product ID. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! 768c2ecf20Sopenharmony_ci * Instead: allocate your own, using normal USB-IF procedures. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#ifndef CONFIG_USB_ZERO_HNPTEST 798c2ecf20Sopenharmony_ci#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ 808c2ecf20Sopenharmony_ci#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */ 818c2ecf20Sopenharmony_ci#define DEFAULT_AUTORESUME 0 828c2ecf20Sopenharmony_ci#else 838c2ecf20Sopenharmony_ci#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */ 848c2ecf20Sopenharmony_ci#define DRIVER_PRODUCT_NUM 0xbadd 858c2ecf20Sopenharmony_ci#define DEFAULT_AUTORESUME 5 868c2ecf20Sopenharmony_ci#endif 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* If the optional "autoresume" mode is enabled, it provides good 898c2ecf20Sopenharmony_ci * functional coverage for the "USBCV" test harness from USB-IF. 908c2ecf20Sopenharmony_ci * It's always set if OTG mode is enabled. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic unsigned autoresume = DEFAULT_AUTORESUME; 938c2ecf20Sopenharmony_cimodule_param(autoresume, uint, S_IRUGO); 948c2ecf20Sopenharmony_ciMODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Maximum Autoresume time */ 978c2ecf20Sopenharmony_cistatic unsigned max_autoresume; 988c2ecf20Sopenharmony_cimodule_param(max_autoresume, uint, S_IRUGO); 998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup"); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* Interval between two remote wakeups */ 1028c2ecf20Sopenharmony_cistatic unsigned autoresume_interval_ms; 1038c2ecf20Sopenharmony_cimodule_param(autoresume_interval_ms, uint, S_IRUGO); 1048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(autoresume_interval_ms, 1058c2ecf20Sopenharmony_ci "milliseconds to increase successive wakeup delays"); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic unsigned autoresume_step_ms; 1088c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic struct usb_device_descriptor device_desc = { 1118c2ecf20Sopenharmony_ci .bLength = sizeof device_desc, 1128c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE, 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* .bcdUSB = DYNAMIC */ 1158c2ecf20Sopenharmony_ci .bDeviceClass = USB_CLASS_VENDOR_SPEC, 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), 1188c2ecf20Sopenharmony_ci .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), 1198c2ecf20Sopenharmony_ci .bNumConfigurations = 2, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const struct usb_descriptor_header *otg_desc[2]; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* string IDs are assigned dynamically */ 1258c2ecf20Sopenharmony_ci/* default serial number takes at least two packets */ 1268c2ecf20Sopenharmony_cistatic char serial[] = "0123456789.0123456789.0123456789"; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define USB_GZERO_SS_DESC (USB_GADGET_FIRST_AVAIL_IDX + 0) 1298c2ecf20Sopenharmony_ci#define USB_GZERO_LB_DESC (USB_GADGET_FIRST_AVAIL_IDX + 1) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct usb_string strings_dev[] = { 1328c2ecf20Sopenharmony_ci [USB_GADGET_MANUFACTURER_IDX].s = "", 1338c2ecf20Sopenharmony_ci [USB_GADGET_PRODUCT_IDX].s = longname, 1348c2ecf20Sopenharmony_ci [USB_GADGET_SERIAL_IDX].s = serial, 1358c2ecf20Sopenharmony_ci [USB_GZERO_SS_DESC].s = "source and sink data", 1368c2ecf20Sopenharmony_ci [USB_GZERO_LB_DESC].s = "loop input to output", 1378c2ecf20Sopenharmony_ci { } /* end of list */ 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic struct usb_gadget_strings stringtab_dev = { 1418c2ecf20Sopenharmony_ci .language = 0x0409, /* en-us */ 1428c2ecf20Sopenharmony_ci .strings = strings_dev, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *dev_strings[] = { 1468c2ecf20Sopenharmony_ci &stringtab_dev, 1478c2ecf20Sopenharmony_ci NULL, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct timer_list autoresume_timer; 1538c2ecf20Sopenharmony_cistatic struct usb_composite_dev *autoresume_cdev; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void zero_autoresume(struct timer_list *unused) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev = autoresume_cdev; 1588c2ecf20Sopenharmony_ci struct usb_gadget *g = cdev->gadget; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* unconfigured devices can't issue wakeups */ 1618c2ecf20Sopenharmony_ci if (!cdev->config) 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Normally the host would be woken up for something 1658c2ecf20Sopenharmony_ci * more significant than just a timer firing; likely 1668c2ecf20Sopenharmony_ci * because of some direct user request. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci if (g->speed != USB_SPEED_UNKNOWN) { 1698c2ecf20Sopenharmony_ci int status = usb_gadget_wakeup(g); 1708c2ecf20Sopenharmony_ci INFO(cdev, "%s --> %d\n", __func__, status); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void zero_suspend(struct usb_composite_dev *cdev) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci if (cdev->gadget->speed == USB_SPEED_UNKNOWN) 1778c2ecf20Sopenharmony_ci return; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (autoresume) { 1808c2ecf20Sopenharmony_ci if (max_autoresume && 1818c2ecf20Sopenharmony_ci (autoresume_step_ms > max_autoresume * 1000)) 1828c2ecf20Sopenharmony_ci autoresume_step_ms = autoresume * 1000; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci mod_timer(&autoresume_timer, jiffies + 1858c2ecf20Sopenharmony_ci msecs_to_jiffies(autoresume_step_ms)); 1868c2ecf20Sopenharmony_ci DBG(cdev, "suspend, wakeup in %d milliseconds\n", 1878c2ecf20Sopenharmony_ci autoresume_step_ms); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci autoresume_step_ms += autoresume_interval_ms; 1908c2ecf20Sopenharmony_ci } else 1918c2ecf20Sopenharmony_ci DBG(cdev, "%s\n", __func__); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void zero_resume(struct usb_composite_dev *cdev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci DBG(cdev, "%s\n", __func__); 1978c2ecf20Sopenharmony_ci del_timer(&autoresume_timer); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct usb_configuration loopback_driver = { 2038c2ecf20Sopenharmony_ci .label = "loopback", 2048c2ecf20Sopenharmony_ci .bConfigurationValue = 2, 2058c2ecf20Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 2068c2ecf20Sopenharmony_ci /* .iConfiguration = DYNAMIC */ 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct usb_function *func_ss; 2108c2ecf20Sopenharmony_cistatic struct usb_function_instance *func_inst_ss; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int ss_config_setup(struct usb_configuration *c, 2138c2ecf20Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci switch (ctrl->bRequest) { 2168c2ecf20Sopenharmony_ci case 0x5b: 2178c2ecf20Sopenharmony_ci case 0x5c: 2188c2ecf20Sopenharmony_ci return func_ss->setup(func_ss, ctrl); 2198c2ecf20Sopenharmony_ci default: 2208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic struct usb_configuration sourcesink_driver = { 2258c2ecf20Sopenharmony_ci .label = "source/sink", 2268c2ecf20Sopenharmony_ci .setup = ss_config_setup, 2278c2ecf20Sopenharmony_ci .bConfigurationValue = 3, 2288c2ecf20Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 2298c2ecf20Sopenharmony_ci /* .iConfiguration = DYNAMIC */ 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cimodule_param_named(buflen, gzero_options.bulk_buflen, uint, 0); 2338c2ecf20Sopenharmony_cimodule_param_named(pattern, gzero_options.pattern, uint, S_IRUGO|S_IWUSR); 2348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none"); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cimodule_param_named(isoc_interval, gzero_options.isoc_interval, uint, 2378c2ecf20Sopenharmony_ci S_IRUGO|S_IWUSR); 2388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isoc_interval, "1 - 16"); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cimodule_param_named(isoc_maxpacket, gzero_options.isoc_maxpacket, uint, 2418c2ecf20Sopenharmony_ci S_IRUGO|S_IWUSR); 2428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cimodule_param_named(isoc_mult, gzero_options.isoc_mult, uint, S_IRUGO|S_IWUSR); 2458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)"); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cimodule_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, 2488c2ecf20Sopenharmony_ci S_IRUGO|S_IWUSR); 2498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct usb_function *func_lb; 2528c2ecf20Sopenharmony_cistatic struct usb_function_instance *func_inst_lb; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cimodule_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); 2558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(qlen, "depth of loopback queue"); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cimodule_param_named(ss_bulk_qlen, gzero_options.ss_bulk_qlen, uint, 2588c2ecf20Sopenharmony_ci S_IRUGO|S_IWUSR); 2598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bulk_qlen, "depth of sourcesink queue for bulk transfer"); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cimodule_param_named(ss_iso_qlen, gzero_options.ss_iso_qlen, uint, 2628c2ecf20Sopenharmony_ci S_IRUGO|S_IWUSR); 2638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(iso_qlen, "depth of sourcesink queue for iso transfer"); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int zero_bind(struct usb_composite_dev *cdev) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct f_ss_opts *ss_opts; 2688c2ecf20Sopenharmony_ci struct f_lb_opts *lb_opts; 2698c2ecf20Sopenharmony_ci int status; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Allocate string descriptor numbers ... note that string 2728c2ecf20Sopenharmony_ci * contents can be overridden by the composite_dev glue. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci status = usb_string_ids_tab(cdev, strings_dev); 2758c2ecf20Sopenharmony_ci if (status < 0) 2768c2ecf20Sopenharmony_ci return status; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; 2798c2ecf20Sopenharmony_ci device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; 2808c2ecf20Sopenharmony_ci device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci autoresume_cdev = cdev; 2838c2ecf20Sopenharmony_ci timer_setup(&autoresume_timer, zero_autoresume, 0); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci func_inst_ss = usb_get_function_instance("SourceSink"); 2868c2ecf20Sopenharmony_ci if (IS_ERR(func_inst_ss)) 2878c2ecf20Sopenharmony_ci return PTR_ERR(func_inst_ss); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ss_opts = container_of(func_inst_ss, struct f_ss_opts, func_inst); 2908c2ecf20Sopenharmony_ci ss_opts->pattern = gzero_options.pattern; 2918c2ecf20Sopenharmony_ci ss_opts->isoc_interval = gzero_options.isoc_interval; 2928c2ecf20Sopenharmony_ci ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; 2938c2ecf20Sopenharmony_ci ss_opts->isoc_mult = gzero_options.isoc_mult; 2948c2ecf20Sopenharmony_ci ss_opts->isoc_maxburst = gzero_options.isoc_maxburst; 2958c2ecf20Sopenharmony_ci ss_opts->bulk_buflen = gzero_options.bulk_buflen; 2968c2ecf20Sopenharmony_ci ss_opts->bulk_qlen = gzero_options.ss_bulk_qlen; 2978c2ecf20Sopenharmony_ci ss_opts->iso_qlen = gzero_options.ss_iso_qlen; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci func_ss = usb_get_function(func_inst_ss); 3008c2ecf20Sopenharmony_ci if (IS_ERR(func_ss)) { 3018c2ecf20Sopenharmony_ci status = PTR_ERR(func_ss); 3028c2ecf20Sopenharmony_ci goto err_put_func_inst_ss; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci func_inst_lb = usb_get_function_instance("Loopback"); 3068c2ecf20Sopenharmony_ci if (IS_ERR(func_inst_lb)) { 3078c2ecf20Sopenharmony_ci status = PTR_ERR(func_inst_lb); 3088c2ecf20Sopenharmony_ci goto err_put_func_ss; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst); 3128c2ecf20Sopenharmony_ci lb_opts->bulk_buflen = gzero_options.bulk_buflen; 3138c2ecf20Sopenharmony_ci lb_opts->qlen = gzero_options.qlen; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci func_lb = usb_get_function(func_inst_lb); 3168c2ecf20Sopenharmony_ci if (IS_ERR(func_lb)) { 3178c2ecf20Sopenharmony_ci status = PTR_ERR(func_lb); 3188c2ecf20Sopenharmony_ci goto err_put_func_inst_lb; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id; 3228c2ecf20Sopenharmony_ci loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* support autoresume for remote wakeup testing */ 3258c2ecf20Sopenharmony_ci sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; 3268c2ecf20Sopenharmony_ci loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; 3278c2ecf20Sopenharmony_ci sourcesink_driver.descriptors = NULL; 3288c2ecf20Sopenharmony_ci loopback_driver.descriptors = NULL; 3298c2ecf20Sopenharmony_ci if (autoresume) { 3308c2ecf20Sopenharmony_ci sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 3318c2ecf20Sopenharmony_ci loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 3328c2ecf20Sopenharmony_ci autoresume_step_ms = autoresume * 1000; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* support OTG systems */ 3368c2ecf20Sopenharmony_ci if (gadget_is_otg(cdev->gadget)) { 3378c2ecf20Sopenharmony_ci if (!otg_desc[0]) { 3388c2ecf20Sopenharmony_ci struct usb_descriptor_header *usb_desc; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci usb_desc = usb_otg_descriptor_alloc(cdev->gadget); 3418c2ecf20Sopenharmony_ci if (!usb_desc) { 3428c2ecf20Sopenharmony_ci status = -ENOMEM; 3438c2ecf20Sopenharmony_ci goto err_conf_flb; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci usb_otg_descriptor_init(cdev->gadget, usb_desc); 3468c2ecf20Sopenharmony_ci otg_desc[0] = usb_desc; 3478c2ecf20Sopenharmony_ci otg_desc[1] = NULL; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci sourcesink_driver.descriptors = otg_desc; 3508c2ecf20Sopenharmony_ci sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 3518c2ecf20Sopenharmony_ci loopback_driver.descriptors = otg_desc; 3528c2ecf20Sopenharmony_ci loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Register primary, then secondary configuration. Note that 3568c2ecf20Sopenharmony_ci * SH3 only allows one config... 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci if (loopdefault) { 3598c2ecf20Sopenharmony_ci usb_add_config_only(cdev, &loopback_driver); 3608c2ecf20Sopenharmony_ci usb_add_config_only(cdev, &sourcesink_driver); 3618c2ecf20Sopenharmony_ci } else { 3628c2ecf20Sopenharmony_ci usb_add_config_only(cdev, &sourcesink_driver); 3638c2ecf20Sopenharmony_ci usb_add_config_only(cdev, &loopback_driver); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci status = usb_add_function(&sourcesink_driver, func_ss); 3668c2ecf20Sopenharmony_ci if (status) 3678c2ecf20Sopenharmony_ci goto err_free_otg_desc; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci usb_ep_autoconfig_reset(cdev->gadget); 3708c2ecf20Sopenharmony_ci status = usb_add_function(&loopback_driver, func_lb); 3718c2ecf20Sopenharmony_ci if (status) 3728c2ecf20Sopenharmony_ci goto err_free_otg_desc; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci usb_ep_autoconfig_reset(cdev->gadget); 3758c2ecf20Sopenharmony_ci usb_composite_overwrite_options(cdev, &coverwrite); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cierr_free_otg_desc: 3828c2ecf20Sopenharmony_ci kfree(otg_desc[0]); 3838c2ecf20Sopenharmony_ci otg_desc[0] = NULL; 3848c2ecf20Sopenharmony_cierr_conf_flb: 3858c2ecf20Sopenharmony_ci usb_put_function(func_lb); 3868c2ecf20Sopenharmony_ci func_lb = NULL; 3878c2ecf20Sopenharmony_cierr_put_func_inst_lb: 3888c2ecf20Sopenharmony_ci usb_put_function_instance(func_inst_lb); 3898c2ecf20Sopenharmony_ci func_inst_lb = NULL; 3908c2ecf20Sopenharmony_cierr_put_func_ss: 3918c2ecf20Sopenharmony_ci usb_put_function(func_ss); 3928c2ecf20Sopenharmony_ci func_ss = NULL; 3938c2ecf20Sopenharmony_cierr_put_func_inst_ss: 3948c2ecf20Sopenharmony_ci usb_put_function_instance(func_inst_ss); 3958c2ecf20Sopenharmony_ci func_inst_ss = NULL; 3968c2ecf20Sopenharmony_ci return status; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int zero_unbind(struct usb_composite_dev *cdev) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci del_timer_sync(&autoresume_timer); 4028c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(func_ss)) 4038c2ecf20Sopenharmony_ci usb_put_function(func_ss); 4048c2ecf20Sopenharmony_ci usb_put_function_instance(func_inst_ss); 4058c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(func_lb)) 4068c2ecf20Sopenharmony_ci usb_put_function(func_lb); 4078c2ecf20Sopenharmony_ci usb_put_function_instance(func_inst_lb); 4088c2ecf20Sopenharmony_ci kfree(otg_desc[0]); 4098c2ecf20Sopenharmony_ci otg_desc[0] = NULL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic struct usb_composite_driver zero_driver = { 4158c2ecf20Sopenharmony_ci .name = "zero", 4168c2ecf20Sopenharmony_ci .dev = &device_desc, 4178c2ecf20Sopenharmony_ci .strings = dev_strings, 4188c2ecf20Sopenharmony_ci .max_speed = USB_SPEED_SUPER, 4198c2ecf20Sopenharmony_ci .bind = zero_bind, 4208c2ecf20Sopenharmony_ci .unbind = zero_unbind, 4218c2ecf20Sopenharmony_ci .suspend = zero_suspend, 4228c2ecf20Sopenharmony_ci .resume = zero_resume, 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cimodule_usb_composite_driver(zero_driver); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 4288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 429