18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell
68c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/* #define VERBOSE_DEBUG */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#if defined USB_ETH_RNDIS
168c2ecf20Sopenharmony_ci#  undef USB_ETH_RNDIS
178c2ecf20Sopenharmony_ci#endif
188c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_ETH_RNDIS
198c2ecf20Sopenharmony_ci#  define USB_ETH_RNDIS y
208c2ecf20Sopenharmony_ci#endif
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "u_ether.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Ethernet gadget driver -- with CDC and non-CDC options
278c2ecf20Sopenharmony_ci * Builds on hardware support for a full duplex link.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * CDC Ethernet is the standard USB solution for sending Ethernet frames
308c2ecf20Sopenharmony_ci * using USB.  Real hardware tends to use the same framing protocol but look
318c2ecf20Sopenharmony_ci * different for control features.  This driver strongly prefers to use
328c2ecf20Sopenharmony_ci * this USB-IF standard as its open-systems interoperability solution;
338c2ecf20Sopenharmony_ci * most host side USB stacks (except from Microsoft) support it.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
368c2ecf20Sopenharmony_ci * TLA-soup.  "CDC ACM" (Abstract Control Model) is for modems, and a new
378c2ecf20Sopenharmony_ci * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * There's some hardware that can't talk CDC ECM.  We make that hardware
408c2ecf20Sopenharmony_ci * implement a "minimalist" vendor-agnostic CDC core:  same framing, but
418c2ecf20Sopenharmony_ci * link-level setup only requires activating the configuration.  Only the
428c2ecf20Sopenharmony_ci * endpoint descriptors, and product/vendor IDs, are relevant; no control
438c2ecf20Sopenharmony_ci * operations are available.  Linux supports it, but other host operating
448c2ecf20Sopenharmony_ci * systems may not.  (This is a subset of CDC Ethernet.)
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * It turns out that if you add a few descriptors to that "CDC Subset",
478c2ecf20Sopenharmony_ci * (Windows) host side drivers from MCCI can treat it as one submode of
488c2ecf20Sopenharmony_ci * a proprietary scheme called "SAFE" ... without needing to know about
498c2ecf20Sopenharmony_ci * specific product/vendor IDs.  So we do that, making it easier to use
508c2ecf20Sopenharmony_ci * those MS-Windows drivers.  Those added descriptors make it resemble a
518c2ecf20Sopenharmony_ci * CDC MDLM device, but they don't change device behavior at all.  (See
528c2ecf20Sopenharmony_ci * MCCI Engineering report 950198 "SAFE Networking Functions".)
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * A third option is also in use.  Rather than CDC Ethernet, or something
558c2ecf20Sopenharmony_ci * simpler, Microsoft pushes their own approach: RNDIS.  The published
568c2ecf20Sopenharmony_ci * RNDIS specs are ambiguous and appear to be incomplete, and are also
578c2ecf20Sopenharmony_ci * needlessly complex.  They borrow more from CDC ACM than CDC ECM.
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define DRIVER_DESC		"Ethernet Gadget"
618c2ecf20Sopenharmony_ci#define DRIVER_VERSION		"Memorial Day 2008"
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#ifdef USB_ETH_RNDIS
648c2ecf20Sopenharmony_ci#define PREFIX			"RNDIS/"
658c2ecf20Sopenharmony_ci#else
668c2ecf20Sopenharmony_ci#define PREFIX			""
678c2ecf20Sopenharmony_ci#endif
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * This driver aims for interoperability by using CDC ECM unless
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci *		can_support_ecm()
738c2ecf20Sopenharmony_ci *
748c2ecf20Sopenharmony_ci * returns false, in which case it supports the CDC Subset.  By default,
758c2ecf20Sopenharmony_ci * that returns true; most hardware has no problems with CDC ECM, that's
768c2ecf20Sopenharmony_ci * a good default.  Previous versions of this driver had no default; this
778c2ecf20Sopenharmony_ci * version changes that, removing overhead for new controller support.
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci *	IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline bool has_rndis(void)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci#ifdef	USB_ETH_RNDIS
858c2ecf20Sopenharmony_ci	return true;
868c2ecf20Sopenharmony_ci#else
878c2ecf20Sopenharmony_ci	return false;
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#include <linux/module.h>
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#include "u_ecm.h"
948c2ecf20Sopenharmony_ci#include "u_gether.h"
958c2ecf20Sopenharmony_ci#ifdef	USB_ETH_RNDIS
968c2ecf20Sopenharmony_ci#include "u_rndis.h"
978c2ecf20Sopenharmony_ci#include "rndis.h"
988c2ecf20Sopenharmony_ci#else
998c2ecf20Sopenharmony_ci#define rndis_borrow_net(...) do {} while (0)
1008c2ecf20Sopenharmony_ci#endif
1018c2ecf20Sopenharmony_ci#include "u_eem.h"
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
1048c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS();
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ciUSB_ETHERNET_MODULE_PARAMETERS();
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
1098c2ecf20Sopenharmony_ci * Instead:  allocate your own, using normal USB-IF procedures.
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* Thanks to NetChip Technologies for donating this product ID.
1138c2ecf20Sopenharmony_ci * It's for devices with only CDC Ethernet configurations.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_ci#define CDC_VENDOR_NUM		0x0525	/* NetChip */
1168c2ecf20Sopenharmony_ci#define CDC_PRODUCT_NUM		0xa4a1	/* Linux-USB Ethernet Gadget */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* For hardware that can't talk CDC, we use the same vendor ID that
1198c2ecf20Sopenharmony_ci * ARM Linux has used for ethernet-over-usb, both with sa1100 and
1208c2ecf20Sopenharmony_ci * with pxa250.  We're protocol-compatible, if the host-side drivers
1218c2ecf20Sopenharmony_ci * use the endpoint descriptors.  bcdDevice (version) is nonzero, so
1228c2ecf20Sopenharmony_ci * drivers that need to hard-wire endpoint numbers have a hook.
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * The protocol is a minimal subset of CDC Ether, which works on any bulk
1258c2ecf20Sopenharmony_ci * hardware that's not deeply broken ... even on hardware that can't talk
1268c2ecf20Sopenharmony_ci * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
1278c2ecf20Sopenharmony_ci * doesn't handle control-OUT).
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_ci#define	SIMPLE_VENDOR_NUM	0x049f
1308c2ecf20Sopenharmony_ci#define	SIMPLE_PRODUCT_NUM	0x505a
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* For hardware that can talk RNDIS and either of the above protocols,
1338c2ecf20Sopenharmony_ci * use this ID ... the windows INF files will know it.  Unless it's
1348c2ecf20Sopenharmony_ci * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
1358c2ecf20Sopenharmony_ci * the non-RNDIS configuration.
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_ci#define RNDIS_VENDOR_NUM	0x0525	/* NetChip */
1388c2ecf20Sopenharmony_ci#define RNDIS_PRODUCT_NUM	0xa4a2	/* Ethernet/RNDIS Gadget */
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* For EEM gadgets */
1418c2ecf20Sopenharmony_ci#define EEM_VENDOR_NUM		0x1d6b	/* Linux Foundation */
1428c2ecf20Sopenharmony_ci#define EEM_PRODUCT_NUM		0x0102	/* EEM Gadget */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic struct usb_device_descriptor device_desc = {
1478c2ecf20Sopenharmony_ci	.bLength =		sizeof device_desc,
1488c2ecf20Sopenharmony_ci	.bDescriptorType =	USB_DT_DEVICE,
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* .bcdUSB = DYNAMIC */
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	.bDeviceClass =		USB_CLASS_COMM,
1538c2ecf20Sopenharmony_ci	.bDeviceSubClass =	0,
1548c2ecf20Sopenharmony_ci	.bDeviceProtocol =	0,
1558c2ecf20Sopenharmony_ci	/* .bMaxPacketSize0 = f(hardware) */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* Vendor and product id defaults change according to what configs
1588c2ecf20Sopenharmony_ci	 * we support.  (As does bNumConfigurations.)  These values can
1598c2ecf20Sopenharmony_ci	 * also be overridden by module parameters.
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci	.idVendor =		cpu_to_le16 (CDC_VENDOR_NUM),
1628c2ecf20Sopenharmony_ci	.idProduct =		cpu_to_le16 (CDC_PRODUCT_NUM),
1638c2ecf20Sopenharmony_ci	/* .bcdDevice = f(hardware) */
1648c2ecf20Sopenharmony_ci	/* .iManufacturer = DYNAMIC */
1658c2ecf20Sopenharmony_ci	/* .iProduct = DYNAMIC */
1668c2ecf20Sopenharmony_ci	/* NO SERIAL NUMBER */
1678c2ecf20Sopenharmony_ci	.bNumConfigurations =	1,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic const struct usb_descriptor_header *otg_desc[2];
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic struct usb_string strings_dev[] = {
1738c2ecf20Sopenharmony_ci	[USB_GADGET_MANUFACTURER_IDX].s = "",
1748c2ecf20Sopenharmony_ci	[USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
1758c2ecf20Sopenharmony_ci	[USB_GADGET_SERIAL_IDX].s = "",
1768c2ecf20Sopenharmony_ci	{  } /* end of list */
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic struct usb_gadget_strings stringtab_dev = {
1808c2ecf20Sopenharmony_ci	.language	= 0x0409,	/* en-us */
1818c2ecf20Sopenharmony_ci	.strings	= strings_dev,
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *dev_strings[] = {
1858c2ecf20Sopenharmony_ci	&stringtab_dev,
1868c2ecf20Sopenharmony_ci	NULL,
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_ecm;
1908c2ecf20Sopenharmony_cistatic struct usb_function *f_ecm;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_eem;
1938c2ecf20Sopenharmony_cistatic struct usb_function *f_eem;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_geth;
1968c2ecf20Sopenharmony_cistatic struct usb_function *f_geth;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_rndis;
1998c2ecf20Sopenharmony_cistatic struct usb_function *f_rndis;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * We may not have an RNDIS configuration, but if we do it needs to be
2058c2ecf20Sopenharmony_ci * the first one present.  That's to make Microsoft's drivers happy,
2068c2ecf20Sopenharmony_ci * and to follow DOCSIS 1.0 (cable modem standard).
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_cistatic int rndis_do_config(struct usb_configuration *c)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int status;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* FIXME alloc iConfiguration string, set it in c->strings */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (gadget_is_otg(c->cdev->gadget)) {
2158c2ecf20Sopenharmony_ci		c->descriptors = otg_desc;
2168c2ecf20Sopenharmony_ci		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	f_rndis = usb_get_function(fi_rndis);
2208c2ecf20Sopenharmony_ci	if (IS_ERR(f_rndis))
2218c2ecf20Sopenharmony_ci		return PTR_ERR(f_rndis);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	status = usb_add_function(c, f_rndis);
2248c2ecf20Sopenharmony_ci	if (status < 0)
2258c2ecf20Sopenharmony_ci		usb_put_function(f_rndis);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return status;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic struct usb_configuration rndis_config_driver = {
2318c2ecf20Sopenharmony_ci	.label			= "RNDIS",
2328c2ecf20Sopenharmony_ci	.bConfigurationValue	= 2,
2338c2ecf20Sopenharmony_ci	/* .iConfiguration = DYNAMIC */
2348c2ecf20Sopenharmony_ci	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_ETH_EEM
2408c2ecf20Sopenharmony_cistatic bool use_eem = 1;
2418c2ecf20Sopenharmony_ci#else
2428c2ecf20Sopenharmony_cistatic bool use_eem;
2438c2ecf20Sopenharmony_ci#endif
2448c2ecf20Sopenharmony_cimodule_param(use_eem, bool, 0);
2458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_eem, "use CDC EEM mode");
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/*
2488c2ecf20Sopenharmony_ci * We _always_ have an ECM, CDC Subset, or EEM configuration.
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cistatic int eth_do_config(struct usb_configuration *c)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	int status = 0;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* FIXME alloc iConfiguration string, set it in c->strings */
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (gadget_is_otg(c->cdev->gadget)) {
2578c2ecf20Sopenharmony_ci		c->descriptors = otg_desc;
2588c2ecf20Sopenharmony_ci		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (use_eem) {
2628c2ecf20Sopenharmony_ci		f_eem = usb_get_function(fi_eem);
2638c2ecf20Sopenharmony_ci		if (IS_ERR(f_eem))
2648c2ecf20Sopenharmony_ci			return PTR_ERR(f_eem);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		status = usb_add_function(c, f_eem);
2678c2ecf20Sopenharmony_ci		if (status < 0)
2688c2ecf20Sopenharmony_ci			usb_put_function(f_eem);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		return status;
2718c2ecf20Sopenharmony_ci	} else if (can_support_ecm(c->cdev->gadget)) {
2728c2ecf20Sopenharmony_ci		f_ecm = usb_get_function(fi_ecm);
2738c2ecf20Sopenharmony_ci		if (IS_ERR(f_ecm))
2748c2ecf20Sopenharmony_ci			return PTR_ERR(f_ecm);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		status = usb_add_function(c, f_ecm);
2778c2ecf20Sopenharmony_ci		if (status < 0)
2788c2ecf20Sopenharmony_ci			usb_put_function(f_ecm);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		return status;
2818c2ecf20Sopenharmony_ci	} else {
2828c2ecf20Sopenharmony_ci		f_geth = usb_get_function(fi_geth);
2838c2ecf20Sopenharmony_ci		if (IS_ERR(f_geth))
2848c2ecf20Sopenharmony_ci			return PTR_ERR(f_geth);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		status = usb_add_function(c, f_geth);
2878c2ecf20Sopenharmony_ci		if (status < 0)
2888c2ecf20Sopenharmony_ci			usb_put_function(f_geth);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		return status;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic struct usb_configuration eth_config_driver = {
2968c2ecf20Sopenharmony_ci	/* .label = f(hardware) */
2978c2ecf20Sopenharmony_ci	.bConfigurationValue	= 1,
2988c2ecf20Sopenharmony_ci	/* .iConfiguration = DYNAMIC */
2998c2ecf20Sopenharmony_ci	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int eth_bind(struct usb_composite_dev *cdev)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct usb_gadget	*gadget = cdev->gadget;
3078c2ecf20Sopenharmony_ci	struct f_eem_opts	*eem_opts = NULL;
3088c2ecf20Sopenharmony_ci	struct f_ecm_opts	*ecm_opts = NULL;
3098c2ecf20Sopenharmony_ci	struct f_gether_opts	*geth_opts = NULL;
3108c2ecf20Sopenharmony_ci	struct net_device	*net;
3118c2ecf20Sopenharmony_ci	int			status;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* set up main config label and device descriptor */
3148c2ecf20Sopenharmony_ci	if (use_eem) {
3158c2ecf20Sopenharmony_ci		/* EEM */
3168c2ecf20Sopenharmony_ci		fi_eem = usb_get_function_instance("eem");
3178c2ecf20Sopenharmony_ci		if (IS_ERR(fi_eem))
3188c2ecf20Sopenharmony_ci			return PTR_ERR(fi_eem);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		net = eem_opts->net;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		eth_config_driver.label = "CDC Ethernet (EEM)";
3258c2ecf20Sopenharmony_ci		device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
3268c2ecf20Sopenharmony_ci		device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
3278c2ecf20Sopenharmony_ci	} else if (can_support_ecm(gadget)) {
3288c2ecf20Sopenharmony_ci		/* ECM */
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		fi_ecm = usb_get_function_instance("ecm");
3318c2ecf20Sopenharmony_ci		if (IS_ERR(fi_ecm))
3328c2ecf20Sopenharmony_ci			return PTR_ERR(fi_ecm);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		net = ecm_opts->net;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		eth_config_driver.label = "CDC Ethernet (ECM)";
3398c2ecf20Sopenharmony_ci	} else {
3408c2ecf20Sopenharmony_ci		/* CDC Subset */
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		fi_geth = usb_get_function_instance("geth");
3438c2ecf20Sopenharmony_ci		if (IS_ERR(fi_geth))
3448c2ecf20Sopenharmony_ci			return PTR_ERR(fi_geth);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		geth_opts = container_of(fi_geth, struct f_gether_opts,
3478c2ecf20Sopenharmony_ci					 func_inst);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		net = geth_opts->net;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		eth_config_driver.label = "CDC Subset/SAFE";
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
3548c2ecf20Sopenharmony_ci		device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
3558c2ecf20Sopenharmony_ci		if (!has_rndis())
3568c2ecf20Sopenharmony_ci			device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	gether_set_qmult(net, qmult);
3608c2ecf20Sopenharmony_ci	if (!gether_set_host_addr(net, host_addr))
3618c2ecf20Sopenharmony_ci		pr_info("using host ethernet address: %s", host_addr);
3628c2ecf20Sopenharmony_ci	if (!gether_set_dev_addr(net, dev_addr))
3638c2ecf20Sopenharmony_ci		pr_info("using self ethernet address: %s", dev_addr);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (has_rndis()) {
3668c2ecf20Sopenharmony_ci		/* RNDIS plus ECM-or-Subset */
3678c2ecf20Sopenharmony_ci		gether_set_gadget(net, cdev->gadget);
3688c2ecf20Sopenharmony_ci		status = gether_register_netdev(net);
3698c2ecf20Sopenharmony_ci		if (status)
3708c2ecf20Sopenharmony_ci			goto fail;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		if (use_eem)
3738c2ecf20Sopenharmony_ci			eem_opts->bound = true;
3748c2ecf20Sopenharmony_ci		else if (can_support_ecm(gadget))
3758c2ecf20Sopenharmony_ci			ecm_opts->bound = true;
3768c2ecf20Sopenharmony_ci		else
3778c2ecf20Sopenharmony_ci			geth_opts->bound = true;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		fi_rndis = usb_get_function_instance("rndis");
3808c2ecf20Sopenharmony_ci		if (IS_ERR(fi_rndis)) {
3818c2ecf20Sopenharmony_ci			status = PTR_ERR(fi_rndis);
3828c2ecf20Sopenharmony_ci			goto fail;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		rndis_borrow_net(fi_rndis, net);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
3888c2ecf20Sopenharmony_ci		device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
3898c2ecf20Sopenharmony_ci		device_desc.bNumConfigurations = 2;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Allocate string descriptor numbers ... note that string
3938c2ecf20Sopenharmony_ci	 * contents can be overridden by the composite_dev glue.
3948c2ecf20Sopenharmony_ci	 */
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	status = usb_string_ids_tab(cdev, strings_dev);
3978c2ecf20Sopenharmony_ci	if (status < 0)
3988c2ecf20Sopenharmony_ci		goto fail1;
3998c2ecf20Sopenharmony_ci	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
4008c2ecf20Sopenharmony_ci	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (gadget_is_otg(gadget) && !otg_desc[0]) {
4038c2ecf20Sopenharmony_ci		struct usb_descriptor_header *usb_desc;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		usb_desc = usb_otg_descriptor_alloc(gadget);
4068c2ecf20Sopenharmony_ci		if (!usb_desc) {
4078c2ecf20Sopenharmony_ci			status = -ENOMEM;
4088c2ecf20Sopenharmony_ci			goto fail1;
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci		usb_otg_descriptor_init(gadget, usb_desc);
4118c2ecf20Sopenharmony_ci		otg_desc[0] = usb_desc;
4128c2ecf20Sopenharmony_ci		otg_desc[1] = NULL;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* register our configuration(s); RNDIS first, if it's used */
4168c2ecf20Sopenharmony_ci	if (has_rndis()) {
4178c2ecf20Sopenharmony_ci		status = usb_add_config(cdev, &rndis_config_driver,
4188c2ecf20Sopenharmony_ci				rndis_do_config);
4198c2ecf20Sopenharmony_ci		if (status < 0)
4208c2ecf20Sopenharmony_ci			goto fail2;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	status = usb_add_config(cdev, &eth_config_driver, eth_do_config);
4248c2ecf20Sopenharmony_ci	if (status < 0)
4258c2ecf20Sopenharmony_ci		goto fail2;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	usb_composite_overwrite_options(cdev, &coverwrite);
4288c2ecf20Sopenharmony_ci	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
4298c2ecf20Sopenharmony_ci			DRIVER_DESC);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cifail2:
4348c2ecf20Sopenharmony_ci	kfree(otg_desc[0]);
4358c2ecf20Sopenharmony_ci	otg_desc[0] = NULL;
4368c2ecf20Sopenharmony_cifail1:
4378c2ecf20Sopenharmony_ci	if (has_rndis())
4388c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_rndis);
4398c2ecf20Sopenharmony_cifail:
4408c2ecf20Sopenharmony_ci	if (use_eem)
4418c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_eem);
4428c2ecf20Sopenharmony_ci	else if (can_support_ecm(gadget))
4438c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_ecm);
4448c2ecf20Sopenharmony_ci	else
4458c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_geth);
4468c2ecf20Sopenharmony_ci	return status;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic int eth_unbind(struct usb_composite_dev *cdev)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	if (has_rndis()) {
4528c2ecf20Sopenharmony_ci		usb_put_function(f_rndis);
4538c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_rndis);
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	if (use_eem) {
4568c2ecf20Sopenharmony_ci		usb_put_function(f_eem);
4578c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_eem);
4588c2ecf20Sopenharmony_ci	} else if (can_support_ecm(cdev->gadget)) {
4598c2ecf20Sopenharmony_ci		usb_put_function(f_ecm);
4608c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_ecm);
4618c2ecf20Sopenharmony_ci	} else {
4628c2ecf20Sopenharmony_ci		usb_put_function(f_geth);
4638c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_geth);
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci	kfree(otg_desc[0]);
4668c2ecf20Sopenharmony_ci	otg_desc[0] = NULL;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic struct usb_composite_driver eth_driver = {
4728c2ecf20Sopenharmony_ci	.name		= "g_ether",
4738c2ecf20Sopenharmony_ci	.dev		= &device_desc,
4748c2ecf20Sopenharmony_ci	.strings	= dev_strings,
4758c2ecf20Sopenharmony_ci	.max_speed	= USB_SPEED_SUPER,
4768c2ecf20Sopenharmony_ci	.bind		= eth_bind,
4778c2ecf20Sopenharmony_ci	.unbind		= eth_unbind,
4788c2ecf20Sopenharmony_ci};
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cimodule_usb_composite_driver(eth_driver);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(PREFIX DRIVER_DESC);
4838c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brownell, Benedikt Spanger");
4848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
485