162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell
662306a36Sopenharmony_ci * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
762306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/* #define VERBOSE_DEBUG */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/netdevice.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#if defined USB_ETH_RNDIS
1662306a36Sopenharmony_ci#  undef USB_ETH_RNDIS
1762306a36Sopenharmony_ci#endif
1862306a36Sopenharmony_ci#ifdef CONFIG_USB_ETH_RNDIS
1962306a36Sopenharmony_ci#  define USB_ETH_RNDIS y
2062306a36Sopenharmony_ci#endif
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "u_ether.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Ethernet gadget driver -- with CDC and non-CDC options
2762306a36Sopenharmony_ci * Builds on hardware support for a full duplex link.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * CDC Ethernet is the standard USB solution for sending Ethernet frames
3062306a36Sopenharmony_ci * using USB.  Real hardware tends to use the same framing protocol but look
3162306a36Sopenharmony_ci * different for control features.  This driver strongly prefers to use
3262306a36Sopenharmony_ci * this USB-IF standard as its open-systems interoperability solution;
3362306a36Sopenharmony_ci * most host side USB stacks (except from Microsoft) support it.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
3662306a36Sopenharmony_ci * TLA-soup.  "CDC ACM" (Abstract Control Model) is for modems, and a new
3762306a36Sopenharmony_ci * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * There's some hardware that can't talk CDC ECM.  We make that hardware
4062306a36Sopenharmony_ci * implement a "minimalist" vendor-agnostic CDC core:  same framing, but
4162306a36Sopenharmony_ci * link-level setup only requires activating the configuration.  Only the
4262306a36Sopenharmony_ci * endpoint descriptors, and product/vendor IDs, are relevant; no control
4362306a36Sopenharmony_ci * operations are available.  Linux supports it, but other host operating
4462306a36Sopenharmony_ci * systems may not.  (This is a subset of CDC Ethernet.)
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * It turns out that if you add a few descriptors to that "CDC Subset",
4762306a36Sopenharmony_ci * (Windows) host side drivers from MCCI can treat it as one submode of
4862306a36Sopenharmony_ci * a proprietary scheme called "SAFE" ... without needing to know about
4962306a36Sopenharmony_ci * specific product/vendor IDs.  So we do that, making it easier to use
5062306a36Sopenharmony_ci * those MS-Windows drivers.  Those added descriptors make it resemble a
5162306a36Sopenharmony_ci * CDC MDLM device, but they don't change device behavior at all.  (See
5262306a36Sopenharmony_ci * MCCI Engineering report 950198 "SAFE Networking Functions".)
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * A third option is also in use.  Rather than CDC Ethernet, or something
5562306a36Sopenharmony_ci * simpler, Microsoft pushes their own approach: RNDIS.  The published
5662306a36Sopenharmony_ci * RNDIS specs are ambiguous and appear to be incomplete, and are also
5762306a36Sopenharmony_ci * needlessly complex.  They borrow more from CDC ACM than CDC ECM.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define DRIVER_DESC		"Ethernet Gadget"
6162306a36Sopenharmony_ci#define DRIVER_VERSION		"Memorial Day 2008"
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#ifdef USB_ETH_RNDIS
6462306a36Sopenharmony_ci#define PREFIX			"RNDIS/"
6562306a36Sopenharmony_ci#else
6662306a36Sopenharmony_ci#define PREFIX			""
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * This driver aims for interoperability by using CDC ECM unless
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci *		can_support_ecm()
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * returns false, in which case it supports the CDC Subset.  By default,
7562306a36Sopenharmony_ci * that returns true; most hardware has no problems with CDC ECM, that's
7662306a36Sopenharmony_ci * a good default.  Previous versions of this driver had no default; this
7762306a36Sopenharmony_ci * version changes that, removing overhead for new controller support.
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci *	IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline bool has_rndis(void)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci#ifdef	USB_ETH_RNDIS
8562306a36Sopenharmony_ci	return true;
8662306a36Sopenharmony_ci#else
8762306a36Sopenharmony_ci	return false;
8862306a36Sopenharmony_ci#endif
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#include <linux/module.h>
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#include "u_ecm.h"
9462306a36Sopenharmony_ci#include "u_gether.h"
9562306a36Sopenharmony_ci#ifdef	USB_ETH_RNDIS
9662306a36Sopenharmony_ci#include "u_rndis.h"
9762306a36Sopenharmony_ci#include "rndis.h"
9862306a36Sopenharmony_ci#else
9962306a36Sopenharmony_ci#define rndis_borrow_net(...) do {} while (0)
10062306a36Sopenharmony_ci#endif
10162306a36Sopenharmony_ci#include "u_eem.h"
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
10462306a36Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS();
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciUSB_ETHERNET_MODULE_PARAMETERS();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
10962306a36Sopenharmony_ci * Instead:  allocate your own, using normal USB-IF procedures.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* Thanks to NetChip Technologies for donating this product ID.
11362306a36Sopenharmony_ci * It's for devices with only CDC Ethernet configurations.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci#define CDC_VENDOR_NUM		0x0525	/* NetChip */
11662306a36Sopenharmony_ci#define CDC_PRODUCT_NUM		0xa4a1	/* Linux-USB Ethernet Gadget */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* For hardware that can't talk CDC, we use the same vendor ID that
11962306a36Sopenharmony_ci * ARM Linux has used for ethernet-over-usb, both with sa1100 and
12062306a36Sopenharmony_ci * with pxa250.  We're protocol-compatible, if the host-side drivers
12162306a36Sopenharmony_ci * use the endpoint descriptors.  bcdDevice (version) is nonzero, so
12262306a36Sopenharmony_ci * drivers that need to hard-wire endpoint numbers have a hook.
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * The protocol is a minimal subset of CDC Ether, which works on any bulk
12562306a36Sopenharmony_ci * hardware that's not deeply broken ... even on hardware that can't talk
12662306a36Sopenharmony_ci * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
12762306a36Sopenharmony_ci * doesn't handle control-OUT).
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ci#define	SIMPLE_VENDOR_NUM	0x049f
13062306a36Sopenharmony_ci#define	SIMPLE_PRODUCT_NUM	0x505a
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* For hardware that can talk RNDIS and either of the above protocols,
13362306a36Sopenharmony_ci * use this ID ... the windows INF files will know it.  Unless it's
13462306a36Sopenharmony_ci * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
13562306a36Sopenharmony_ci * the non-RNDIS configuration.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_ci#define RNDIS_VENDOR_NUM	0x0525	/* NetChip */
13862306a36Sopenharmony_ci#define RNDIS_PRODUCT_NUM	0xa4a2	/* Ethernet/RNDIS Gadget */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/* For EEM gadgets */
14162306a36Sopenharmony_ci#define EEM_VENDOR_NUM		0x1d6b	/* Linux Foundation */
14262306a36Sopenharmony_ci#define EEM_PRODUCT_NUM		0x0102	/* EEM Gadget */
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct usb_device_descriptor device_desc = {
14762306a36Sopenharmony_ci	.bLength =		sizeof device_desc,
14862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_DEVICE,
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* .bcdUSB = DYNAMIC */
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	.bDeviceClass =		USB_CLASS_COMM,
15362306a36Sopenharmony_ci	.bDeviceSubClass =	0,
15462306a36Sopenharmony_ci	.bDeviceProtocol =	0,
15562306a36Sopenharmony_ci	/* .bMaxPacketSize0 = f(hardware) */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Vendor and product id defaults change according to what configs
15862306a36Sopenharmony_ci	 * we support.  (As does bNumConfigurations.)  These values can
15962306a36Sopenharmony_ci	 * also be overridden by module parameters.
16062306a36Sopenharmony_ci	 */
16162306a36Sopenharmony_ci	.idVendor =		cpu_to_le16 (CDC_VENDOR_NUM),
16262306a36Sopenharmony_ci	.idProduct =		cpu_to_le16 (CDC_PRODUCT_NUM),
16362306a36Sopenharmony_ci	/* .bcdDevice = f(hardware) */
16462306a36Sopenharmony_ci	/* .iManufacturer = DYNAMIC */
16562306a36Sopenharmony_ci	/* .iProduct = DYNAMIC */
16662306a36Sopenharmony_ci	/* NO SERIAL NUMBER */
16762306a36Sopenharmony_ci	.bNumConfigurations =	1,
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic const struct usb_descriptor_header *otg_desc[2];
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic struct usb_string strings_dev[] = {
17362306a36Sopenharmony_ci	[USB_GADGET_MANUFACTURER_IDX].s = "",
17462306a36Sopenharmony_ci	[USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
17562306a36Sopenharmony_ci	[USB_GADGET_SERIAL_IDX].s = "",
17662306a36Sopenharmony_ci	{  } /* end of list */
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic struct usb_gadget_strings stringtab_dev = {
18062306a36Sopenharmony_ci	.language	= 0x0409,	/* en-us */
18162306a36Sopenharmony_ci	.strings	= strings_dev,
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic struct usb_gadget_strings *dev_strings[] = {
18562306a36Sopenharmony_ci	&stringtab_dev,
18662306a36Sopenharmony_ci	NULL,
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic struct usb_function_instance *fi_ecm;
19062306a36Sopenharmony_cistatic struct usb_function *f_ecm;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct usb_function_instance *fi_eem;
19362306a36Sopenharmony_cistatic struct usb_function *f_eem;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct usb_function_instance *fi_geth;
19662306a36Sopenharmony_cistatic struct usb_function *f_geth;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic struct usb_function_instance *fi_rndis;
19962306a36Sopenharmony_cistatic struct usb_function *f_rndis;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * We may not have an RNDIS configuration, but if we do it needs to be
20562306a36Sopenharmony_ci * the first one present.  That's to make Microsoft's drivers happy,
20662306a36Sopenharmony_ci * and to follow DOCSIS 1.0 (cable modem standard).
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_cistatic int rndis_do_config(struct usb_configuration *c)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int status;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* FIXME alloc iConfiguration string, set it in c->strings */
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (gadget_is_otg(c->cdev->gadget)) {
21562306a36Sopenharmony_ci		c->descriptors = otg_desc;
21662306a36Sopenharmony_ci		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	f_rndis = usb_get_function(fi_rndis);
22062306a36Sopenharmony_ci	if (IS_ERR(f_rndis))
22162306a36Sopenharmony_ci		return PTR_ERR(f_rndis);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	status = usb_add_function(c, f_rndis);
22462306a36Sopenharmony_ci	if (status < 0)
22562306a36Sopenharmony_ci		usb_put_function(f_rndis);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return status;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic struct usb_configuration rndis_config_driver = {
23162306a36Sopenharmony_ci	.label			= "RNDIS",
23262306a36Sopenharmony_ci	.bConfigurationValue	= 2,
23362306a36Sopenharmony_ci	/* .iConfiguration = DYNAMIC */
23462306a36Sopenharmony_ci	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci#ifdef CONFIG_USB_ETH_EEM
24062306a36Sopenharmony_cistatic bool use_eem = 1;
24162306a36Sopenharmony_ci#else
24262306a36Sopenharmony_cistatic bool use_eem;
24362306a36Sopenharmony_ci#endif
24462306a36Sopenharmony_cimodule_param(use_eem, bool, 0);
24562306a36Sopenharmony_ciMODULE_PARM_DESC(use_eem, "use CDC EEM mode");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/*
24862306a36Sopenharmony_ci * We _always_ have an ECM, CDC Subset, or EEM configuration.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_cistatic int eth_do_config(struct usb_configuration *c)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	int status = 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* FIXME alloc iConfiguration string, set it in c->strings */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (gadget_is_otg(c->cdev->gadget)) {
25762306a36Sopenharmony_ci		c->descriptors = otg_desc;
25862306a36Sopenharmony_ci		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (use_eem) {
26262306a36Sopenharmony_ci		f_eem = usb_get_function(fi_eem);
26362306a36Sopenharmony_ci		if (IS_ERR(f_eem))
26462306a36Sopenharmony_ci			return PTR_ERR(f_eem);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		status = usb_add_function(c, f_eem);
26762306a36Sopenharmony_ci		if (status < 0)
26862306a36Sopenharmony_ci			usb_put_function(f_eem);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		return status;
27162306a36Sopenharmony_ci	} else if (can_support_ecm(c->cdev->gadget)) {
27262306a36Sopenharmony_ci		f_ecm = usb_get_function(fi_ecm);
27362306a36Sopenharmony_ci		if (IS_ERR(f_ecm))
27462306a36Sopenharmony_ci			return PTR_ERR(f_ecm);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		status = usb_add_function(c, f_ecm);
27762306a36Sopenharmony_ci		if (status < 0)
27862306a36Sopenharmony_ci			usb_put_function(f_ecm);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		return status;
28162306a36Sopenharmony_ci	} else {
28262306a36Sopenharmony_ci		f_geth = usb_get_function(fi_geth);
28362306a36Sopenharmony_ci		if (IS_ERR(f_geth))
28462306a36Sopenharmony_ci			return PTR_ERR(f_geth);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		status = usb_add_function(c, f_geth);
28762306a36Sopenharmony_ci		if (status < 0)
28862306a36Sopenharmony_ci			usb_put_function(f_geth);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		return status;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic struct usb_configuration eth_config_driver = {
29662306a36Sopenharmony_ci	/* .label = f(hardware) */
29762306a36Sopenharmony_ci	.bConfigurationValue	= 1,
29862306a36Sopenharmony_ci	/* .iConfiguration = DYNAMIC */
29962306a36Sopenharmony_ci	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int eth_bind(struct usb_composite_dev *cdev)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct usb_gadget	*gadget = cdev->gadget;
30762306a36Sopenharmony_ci	struct f_eem_opts	*eem_opts = NULL;
30862306a36Sopenharmony_ci	struct f_ecm_opts	*ecm_opts = NULL;
30962306a36Sopenharmony_ci	struct f_gether_opts	*geth_opts = NULL;
31062306a36Sopenharmony_ci	struct net_device	*net;
31162306a36Sopenharmony_ci	int			status;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* set up main config label and device descriptor */
31462306a36Sopenharmony_ci	if (use_eem) {
31562306a36Sopenharmony_ci		/* EEM */
31662306a36Sopenharmony_ci		fi_eem = usb_get_function_instance("eem");
31762306a36Sopenharmony_ci		if (IS_ERR(fi_eem))
31862306a36Sopenharmony_ci			return PTR_ERR(fi_eem);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		net = eem_opts->net;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		eth_config_driver.label = "CDC Ethernet (EEM)";
32562306a36Sopenharmony_ci		device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
32662306a36Sopenharmony_ci		device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
32762306a36Sopenharmony_ci	} else if (can_support_ecm(gadget)) {
32862306a36Sopenharmony_ci		/* ECM */
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		fi_ecm = usb_get_function_instance("ecm");
33162306a36Sopenharmony_ci		if (IS_ERR(fi_ecm))
33262306a36Sopenharmony_ci			return PTR_ERR(fi_ecm);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		net = ecm_opts->net;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		eth_config_driver.label = "CDC Ethernet (ECM)";
33962306a36Sopenharmony_ci	} else {
34062306a36Sopenharmony_ci		/* CDC Subset */
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		fi_geth = usb_get_function_instance("geth");
34362306a36Sopenharmony_ci		if (IS_ERR(fi_geth))
34462306a36Sopenharmony_ci			return PTR_ERR(fi_geth);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		geth_opts = container_of(fi_geth, struct f_gether_opts,
34762306a36Sopenharmony_ci					 func_inst);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		net = geth_opts->net;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		eth_config_driver.label = "CDC Subset/SAFE";
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
35462306a36Sopenharmony_ci		device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
35562306a36Sopenharmony_ci		if (!has_rndis())
35662306a36Sopenharmony_ci			device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	gether_set_qmult(net, qmult);
36062306a36Sopenharmony_ci	if (!gether_set_host_addr(net, host_addr))
36162306a36Sopenharmony_ci		pr_info("using host ethernet address: %s", host_addr);
36262306a36Sopenharmony_ci	if (!gether_set_dev_addr(net, dev_addr))
36362306a36Sopenharmony_ci		pr_info("using self ethernet address: %s", dev_addr);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (has_rndis()) {
36662306a36Sopenharmony_ci		/* RNDIS plus ECM-or-Subset */
36762306a36Sopenharmony_ci		gether_set_gadget(net, cdev->gadget);
36862306a36Sopenharmony_ci		status = gether_register_netdev(net);
36962306a36Sopenharmony_ci		if (status)
37062306a36Sopenharmony_ci			goto fail;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		if (use_eem)
37362306a36Sopenharmony_ci			eem_opts->bound = true;
37462306a36Sopenharmony_ci		else if (can_support_ecm(gadget))
37562306a36Sopenharmony_ci			ecm_opts->bound = true;
37662306a36Sopenharmony_ci		else
37762306a36Sopenharmony_ci			geth_opts->bound = true;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		fi_rndis = usb_get_function_instance("rndis");
38062306a36Sopenharmony_ci		if (IS_ERR(fi_rndis)) {
38162306a36Sopenharmony_ci			status = PTR_ERR(fi_rndis);
38262306a36Sopenharmony_ci			goto fail;
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		rndis_borrow_net(fi_rndis, net);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
38862306a36Sopenharmony_ci		device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
38962306a36Sopenharmony_ci		device_desc.bNumConfigurations = 2;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Allocate string descriptor numbers ... note that string
39362306a36Sopenharmony_ci	 * contents can be overridden by the composite_dev glue.
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	status = usb_string_ids_tab(cdev, strings_dev);
39762306a36Sopenharmony_ci	if (status < 0)
39862306a36Sopenharmony_ci		goto fail1;
39962306a36Sopenharmony_ci	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
40062306a36Sopenharmony_ci	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (gadget_is_otg(gadget) && !otg_desc[0]) {
40362306a36Sopenharmony_ci		struct usb_descriptor_header *usb_desc;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		usb_desc = usb_otg_descriptor_alloc(gadget);
40662306a36Sopenharmony_ci		if (!usb_desc) {
40762306a36Sopenharmony_ci			status = -ENOMEM;
40862306a36Sopenharmony_ci			goto fail1;
40962306a36Sopenharmony_ci		}
41062306a36Sopenharmony_ci		usb_otg_descriptor_init(gadget, usb_desc);
41162306a36Sopenharmony_ci		otg_desc[0] = usb_desc;
41262306a36Sopenharmony_ci		otg_desc[1] = NULL;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* register our configuration(s); RNDIS first, if it's used */
41662306a36Sopenharmony_ci	if (has_rndis()) {
41762306a36Sopenharmony_ci		status = usb_add_config(cdev, &rndis_config_driver,
41862306a36Sopenharmony_ci				rndis_do_config);
41962306a36Sopenharmony_ci		if (status < 0)
42062306a36Sopenharmony_ci			goto fail2;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	status = usb_add_config(cdev, &eth_config_driver, eth_do_config);
42462306a36Sopenharmony_ci	if (status < 0)
42562306a36Sopenharmony_ci		goto fail2;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	usb_composite_overwrite_options(cdev, &coverwrite);
42862306a36Sopenharmony_ci	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
42962306a36Sopenharmony_ci			DRIVER_DESC);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cifail2:
43462306a36Sopenharmony_ci	kfree(otg_desc[0]);
43562306a36Sopenharmony_ci	otg_desc[0] = NULL;
43662306a36Sopenharmony_cifail1:
43762306a36Sopenharmony_ci	if (has_rndis())
43862306a36Sopenharmony_ci		usb_put_function_instance(fi_rndis);
43962306a36Sopenharmony_cifail:
44062306a36Sopenharmony_ci	if (use_eem)
44162306a36Sopenharmony_ci		usb_put_function_instance(fi_eem);
44262306a36Sopenharmony_ci	else if (can_support_ecm(gadget))
44362306a36Sopenharmony_ci		usb_put_function_instance(fi_ecm);
44462306a36Sopenharmony_ci	else
44562306a36Sopenharmony_ci		usb_put_function_instance(fi_geth);
44662306a36Sopenharmony_ci	return status;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic int eth_unbind(struct usb_composite_dev *cdev)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	if (has_rndis()) {
45262306a36Sopenharmony_ci		usb_put_function(f_rndis);
45362306a36Sopenharmony_ci		usb_put_function_instance(fi_rndis);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci	if (use_eem) {
45662306a36Sopenharmony_ci		usb_put_function(f_eem);
45762306a36Sopenharmony_ci		usb_put_function_instance(fi_eem);
45862306a36Sopenharmony_ci	} else if (can_support_ecm(cdev->gadget)) {
45962306a36Sopenharmony_ci		usb_put_function(f_ecm);
46062306a36Sopenharmony_ci		usb_put_function_instance(fi_ecm);
46162306a36Sopenharmony_ci	} else {
46262306a36Sopenharmony_ci		usb_put_function(f_geth);
46362306a36Sopenharmony_ci		usb_put_function_instance(fi_geth);
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	kfree(otg_desc[0]);
46662306a36Sopenharmony_ci	otg_desc[0] = NULL;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return 0;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic struct usb_composite_driver eth_driver = {
47262306a36Sopenharmony_ci	.name		= "g_ether",
47362306a36Sopenharmony_ci	.dev		= &device_desc,
47462306a36Sopenharmony_ci	.strings	= dev_strings,
47562306a36Sopenharmony_ci	.max_speed	= USB_SPEED_SUPER,
47662306a36Sopenharmony_ci	.bind		= eth_bind,
47762306a36Sopenharmony_ci	.unbind		= eth_unbind,
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cimodule_usb_composite_driver(eth_driver);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ciMODULE_DESCRIPTION(PREFIX DRIVER_DESC);
48362306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell, Benedikt Spanger");
48462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
485