162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_ecm.c -- USB CDC Ethernet (ECM) 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/* #define VERBOSE_DEBUG */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/etherdevice.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "u_ether.h"
1862306a36Sopenharmony_ci#include "u_ether_configfs.h"
1962306a36Sopenharmony_ci#include "u_ecm.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
2462306a36Sopenharmony_ci * Ethernet link.  The data transfer model is simple (packets sent and
2562306a36Sopenharmony_ci * received over bulk endpoints using normal short packet termination),
2662306a36Sopenharmony_ci * and the control model exposes various data and optional notifications.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * ECM is well standardized and (except for Microsoft) supported by most
2962306a36Sopenharmony_ci * operating systems with USB host support.  It's the preferred interop
3062306a36Sopenharmony_ci * solution for Ethernet over USB, at least for firmware based solutions.
3162306a36Sopenharmony_ci * (Hardware solutions tend to be more minimalist.)  A newer and simpler
3262306a36Sopenharmony_ci * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Note that ECM requires the use of "alternate settings" for its data
3562306a36Sopenharmony_ci * interface.  This means that the set_alt() method has real work to do,
3662306a36Sopenharmony_ci * and also means that a get_alt() method is required.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cienum ecm_notify_state {
4162306a36Sopenharmony_ci	ECM_NOTIFY_NONE,		/* don't notify */
4262306a36Sopenharmony_ci	ECM_NOTIFY_CONNECT,		/* issue CONNECT next */
4362306a36Sopenharmony_ci	ECM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct f_ecm {
4762306a36Sopenharmony_ci	struct gether			port;
4862306a36Sopenharmony_ci	u8				ctrl_id, data_id;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	char				ethaddr[14];
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	struct usb_ep			*notify;
5362306a36Sopenharmony_ci	struct usb_request		*notify_req;
5462306a36Sopenharmony_ci	u8				notify_state;
5562306a36Sopenharmony_ci	atomic_t			notify_count;
5662306a36Sopenharmony_ci	bool				is_open;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* FIXME is_open needs some irq-ish locking
5962306a36Sopenharmony_ci	 * ... possibly the same as port.ioport
6062306a36Sopenharmony_ci	 */
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic inline struct f_ecm *func_to_ecm(struct usb_function *f)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return container_of(f, struct f_ecm, port.func);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Include the status endpoint if we can, even though it's optional.
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
7462306a36Sopenharmony_ci * packet, to simplify cancellation; and a big transfer interval, to
7562306a36Sopenharmony_ci * waste less bandwidth.
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
7862306a36Sopenharmony_ci * if they ignore the connect/disconnect notifications that real aether
7962306a36Sopenharmony_ci * can provide.  More advanced cdc configurations might want to support
8062306a36Sopenharmony_ci * encapsulated commands (vendor-specific, using control-OUT).
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define ECM_STATUS_INTERVAL_MS		32
8462306a36Sopenharmony_ci#define ECM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* interface descriptor: */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic struct usb_interface_assoc_descriptor
9062306a36Sopenharmony_ciecm_iad_descriptor = {
9162306a36Sopenharmony_ci	.bLength =		sizeof ecm_iad_descriptor,
9262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* .bFirstInterface =	DYNAMIC, */
9562306a36Sopenharmony_ci	.bInterfaceCount =	2,	/* control + data */
9662306a36Sopenharmony_ci	.bFunctionClass =	USB_CLASS_COMM,
9762306a36Sopenharmony_ci	.bFunctionSubClass =	USB_CDC_SUBCLASS_ETHERNET,
9862306a36Sopenharmony_ci	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
9962306a36Sopenharmony_ci	/* .iFunction =		DYNAMIC */
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic struct usb_interface_descriptor ecm_control_intf = {
10462306a36Sopenharmony_ci	.bLength =		sizeof ecm_control_intf,
10562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* .bInterfaceNumber = DYNAMIC */
10862306a36Sopenharmony_ci	/* status endpoint is optional; this could be patched later */
10962306a36Sopenharmony_ci	.bNumEndpoints =	1,
11062306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_COMM,
11162306a36Sopenharmony_ci	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ETHERNET,
11262306a36Sopenharmony_ci	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
11362306a36Sopenharmony_ci	/* .iInterface = DYNAMIC */
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic struct usb_cdc_header_desc ecm_header_desc = {
11762306a36Sopenharmony_ci	.bLength =		sizeof ecm_header_desc,
11862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
11962306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	.bcdCDC =		cpu_to_le16(0x0110),
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct usb_cdc_union_desc ecm_union_desc = {
12562306a36Sopenharmony_ci	.bLength =		sizeof(ecm_union_desc),
12662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
12762306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
12862306a36Sopenharmony_ci	/* .bMasterInterface0 =	DYNAMIC */
12962306a36Sopenharmony_ci	/* .bSlaveInterface0 =	DYNAMIC */
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct usb_cdc_ether_desc ecm_desc = {
13362306a36Sopenharmony_ci	.bLength =		sizeof ecm_desc,
13462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
13562306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* this descriptor actually adds value, surprise! */
13862306a36Sopenharmony_ci	/* .iMACAddress = DYNAMIC */
13962306a36Sopenharmony_ci	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
14062306a36Sopenharmony_ci	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
14162306a36Sopenharmony_ci	.wNumberMCFilters =	cpu_to_le16(0),
14262306a36Sopenharmony_ci	.bNumberPowerFilters =	0,
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* the default data interface has no endpoints ... */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic struct usb_interface_descriptor ecm_data_nop_intf = {
14862306a36Sopenharmony_ci	.bLength =		sizeof ecm_data_nop_intf,
14962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	.bInterfaceNumber =	1,
15262306a36Sopenharmony_ci	.bAlternateSetting =	0,
15362306a36Sopenharmony_ci	.bNumEndpoints =	0,
15462306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_CDC_DATA,
15562306a36Sopenharmony_ci	.bInterfaceSubClass =	0,
15662306a36Sopenharmony_ci	.bInterfaceProtocol =	0,
15762306a36Sopenharmony_ci	/* .iInterface = DYNAMIC */
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/* ... but the "real" data interface has two bulk endpoints */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct usb_interface_descriptor ecm_data_intf = {
16362306a36Sopenharmony_ci	.bLength =		sizeof ecm_data_intf,
16462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	.bInterfaceNumber =	1,
16762306a36Sopenharmony_ci	.bAlternateSetting =	1,
16862306a36Sopenharmony_ci	.bNumEndpoints =	2,
16962306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_CDC_DATA,
17062306a36Sopenharmony_ci	.bInterfaceSubClass =	0,
17162306a36Sopenharmony_ci	.bInterfaceProtocol =	0,
17262306a36Sopenharmony_ci	/* .iInterface = DYNAMIC */
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/* full speed support: */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_ecm_notify_desc = {
17862306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
17962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
18262306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_INT,
18362306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(ECM_STATUS_BYTECOUNT),
18462306a36Sopenharmony_ci	.bInterval =		ECM_STATUS_INTERVAL_MS,
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_ecm_in_desc = {
18862306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
18962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
19262306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_ecm_out_desc = {
19662306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
19762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
20062306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic struct usb_descriptor_header *ecm_fs_function[] = {
20462306a36Sopenharmony_ci	/* CDC ECM control descriptors */
20562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_iad_descriptor,
20662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_control_intf,
20762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_header_desc,
20862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_union_desc,
20962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_desc,
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* NOTE: status endpoint might need to be removed */
21262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_ecm_notify_desc,
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* data interface, altsettings 0 and 1 */
21562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_data_nop_intf,
21662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_data_intf,
21762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_ecm_in_desc,
21862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_ecm_out_desc,
21962306a36Sopenharmony_ci	NULL,
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/* high speed support: */
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_ecm_notify_desc = {
22562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
22662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
22962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_INT,
23062306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(ECM_STATUS_BYTECOUNT),
23162306a36Sopenharmony_ci	.bInterval =		USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_ecm_in_desc = {
23562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
23662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
23962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
24062306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_ecm_out_desc = {
24462306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
24562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
24862306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
24962306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic struct usb_descriptor_header *ecm_hs_function[] = {
25362306a36Sopenharmony_ci	/* CDC ECM control descriptors */
25462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_iad_descriptor,
25562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_control_intf,
25662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_header_desc,
25762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_union_desc,
25862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_desc,
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* NOTE: status endpoint might need to be removed */
26162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_ecm_notify_desc,
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* data interface, altsettings 0 and 1 */
26462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_data_nop_intf,
26562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_data_intf,
26662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_ecm_in_desc,
26762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_ecm_out_desc,
26862306a36Sopenharmony_ci	NULL,
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/* super speed support: */
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_ecm_notify_desc = {
27462306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
27562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
27862306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_INT,
27962306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(ECM_STATUS_BYTECOUNT),
28062306a36Sopenharmony_ci	.bInterval =		USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
28462306a36Sopenharmony_ci	.bLength =		sizeof ss_ecm_intr_comp_desc,
28562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* the following 3 values can be tweaked if necessary */
28862306a36Sopenharmony_ci	/* .bMaxBurst =		0, */
28962306a36Sopenharmony_ci	/* .bmAttributes =	0, */
29062306a36Sopenharmony_ci	.wBytesPerInterval =	cpu_to_le16(ECM_STATUS_BYTECOUNT),
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_ecm_in_desc = {
29462306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
29562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
29862306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
29962306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_ecm_out_desc = {
30362306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
30462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
30762306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
30862306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
30962306a36Sopenharmony_ci};
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
31262306a36Sopenharmony_ci	.bLength =		sizeof ss_ecm_bulk_comp_desc,
31362306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* the following 2 values can be tweaked if necessary */
31662306a36Sopenharmony_ci	/* .bMaxBurst =		0, */
31762306a36Sopenharmony_ci	/* .bmAttributes =	0, */
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic struct usb_descriptor_header *ecm_ss_function[] = {
32162306a36Sopenharmony_ci	/* CDC ECM control descriptors */
32262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_iad_descriptor,
32362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_control_intf,
32462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_header_desc,
32562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_union_desc,
32662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_desc,
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* NOTE: status endpoint might need to be removed */
32962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_ecm_notify_desc,
33062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* data interface, altsettings 0 and 1 */
33362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_data_nop_intf,
33462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ecm_data_intf,
33562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_ecm_in_desc,
33662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
33762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_ecm_out_desc,
33862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
33962306a36Sopenharmony_ci	NULL,
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/* string descriptors: */
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic struct usb_string ecm_string_defs[] = {
34562306a36Sopenharmony_ci	[0].s = "CDC Ethernet Control Model (ECM)",
34662306a36Sopenharmony_ci	[1].s = "",
34762306a36Sopenharmony_ci	[2].s = "CDC Ethernet Data",
34862306a36Sopenharmony_ci	[3].s = "CDC ECM",
34962306a36Sopenharmony_ci	{  } /* end of list */
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct usb_gadget_strings ecm_string_table = {
35362306a36Sopenharmony_ci	.language =		0x0409,	/* en-us */
35462306a36Sopenharmony_ci	.strings =		ecm_string_defs,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic struct usb_gadget_strings *ecm_strings[] = {
35862306a36Sopenharmony_ci	&ecm_string_table,
35962306a36Sopenharmony_ci	NULL,
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void ecm_do_notify(struct f_ecm *ecm)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct usb_request		*req = ecm->notify_req;
36762306a36Sopenharmony_ci	struct usb_cdc_notification	*event;
36862306a36Sopenharmony_ci	struct usb_composite_dev	*cdev = ecm->port.func.config->cdev;
36962306a36Sopenharmony_ci	__le32				*data;
37062306a36Sopenharmony_ci	int				status;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/* notification already in flight? */
37362306a36Sopenharmony_ci	if (atomic_read(&ecm->notify_count))
37462306a36Sopenharmony_ci		return;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	event = req->buf;
37762306a36Sopenharmony_ci	switch (ecm->notify_state) {
37862306a36Sopenharmony_ci	case ECM_NOTIFY_NONE:
37962306a36Sopenharmony_ci		return;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	case ECM_NOTIFY_CONNECT:
38262306a36Sopenharmony_ci		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
38362306a36Sopenharmony_ci		if (ecm->is_open)
38462306a36Sopenharmony_ci			event->wValue = cpu_to_le16(1);
38562306a36Sopenharmony_ci		else
38662306a36Sopenharmony_ci			event->wValue = cpu_to_le16(0);
38762306a36Sopenharmony_ci		event->wLength = 0;
38862306a36Sopenharmony_ci		req->length = sizeof *event;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		DBG(cdev, "notify connect %s\n",
39162306a36Sopenharmony_ci				ecm->is_open ? "true" : "false");
39262306a36Sopenharmony_ci		ecm->notify_state = ECM_NOTIFY_SPEED;
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	case ECM_NOTIFY_SPEED:
39662306a36Sopenharmony_ci		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
39762306a36Sopenharmony_ci		event->wValue = cpu_to_le16(0);
39862306a36Sopenharmony_ci		event->wLength = cpu_to_le16(8);
39962306a36Sopenharmony_ci		req->length = ECM_STATUS_BYTECOUNT;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		/* SPEED_CHANGE data is up/down speeds in bits/sec */
40262306a36Sopenharmony_ci		data = req->buf + sizeof *event;
40362306a36Sopenharmony_ci		data[0] = cpu_to_le32(gether_bitrate(cdev->gadget));
40462306a36Sopenharmony_ci		data[1] = data[0];
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		DBG(cdev, "notify speed %d\n", gether_bitrate(cdev->gadget));
40762306a36Sopenharmony_ci		ecm->notify_state = ECM_NOTIFY_NONE;
40862306a36Sopenharmony_ci		break;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	event->bmRequestType = 0xA1;
41162306a36Sopenharmony_ci	event->wIndex = cpu_to_le16(ecm->ctrl_id);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	atomic_inc(&ecm->notify_count);
41462306a36Sopenharmony_ci	status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
41562306a36Sopenharmony_ci	if (status < 0) {
41662306a36Sopenharmony_ci		atomic_dec(&ecm->notify_count);
41762306a36Sopenharmony_ci		DBG(cdev, "notify --> %d\n", status);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void ecm_notify(struct f_ecm *ecm)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	/* NOTE on most versions of Linux, host side cdc-ethernet
42462306a36Sopenharmony_ci	 * won't listen for notifications until its netdevice opens.
42562306a36Sopenharmony_ci	 * The first notification then sits in the FIFO for a long
42662306a36Sopenharmony_ci	 * time, and the second one is queued.
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	ecm->notify_state = ECM_NOTIFY_CONNECT;
42962306a36Sopenharmony_ci	ecm_do_notify(ecm);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct f_ecm			*ecm = req->context;
43562306a36Sopenharmony_ci	struct usb_composite_dev	*cdev = ecm->port.func.config->cdev;
43662306a36Sopenharmony_ci	struct usb_cdc_notification	*event = req->buf;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	switch (req->status) {
43962306a36Sopenharmony_ci	case 0:
44062306a36Sopenharmony_ci		/* no fault */
44162306a36Sopenharmony_ci		atomic_dec(&ecm->notify_count);
44262306a36Sopenharmony_ci		break;
44362306a36Sopenharmony_ci	case -ECONNRESET:
44462306a36Sopenharmony_ci	case -ESHUTDOWN:
44562306a36Sopenharmony_ci		atomic_set(&ecm->notify_count, 0);
44662306a36Sopenharmony_ci		ecm->notify_state = ECM_NOTIFY_NONE;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	default:
44962306a36Sopenharmony_ci		DBG(cdev, "event %02x --> %d\n",
45062306a36Sopenharmony_ci			event->bNotificationType, req->status);
45162306a36Sopenharmony_ci		atomic_dec(&ecm->notify_count);
45262306a36Sopenharmony_ci		break;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci	ecm_do_notify(ecm);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(f);
46062306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
46162306a36Sopenharmony_ci	struct usb_request	*req = cdev->req;
46262306a36Sopenharmony_ci	int			value = -EOPNOTSUPP;
46362306a36Sopenharmony_ci	u16			w_index = le16_to_cpu(ctrl->wIndex);
46462306a36Sopenharmony_ci	u16			w_value = le16_to_cpu(ctrl->wValue);
46562306a36Sopenharmony_ci	u16			w_length = le16_to_cpu(ctrl->wLength);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* composite driver infrastructure handles everything except
46862306a36Sopenharmony_ci	 * CDC class messages; interface activation uses set_alt().
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
47162306a36Sopenharmony_ci	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
47262306a36Sopenharmony_ci			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
47362306a36Sopenharmony_ci		/* see 6.2.30: no data, wIndex = interface,
47462306a36Sopenharmony_ci		 * wValue = packet filter bitmap
47562306a36Sopenharmony_ci		 */
47662306a36Sopenharmony_ci		if (w_length != 0 || w_index != ecm->ctrl_id)
47762306a36Sopenharmony_ci			goto invalid;
47862306a36Sopenharmony_ci		DBG(cdev, "packet filter %02x\n", w_value);
47962306a36Sopenharmony_ci		/* REVISIT locking of cdc_filter.  This assumes the UDC
48062306a36Sopenharmony_ci		 * driver won't have a concurrent packet TX irq running on
48162306a36Sopenharmony_ci		 * another CPU; or that if it does, this write is atomic...
48262306a36Sopenharmony_ci		 */
48362306a36Sopenharmony_ci		ecm->port.cdc_filter = w_value;
48462306a36Sopenharmony_ci		value = 0;
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* and optionally:
48862306a36Sopenharmony_ci	 * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
48962306a36Sopenharmony_ci	 * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
49062306a36Sopenharmony_ci	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
49162306a36Sopenharmony_ci	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
49262306a36Sopenharmony_ci	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
49362306a36Sopenharmony_ci	 * case USB_CDC_GET_ETHERNET_STATISTIC:
49462306a36Sopenharmony_ci	 */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	default:
49762306a36Sopenharmony_ciinvalid:
49862306a36Sopenharmony_ci		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
49962306a36Sopenharmony_ci			ctrl->bRequestType, ctrl->bRequest,
50062306a36Sopenharmony_ci			w_value, w_index, w_length);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* respond with data transfer or status phase? */
50462306a36Sopenharmony_ci	if (value >= 0) {
50562306a36Sopenharmony_ci		DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
50662306a36Sopenharmony_ci			ctrl->bRequestType, ctrl->bRequest,
50762306a36Sopenharmony_ci			w_value, w_index, w_length);
50862306a36Sopenharmony_ci		req->zero = 0;
50962306a36Sopenharmony_ci		req->length = value;
51062306a36Sopenharmony_ci		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
51162306a36Sopenharmony_ci		if (value < 0)
51262306a36Sopenharmony_ci			ERROR(cdev, "ecm req %02x.%02x response err %d\n",
51362306a36Sopenharmony_ci					ctrl->bRequestType, ctrl->bRequest,
51462306a36Sopenharmony_ci					value);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* device either stalls (value < 0) or reports success */
51862306a36Sopenharmony_ci	return value;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(f);
52562306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Control interface has only altsetting 0 */
52862306a36Sopenharmony_ci	if (intf == ecm->ctrl_id) {
52962306a36Sopenharmony_ci		if (alt != 0)
53062306a36Sopenharmony_ci			goto fail;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		VDBG(cdev, "reset ecm control %d\n", intf);
53362306a36Sopenharmony_ci		usb_ep_disable(ecm->notify);
53462306a36Sopenharmony_ci		if (!(ecm->notify->desc)) {
53562306a36Sopenharmony_ci			VDBG(cdev, "init ecm ctrl %d\n", intf);
53662306a36Sopenharmony_ci			if (config_ep_by_speed(cdev->gadget, f, ecm->notify))
53762306a36Sopenharmony_ci				goto fail;
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci		usb_ep_enable(ecm->notify);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* Data interface has two altsettings, 0 and 1 */
54262306a36Sopenharmony_ci	} else if (intf == ecm->data_id) {
54362306a36Sopenharmony_ci		if (alt > 1)
54462306a36Sopenharmony_ci			goto fail;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		if (ecm->port.in_ep->enabled) {
54762306a36Sopenharmony_ci			DBG(cdev, "reset ecm\n");
54862306a36Sopenharmony_ci			gether_disconnect(&ecm->port);
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		if (!ecm->port.in_ep->desc ||
55262306a36Sopenharmony_ci		    !ecm->port.out_ep->desc) {
55362306a36Sopenharmony_ci			DBG(cdev, "init ecm\n");
55462306a36Sopenharmony_ci			if (config_ep_by_speed(cdev->gadget, f,
55562306a36Sopenharmony_ci					       ecm->port.in_ep) ||
55662306a36Sopenharmony_ci			    config_ep_by_speed(cdev->gadget, f,
55762306a36Sopenharmony_ci					       ecm->port.out_ep)) {
55862306a36Sopenharmony_ci				ecm->port.in_ep->desc = NULL;
55962306a36Sopenharmony_ci				ecm->port.out_ep->desc = NULL;
56062306a36Sopenharmony_ci				goto fail;
56162306a36Sopenharmony_ci			}
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		/* CDC Ethernet only sends data in non-default altsettings.
56562306a36Sopenharmony_ci		 * Changing altsettings resets filters, statistics, etc.
56662306a36Sopenharmony_ci		 */
56762306a36Sopenharmony_ci		if (alt == 1) {
56862306a36Sopenharmony_ci			struct net_device	*net;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci			/* Enable zlps by default for ECM conformance;
57162306a36Sopenharmony_ci			 * override for musb_hdrc (avoids txdma ovhead).
57262306a36Sopenharmony_ci			 */
57362306a36Sopenharmony_ci			ecm->port.is_zlp_ok =
57462306a36Sopenharmony_ci				gadget_is_zlp_supported(cdev->gadget);
57562306a36Sopenharmony_ci			ecm->port.cdc_filter = DEFAULT_FILTER;
57662306a36Sopenharmony_ci			DBG(cdev, "activate ecm\n");
57762306a36Sopenharmony_ci			net = gether_connect(&ecm->port);
57862306a36Sopenharmony_ci			if (IS_ERR(net))
57962306a36Sopenharmony_ci				return PTR_ERR(net);
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		/* NOTE this can be a minor disagreement with the ECM spec,
58362306a36Sopenharmony_ci		 * which says speed notifications will "always" follow
58462306a36Sopenharmony_ci		 * connection notifications.  But we allow one connect to
58562306a36Sopenharmony_ci		 * follow another (if the first is in flight), and instead
58662306a36Sopenharmony_ci		 * just guarantee that a speed notification is always sent.
58762306a36Sopenharmony_ci		 */
58862306a36Sopenharmony_ci		ecm_notify(ecm);
58962306a36Sopenharmony_ci	} else
59062306a36Sopenharmony_ci		goto fail;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return 0;
59362306a36Sopenharmony_cifail:
59462306a36Sopenharmony_ci	return -EINVAL;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/* Because the data interface supports multiple altsettings,
59862306a36Sopenharmony_ci * this ECM function *MUST* implement a get_alt() method.
59962306a36Sopenharmony_ci */
60062306a36Sopenharmony_cistatic int ecm_get_alt(struct usb_function *f, unsigned intf)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(f);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (intf == ecm->ctrl_id)
60562306a36Sopenharmony_ci		return 0;
60662306a36Sopenharmony_ci	return ecm->port.in_ep->enabled ? 1 : 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void ecm_disable(struct usb_function *f)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(f);
61262306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	DBG(cdev, "ecm deactivated\n");
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (ecm->port.in_ep->enabled) {
61762306a36Sopenharmony_ci		gether_disconnect(&ecm->port);
61862306a36Sopenharmony_ci	} else {
61962306a36Sopenharmony_ci		ecm->port.in_ep->desc = NULL;
62062306a36Sopenharmony_ci		ecm->port.out_ep->desc = NULL;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	usb_ep_disable(ecm->notify);
62462306a36Sopenharmony_ci	ecm->notify->desc = NULL;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/*
63062306a36Sopenharmony_ci * Callbacks let us notify the host about connect/disconnect when the
63162306a36Sopenharmony_ci * net device is opened or closed.
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * For testing, note that link states on this side include both opened
63462306a36Sopenharmony_ci * and closed variants of:
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci *   - disconnected/unconfigured
63762306a36Sopenharmony_ci *   - configured but inactive (data alt 0)
63862306a36Sopenharmony_ci *   - configured and active (data alt 1)
63962306a36Sopenharmony_ci *
64062306a36Sopenharmony_ci * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
64162306a36Sopenharmony_ci * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
64262306a36Sopenharmony_ci * imply the host is actually polling the notification endpoint, and
64362306a36Sopenharmony_ci * likewise that "active" doesn't imply it's actually using the data
64462306a36Sopenharmony_ci * endpoints for traffic.
64562306a36Sopenharmony_ci */
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void ecm_open(struct gether *geth)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(&geth->func);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	DBG(ecm->port.func.config->cdev, "%s\n", __func__);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	ecm->is_open = true;
65462306a36Sopenharmony_ci	ecm_notify(ecm);
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic void ecm_close(struct gether *geth)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(&geth->func);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	DBG(ecm->port.func.config->cdev, "%s\n", __func__);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	ecm->is_open = false;
66462306a36Sopenharmony_ci	ecm_notify(ecm);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/* ethernet function driver setup/binding */
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int
67262306a36Sopenharmony_ciecm_bind(struct usb_configuration *c, struct usb_function *f)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct usb_composite_dev *cdev = c->cdev;
67562306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(f);
67662306a36Sopenharmony_ci	struct usb_string	*us;
67762306a36Sopenharmony_ci	int			status = 0;
67862306a36Sopenharmony_ci	struct usb_ep		*ep;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	struct f_ecm_opts	*ecm_opts;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (!can_support_ecm(cdev->gadget))
68362306a36Sopenharmony_ci		return -EINVAL;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	mutex_lock(&ecm_opts->lock);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	gether_set_gadget(ecm_opts->net, cdev->gadget);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (!ecm_opts->bound) {
69262306a36Sopenharmony_ci		status = gether_register_netdev(ecm_opts->net);
69362306a36Sopenharmony_ci		ecm_opts->bound = true;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	mutex_unlock(&ecm_opts->lock);
69762306a36Sopenharmony_ci	if (status)
69862306a36Sopenharmony_ci		return status;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	ecm_string_defs[1].s = ecm->ethaddr;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	us = usb_gstrings_attach(cdev, ecm_strings,
70362306a36Sopenharmony_ci				 ARRAY_SIZE(ecm_string_defs));
70462306a36Sopenharmony_ci	if (IS_ERR(us))
70562306a36Sopenharmony_ci		return PTR_ERR(us);
70662306a36Sopenharmony_ci	ecm_control_intf.iInterface = us[0].id;
70762306a36Sopenharmony_ci	ecm_data_intf.iInterface = us[2].id;
70862306a36Sopenharmony_ci	ecm_desc.iMACAddress = us[1].id;
70962306a36Sopenharmony_ci	ecm_iad_descriptor.iFunction = us[3].id;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* allocate instance-specific interface IDs */
71262306a36Sopenharmony_ci	status = usb_interface_id(c, f);
71362306a36Sopenharmony_ci	if (status < 0)
71462306a36Sopenharmony_ci		goto fail;
71562306a36Sopenharmony_ci	ecm->ctrl_id = status;
71662306a36Sopenharmony_ci	ecm_iad_descriptor.bFirstInterface = status;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	ecm_control_intf.bInterfaceNumber = status;
71962306a36Sopenharmony_ci	ecm_union_desc.bMasterInterface0 = status;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	status = usb_interface_id(c, f);
72262306a36Sopenharmony_ci	if (status < 0)
72362306a36Sopenharmony_ci		goto fail;
72462306a36Sopenharmony_ci	ecm->data_id = status;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	ecm_data_nop_intf.bInterfaceNumber = status;
72762306a36Sopenharmony_ci	ecm_data_intf.bInterfaceNumber = status;
72862306a36Sopenharmony_ci	ecm_union_desc.bSlaveInterface0 = status;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	status = -ENODEV;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* allocate instance-specific endpoints */
73362306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
73462306a36Sopenharmony_ci	if (!ep)
73562306a36Sopenharmony_ci		goto fail;
73662306a36Sopenharmony_ci	ecm->port.in_ep = ep;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
73962306a36Sopenharmony_ci	if (!ep)
74062306a36Sopenharmony_ci		goto fail;
74162306a36Sopenharmony_ci	ecm->port.out_ep = ep;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* NOTE:  a status/notification endpoint is *OPTIONAL* but we
74462306a36Sopenharmony_ci	 * don't treat it that way.  It's simpler, and some newer CDC
74562306a36Sopenharmony_ci	 * profiles (wireless handsets) no longer treat it as optional.
74662306a36Sopenharmony_ci	 */
74762306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
74862306a36Sopenharmony_ci	if (!ep)
74962306a36Sopenharmony_ci		goto fail;
75062306a36Sopenharmony_ci	ecm->notify = ep;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	status = -ENOMEM;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	/* allocate notification request and buffer */
75562306a36Sopenharmony_ci	ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
75662306a36Sopenharmony_ci	if (!ecm->notify_req)
75762306a36Sopenharmony_ci		goto fail;
75862306a36Sopenharmony_ci	ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
75962306a36Sopenharmony_ci	if (!ecm->notify_req->buf)
76062306a36Sopenharmony_ci		goto fail;
76162306a36Sopenharmony_ci	ecm->notify_req->context = ecm;
76262306a36Sopenharmony_ci	ecm->notify_req->complete = ecm_notify_complete;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	/* support all relevant hardware speeds... we expect that when
76562306a36Sopenharmony_ci	 * hardware is dual speed, all bulk-capable endpoints work at
76662306a36Sopenharmony_ci	 * both speeds
76762306a36Sopenharmony_ci	 */
76862306a36Sopenharmony_ci	hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
76962306a36Sopenharmony_ci	hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
77062306a36Sopenharmony_ci	hs_ecm_notify_desc.bEndpointAddress =
77162306a36Sopenharmony_ci		fs_ecm_notify_desc.bEndpointAddress;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
77462306a36Sopenharmony_ci	ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
77562306a36Sopenharmony_ci	ss_ecm_notify_desc.bEndpointAddress =
77662306a36Sopenharmony_ci		fs_ecm_notify_desc.bEndpointAddress;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
77962306a36Sopenharmony_ci			ecm_ss_function, ecm_ss_function);
78062306a36Sopenharmony_ci	if (status)
78162306a36Sopenharmony_ci		goto fail;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* NOTE:  all that is done without knowing or caring about
78462306a36Sopenharmony_ci	 * the network link ... which is unavailable to this code
78562306a36Sopenharmony_ci	 * until we're activated via set_alt().
78662306a36Sopenharmony_ci	 */
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ecm->port.open = ecm_open;
78962306a36Sopenharmony_ci	ecm->port.close = ecm_close;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n",
79262306a36Sopenharmony_ci			ecm->port.in_ep->name, ecm->port.out_ep->name,
79362306a36Sopenharmony_ci			ecm->notify->name);
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cifail:
79762306a36Sopenharmony_ci	if (ecm->notify_req) {
79862306a36Sopenharmony_ci		kfree(ecm->notify_req->buf);
79962306a36Sopenharmony_ci		usb_ep_free_request(ecm->notify, ecm->notify_req);
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return status;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_ecm_opts,
81062306a36Sopenharmony_ci			    func_inst.group);
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci/* f_ecm_item_ops */
81462306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(ecm);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci/* f_ecm_opts_dev_addr */
81762306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci/* f_ecm_opts_host_addr */
82062306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/* f_ecm_opts_qmult */
82362306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/* f_ecm_opts_ifname */
82662306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic struct configfs_attribute *ecm_attrs[] = {
82962306a36Sopenharmony_ci	&ecm_opts_attr_dev_addr,
83062306a36Sopenharmony_ci	&ecm_opts_attr_host_addr,
83162306a36Sopenharmony_ci	&ecm_opts_attr_qmult,
83262306a36Sopenharmony_ci	&ecm_opts_attr_ifname,
83362306a36Sopenharmony_ci	NULL,
83462306a36Sopenharmony_ci};
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic const struct config_item_type ecm_func_type = {
83762306a36Sopenharmony_ci	.ct_item_ops	= &ecm_item_ops,
83862306a36Sopenharmony_ci	.ct_attrs	= ecm_attrs,
83962306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
84062306a36Sopenharmony_ci};
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic void ecm_free_inst(struct usb_function_instance *f)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct f_ecm_opts *opts;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	opts = container_of(f, struct f_ecm_opts, func_inst);
84762306a36Sopenharmony_ci	if (opts->bound)
84862306a36Sopenharmony_ci		gether_cleanup(netdev_priv(opts->net));
84962306a36Sopenharmony_ci	else
85062306a36Sopenharmony_ci		free_netdev(opts->net);
85162306a36Sopenharmony_ci	kfree(opts);
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic struct usb_function_instance *ecm_alloc_inst(void)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct f_ecm_opts *opts;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
85962306a36Sopenharmony_ci	if (!opts)
86062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
86162306a36Sopenharmony_ci	mutex_init(&opts->lock);
86262306a36Sopenharmony_ci	opts->func_inst.free_func_inst = ecm_free_inst;
86362306a36Sopenharmony_ci	opts->net = gether_setup_default();
86462306a36Sopenharmony_ci	if (IS_ERR(opts->net)) {
86562306a36Sopenharmony_ci		struct net_device *net = opts->net;
86662306a36Sopenharmony_ci		kfree(opts);
86762306a36Sopenharmony_ci		return ERR_CAST(net);
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	return &opts->func_inst;
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic void ecm_suspend(struct usb_function *f)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct f_ecm *ecm = func_to_ecm(f);
87862306a36Sopenharmony_ci	struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	DBG(cdev, "ECM Suspend\n");
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	gether_suspend(&ecm->port);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic void ecm_resume(struct usb_function *f)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct f_ecm *ecm = func_to_ecm(f);
88862306a36Sopenharmony_ci	struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	DBG(cdev, "ECM Resume\n");
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	gether_resume(&ecm->port);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic void ecm_free(struct usb_function *f)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct f_ecm *ecm;
89862306a36Sopenharmony_ci	struct f_ecm_opts *opts;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	ecm = func_to_ecm(f);
90162306a36Sopenharmony_ci	opts = container_of(f->fi, struct f_ecm_opts, func_inst);
90262306a36Sopenharmony_ci	kfree(ecm);
90362306a36Sopenharmony_ci	mutex_lock(&opts->lock);
90462306a36Sopenharmony_ci	opts->refcnt--;
90562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct f_ecm		*ecm = func_to_ecm(f);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	DBG(c->cdev, "ecm unbind\n");
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	usb_free_all_descriptors(f);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (atomic_read(&ecm->notify_count)) {
91762306a36Sopenharmony_ci		usb_ep_dequeue(ecm->notify, ecm->notify_req);
91862306a36Sopenharmony_ci		atomic_set(&ecm->notify_count, 0);
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	kfree(ecm->notify_req->buf);
92262306a36Sopenharmony_ci	usb_ep_free_request(ecm->notify, ecm->notify_req);
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic struct usb_function *ecm_alloc(struct usb_function_instance *fi)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	struct f_ecm	*ecm;
92862306a36Sopenharmony_ci	struct f_ecm_opts *opts;
92962306a36Sopenharmony_ci	int status;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* allocate and initialize one new instance */
93262306a36Sopenharmony_ci	ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
93362306a36Sopenharmony_ci	if (!ecm)
93462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	opts = container_of(fi, struct f_ecm_opts, func_inst);
93762306a36Sopenharmony_ci	mutex_lock(&opts->lock);
93862306a36Sopenharmony_ci	opts->refcnt++;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* export host's Ethernet address in CDC format */
94162306a36Sopenharmony_ci	status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,
94262306a36Sopenharmony_ci					  sizeof(ecm->ethaddr));
94362306a36Sopenharmony_ci	if (status < 12) {
94462306a36Sopenharmony_ci		kfree(ecm);
94562306a36Sopenharmony_ci		mutex_unlock(&opts->lock);
94662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	ecm->port.ioport = netdev_priv(opts->net);
95062306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
95162306a36Sopenharmony_ci	ecm->port.cdc_filter = DEFAULT_FILTER;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	ecm->port.func.name = "cdc_ethernet";
95462306a36Sopenharmony_ci	/* descriptors are per-instance copies */
95562306a36Sopenharmony_ci	ecm->port.func.bind = ecm_bind;
95662306a36Sopenharmony_ci	ecm->port.func.unbind = ecm_unbind;
95762306a36Sopenharmony_ci	ecm->port.func.set_alt = ecm_set_alt;
95862306a36Sopenharmony_ci	ecm->port.func.get_alt = ecm_get_alt;
95962306a36Sopenharmony_ci	ecm->port.func.setup = ecm_setup;
96062306a36Sopenharmony_ci	ecm->port.func.disable = ecm_disable;
96162306a36Sopenharmony_ci	ecm->port.func.free_func = ecm_free;
96262306a36Sopenharmony_ci	ecm->port.func.suspend = ecm_suspend;
96362306a36Sopenharmony_ci	ecm->port.func.resume = ecm_resume;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	return &ecm->port.func;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
96962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
97062306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell");
971