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