162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_subset.c -- "CDC Subset" Ethernet link function driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell
662306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/etherdevice.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "u_ether.h"
1662306a36Sopenharmony_ci#include "u_ether_configfs.h"
1762306a36Sopenharmony_ci#include "u_gether.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * This function packages a simple "CDC Subset" Ethernet port with no real
2162306a36Sopenharmony_ci * control mechanisms; just raw data transfer over two bulk endpoints.
2262306a36Sopenharmony_ci * The data transfer model is exactly that of CDC Ethernet, which is
2362306a36Sopenharmony_ci * why we call it the "CDC Subset".
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Because it's not standardized, this has some interoperability issues.
2662306a36Sopenharmony_ci * They mostly relate to driver binding, since the data transfer model is
2762306a36Sopenharmony_ci * so simple (CDC Ethernet).  The original versions of this protocol used
2862306a36Sopenharmony_ci * specific product/vendor IDs:  byteswapped IDs for Digital Equipment's
2962306a36Sopenharmony_ci * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
3062306a36Sopenharmony_ci * daughtercards with USB peripheral connectors.  (It was used more often
3162306a36Sopenharmony_ci * with other boards, using the Itsy identifiers.)  Linux hosts recognized
3262306a36Sopenharmony_ci * this with CONFIG_USB_ARMLINUX; these devices have only one configuration
3362306a36Sopenharmony_ci * and one interface.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * At some point, MCCI defined a (nonconformant) CDC MDLM variant called
3662306a36Sopenharmony_ci * "SAFE", which happens to have a mode which is identical to the "CDC
3762306a36Sopenharmony_ci * Subset" in terms of data transfer and lack of control model.  This was
3862306a36Sopenharmony_ci * adopted by later Sharp Zaurus models, and by some other software which
3962306a36Sopenharmony_ci * Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * Because Microsoft's RNDIS drivers are far from robust, we added a few
4262306a36Sopenharmony_ci * descriptors to the CDC Subset code, making this code look like a SAFE
4362306a36Sopenharmony_ci * implementation.  This lets you use MCCI's host side MS-Windows drivers
4462306a36Sopenharmony_ci * if you get fed up with RNDIS.  It also makes it easier for composite
4562306a36Sopenharmony_ci * drivers to work, since they can use class based binding instead of
4662306a36Sopenharmony_ci * caring about specific product and vendor IDs.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct f_gether {
5062306a36Sopenharmony_ci	struct gether			port;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	char				ethaddr[14];
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic inline struct f_gether *func_to_geth(struct usb_function *f)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return container_of(f, struct f_gether, port.func);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * "Simple" CDC-subset option is a simple vendor-neutral model that most
6462306a36Sopenharmony_ci * full speed controllers can handle:  one interface, two bulk endpoints.
6562306a36Sopenharmony_ci * To assist host side drivers, we fancy it up a bit, and add descriptors so
6662306a36Sopenharmony_ci * some host side drivers will understand it as a "SAFE" variant.
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
6962306a36Sopenharmony_ci * Data endpoints live in the control interface, there's no data interface.
7062306a36Sopenharmony_ci * And it's not used to talk to a cell phone radio.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* interface descriptor: */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic struct usb_interface_descriptor subset_data_intf = {
7662306a36Sopenharmony_ci	.bLength =		sizeof subset_data_intf,
7762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* .bInterfaceNumber = DYNAMIC */
8062306a36Sopenharmony_ci	.bAlternateSetting =	0,
8162306a36Sopenharmony_ci	.bNumEndpoints =	2,
8262306a36Sopenharmony_ci	.bInterfaceClass =      USB_CLASS_COMM,
8362306a36Sopenharmony_ci	.bInterfaceSubClass =	USB_CDC_SUBCLASS_MDLM,
8462306a36Sopenharmony_ci	.bInterfaceProtocol =	0,
8562306a36Sopenharmony_ci	/* .iInterface = DYNAMIC */
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct usb_cdc_header_desc mdlm_header_desc = {
8962306a36Sopenharmony_ci	.bLength =		sizeof mdlm_header_desc,
9062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
9162306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	.bcdCDC =		cpu_to_le16(0x0110),
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic struct usb_cdc_mdlm_desc mdlm_desc = {
9762306a36Sopenharmony_ci	.bLength =		sizeof mdlm_desc,
9862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
9962306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_MDLM_TYPE,
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	.bcdVersion =		cpu_to_le16(0x0100),
10262306a36Sopenharmony_ci	.bGUID = {
10362306a36Sopenharmony_ci		0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
10462306a36Sopenharmony_ci		0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
10562306a36Sopenharmony_ci	},
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
10962306a36Sopenharmony_ci * can't really use its struct.  All we do here is say that we're using
11062306a36Sopenharmony_ci * the submode of "SAFE" which directly matches the CDC Subset.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_cistatic u8 mdlm_detail_desc[] = {
11362306a36Sopenharmony_ci	6,
11462306a36Sopenharmony_ci	USB_DT_CS_INTERFACE,
11562306a36Sopenharmony_ci	USB_CDC_MDLM_DETAIL_TYPE,
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	0,	/* "SAFE" */
11862306a36Sopenharmony_ci	0,	/* network control capabilities (none) */
11962306a36Sopenharmony_ci	0,	/* network data capabilities ("raw" encapsulation) */
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct usb_cdc_ether_desc ether_desc = {
12362306a36Sopenharmony_ci	.bLength =		sizeof ether_desc,
12462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
12562306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* this descriptor actually adds value, surprise! */
12862306a36Sopenharmony_ci	/* .iMACAddress = DYNAMIC */
12962306a36Sopenharmony_ci	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
13062306a36Sopenharmony_ci	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
13162306a36Sopenharmony_ci	.wNumberMCFilters =	cpu_to_le16(0),
13262306a36Sopenharmony_ci	.bNumberPowerFilters =	0,
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* full speed support: */
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_subset_in_desc = {
13862306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
13962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
14262306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_subset_out_desc = {
14662306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
14762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
15062306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic struct usb_descriptor_header *fs_eth_function[] = {
15462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &subset_data_intf,
15562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_header_desc,
15662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_desc,
15762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_detail_desc,
15862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ether_desc,
15962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_subset_in_desc,
16062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_subset_out_desc,
16162306a36Sopenharmony_ci	NULL,
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/* high speed support: */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_subset_in_desc = {
16762306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
16862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
17162306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_subset_out_desc = {
17562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
17662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
17962306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct usb_descriptor_header *hs_eth_function[] = {
18362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &subset_data_intf,
18462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_header_desc,
18562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_desc,
18662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_detail_desc,
18762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ether_desc,
18862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_subset_in_desc,
18962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_subset_out_desc,
19062306a36Sopenharmony_ci	NULL,
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/* super speed support: */
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_subset_in_desc = {
19662306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
19762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
20062306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_subset_out_desc = {
20462306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
20562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
20862306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
20962306a36Sopenharmony_ci};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc = {
21262306a36Sopenharmony_ci	.bLength =		sizeof ss_subset_bulk_comp_desc,
21362306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* the following 2 values can be tweaked if necessary */
21662306a36Sopenharmony_ci	/* .bMaxBurst =		0, */
21762306a36Sopenharmony_ci	/* .bmAttributes =	0, */
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic struct usb_descriptor_header *ss_eth_function[] = {
22162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &subset_data_intf,
22262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_header_desc,
22362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_desc,
22462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &mdlm_detail_desc,
22562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ether_desc,
22662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_subset_in_desc,
22762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
22862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_subset_out_desc,
22962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
23062306a36Sopenharmony_ci	NULL,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/* string descriptors: */
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic struct usb_string geth_string_defs[] = {
23662306a36Sopenharmony_ci	[0].s = "CDC Ethernet Subset/SAFE",
23762306a36Sopenharmony_ci	[1].s = "",
23862306a36Sopenharmony_ci	{  } /* end of list */
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic struct usb_gadget_strings geth_string_table = {
24262306a36Sopenharmony_ci	.language =		0x0409,	/* en-us */
24362306a36Sopenharmony_ci	.strings =		geth_string_defs,
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic struct usb_gadget_strings *geth_strings[] = {
24762306a36Sopenharmony_ci	&geth_string_table,
24862306a36Sopenharmony_ci	NULL,
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct f_gether		*geth = func_to_geth(f);
25662306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
25762306a36Sopenharmony_ci	struct net_device	*net;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* we know alt == 0, so this is an activation or a reset */
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (geth->port.in_ep->enabled) {
26262306a36Sopenharmony_ci		DBG(cdev, "reset cdc subset\n");
26362306a36Sopenharmony_ci		gether_disconnect(&geth->port);
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	DBG(cdev, "init + activate cdc subset\n");
26762306a36Sopenharmony_ci	if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) ||
26862306a36Sopenharmony_ci	    config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
26962306a36Sopenharmony_ci		geth->port.in_ep->desc = NULL;
27062306a36Sopenharmony_ci		geth->port.out_ep->desc = NULL;
27162306a36Sopenharmony_ci		return -EINVAL;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	net = gether_connect(&geth->port);
27562306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(net);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void geth_disable(struct usb_function *f)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct f_gether	*geth = func_to_geth(f);
28162306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	DBG(cdev, "net deactivated\n");
28462306a36Sopenharmony_ci	gether_disconnect(&geth->port);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/* serial function driver setup/binding */
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int
29262306a36Sopenharmony_cigeth_bind(struct usb_configuration *c, struct usb_function *f)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct usb_composite_dev *cdev = c->cdev;
29562306a36Sopenharmony_ci	struct f_gether		*geth = func_to_geth(f);
29662306a36Sopenharmony_ci	struct usb_string	*us;
29762306a36Sopenharmony_ci	int			status;
29862306a36Sopenharmony_ci	struct usb_ep		*ep;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	struct f_gether_opts	*gether_opts;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/*
30562306a36Sopenharmony_ci	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
30662306a36Sopenharmony_ci	 * configurations are bound in sequence with list_for_each_entry,
30762306a36Sopenharmony_ci	 * in each configuration its functions are bound in sequence
30862306a36Sopenharmony_ci	 * with list_for_each_entry, so we assume no race condition
30962306a36Sopenharmony_ci	 * with regard to gether_opts->bound access
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	if (!gether_opts->bound) {
31262306a36Sopenharmony_ci		mutex_lock(&gether_opts->lock);
31362306a36Sopenharmony_ci		gether_set_gadget(gether_opts->net, cdev->gadget);
31462306a36Sopenharmony_ci		status = gether_register_netdev(gether_opts->net);
31562306a36Sopenharmony_ci		mutex_unlock(&gether_opts->lock);
31662306a36Sopenharmony_ci		if (status)
31762306a36Sopenharmony_ci			return status;
31862306a36Sopenharmony_ci		gether_opts->bound = true;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	us = usb_gstrings_attach(cdev, geth_strings,
32262306a36Sopenharmony_ci				 ARRAY_SIZE(geth_string_defs));
32362306a36Sopenharmony_ci	if (IS_ERR(us))
32462306a36Sopenharmony_ci		return PTR_ERR(us);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	subset_data_intf.iInterface = us[0].id;
32762306a36Sopenharmony_ci	ether_desc.iMACAddress = us[1].id;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* allocate instance-specific interface IDs */
33062306a36Sopenharmony_ci	status = usb_interface_id(c, f);
33162306a36Sopenharmony_ci	if (status < 0)
33262306a36Sopenharmony_ci		goto fail;
33362306a36Sopenharmony_ci	subset_data_intf.bInterfaceNumber = status;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	status = -ENODEV;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* allocate instance-specific endpoints */
33862306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
33962306a36Sopenharmony_ci	if (!ep)
34062306a36Sopenharmony_ci		goto fail;
34162306a36Sopenharmony_ci	geth->port.in_ep = ep;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
34462306a36Sopenharmony_ci	if (!ep)
34562306a36Sopenharmony_ci		goto fail;
34662306a36Sopenharmony_ci	geth->port.out_ep = ep;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* support all relevant hardware speeds... we expect that when
34962306a36Sopenharmony_ci	 * hardware is dual speed, all bulk-capable endpoints work at
35062306a36Sopenharmony_ci	 * both speeds
35162306a36Sopenharmony_ci	 */
35262306a36Sopenharmony_ci	hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
35362306a36Sopenharmony_ci	hs_subset_out_desc.bEndpointAddress =
35462306a36Sopenharmony_ci		fs_subset_out_desc.bEndpointAddress;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
35762306a36Sopenharmony_ci	ss_subset_out_desc.bEndpointAddress =
35862306a36Sopenharmony_ci		fs_subset_out_desc.bEndpointAddress;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
36162306a36Sopenharmony_ci			ss_eth_function, ss_eth_function);
36262306a36Sopenharmony_ci	if (status)
36362306a36Sopenharmony_ci		goto fail;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* NOTE:  all that is done without knowing or caring about
36662306a36Sopenharmony_ci	 * the network link ... which is unavailable to this code
36762306a36Sopenharmony_ci	 * until we're activated via set_alt().
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	DBG(cdev, "CDC Subset: IN/%s OUT/%s\n",
37162306a36Sopenharmony_ci			geth->port.in_ep->name, geth->port.out_ep->name);
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cifail:
37562306a36Sopenharmony_ci	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return status;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_gether_opts,
38362306a36Sopenharmony_ci			    func_inst.group);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/* f_gether_item_ops */
38762306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(gether);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/* f_gether_opts_dev_addr */
39062306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/* f_gether_opts_host_addr */
39362306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/* f_gether_opts_qmult */
39662306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/* f_gether_opts_ifname */
39962306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic struct configfs_attribute *gether_attrs[] = {
40262306a36Sopenharmony_ci	&gether_opts_attr_dev_addr,
40362306a36Sopenharmony_ci	&gether_opts_attr_host_addr,
40462306a36Sopenharmony_ci	&gether_opts_attr_qmult,
40562306a36Sopenharmony_ci	&gether_opts_attr_ifname,
40662306a36Sopenharmony_ci	NULL,
40762306a36Sopenharmony_ci};
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic const struct config_item_type gether_func_type = {
41062306a36Sopenharmony_ci	.ct_item_ops	= &gether_item_ops,
41162306a36Sopenharmony_ci	.ct_attrs	= gether_attrs,
41262306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
41362306a36Sopenharmony_ci};
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void geth_free_inst(struct usb_function_instance *f)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct f_gether_opts *opts;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	opts = container_of(f, struct f_gether_opts, func_inst);
42062306a36Sopenharmony_ci	if (opts->bound)
42162306a36Sopenharmony_ci		gether_cleanup(netdev_priv(opts->net));
42262306a36Sopenharmony_ci	else
42362306a36Sopenharmony_ci		free_netdev(opts->net);
42462306a36Sopenharmony_ci	kfree(opts);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic struct usb_function_instance *geth_alloc_inst(void)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct f_gether_opts *opts;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
43262306a36Sopenharmony_ci	if (!opts)
43362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
43462306a36Sopenharmony_ci	mutex_init(&opts->lock);
43562306a36Sopenharmony_ci	opts->func_inst.free_func_inst = geth_free_inst;
43662306a36Sopenharmony_ci	opts->net = gether_setup_default();
43762306a36Sopenharmony_ci	if (IS_ERR(opts->net)) {
43862306a36Sopenharmony_ci		struct net_device *net = opts->net;
43962306a36Sopenharmony_ci		kfree(opts);
44062306a36Sopenharmony_ci		return ERR_CAST(net);
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	config_group_init_type_name(&opts->func_inst.group, "",
44462306a36Sopenharmony_ci				    &gether_func_type);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return &opts->func_inst;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void geth_free(struct usb_function *f)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct f_gether *eth;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	eth = func_to_geth(f);
45462306a36Sopenharmony_ci	kfree(eth);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void geth_unbind(struct usb_configuration *c, struct usb_function *f)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	geth_string_defs[0].id = 0;
46062306a36Sopenharmony_ci	usb_free_all_descriptors(f);
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic struct usb_function *geth_alloc(struct usb_function_instance *fi)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct f_gether	*geth;
46662306a36Sopenharmony_ci	struct f_gether_opts *opts;
46762306a36Sopenharmony_ci	int status;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* allocate and initialize one new instance */
47062306a36Sopenharmony_ci	geth = kzalloc(sizeof(*geth), GFP_KERNEL);
47162306a36Sopenharmony_ci	if (!geth)
47262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	opts = container_of(fi, struct f_gether_opts, func_inst);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	mutex_lock(&opts->lock);
47762306a36Sopenharmony_ci	opts->refcnt++;
47862306a36Sopenharmony_ci	/* export host's Ethernet address in CDC format */
47962306a36Sopenharmony_ci	status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
48062306a36Sopenharmony_ci					  sizeof(geth->ethaddr));
48162306a36Sopenharmony_ci	if (status < 12) {
48262306a36Sopenharmony_ci		kfree(geth);
48362306a36Sopenharmony_ci		mutex_unlock(&opts->lock);
48462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	geth_string_defs[1].s = geth->ethaddr;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	geth->port.ioport = netdev_priv(opts->net);
48962306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
49062306a36Sopenharmony_ci	geth->port.cdc_filter = DEFAULT_FILTER;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	geth->port.func.name = "cdc_subset";
49362306a36Sopenharmony_ci	geth->port.func.bind = geth_bind;
49462306a36Sopenharmony_ci	geth->port.func.unbind = geth_unbind;
49562306a36Sopenharmony_ci	geth->port.func.set_alt = geth_set_alt;
49662306a36Sopenharmony_ci	geth->port.func.disable = geth_disable;
49762306a36Sopenharmony_ci	geth->port.func.free_func = geth_free;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return &geth->port.func;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
50362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
50462306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell");
505