162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_rndis.c -- RNDIS link function driver
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 * Copyright (C) 2009 Samsung Electronics
962306a36Sopenharmony_ci *                    Author: Michal Nazarewicz (mina86@mina86.com)
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* #define VERBOSE_DEBUG */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/etherdevice.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/atomic.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "u_ether.h"
2362306a36Sopenharmony_ci#include "u_ether_configfs.h"
2462306a36Sopenharmony_ci#include "u_rndis.h"
2562306a36Sopenharmony_ci#include "rndis.h"
2662306a36Sopenharmony_ci#include "configfs.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
3062306a36Sopenharmony_ci * been promoted instead of the standard CDC Ethernet.  The published RNDIS
3162306a36Sopenharmony_ci * spec is ambiguous, incomplete, and needlessly complex.  Variants such as
3262306a36Sopenharmony_ci * ActiveSync have even worse status in terms of specification.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * In short:  it's a protocol controlled by (and for) Microsoft, not for an
3562306a36Sopenharmony_ci * Open ecosystem or markets.  Linux supports it *only* because Microsoft
3662306a36Sopenharmony_ci * doesn't support the CDC Ethernet standard.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * The RNDIS data transfer model is complex, with multiple Ethernet packets
3962306a36Sopenharmony_ci * per USB message, and out of band data.  The control model is built around
4062306a36Sopenharmony_ci * what's essentially an "RNDIS RPC" protocol.  It's all wrapped in a CDC ACM
4162306a36Sopenharmony_ci * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
4262306a36Sopenharmony_ci * useless (they're ignored).  RNDIS expects to be the only function in its
4362306a36Sopenharmony_ci * configuration, so it's no real help if you need composite devices; and
4462306a36Sopenharmony_ci * it expects to be the first configuration too.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * There is a single technical advantage of RNDIS over CDC Ethernet, if you
4762306a36Sopenharmony_ci * discount the fluff that its RPC can be made to deliver: it doesn't need
4862306a36Sopenharmony_ci * a NOP altsetting for the data interface.  That lets it work on some of the
4962306a36Sopenharmony_ci * "so smart it's stupid" hardware which takes over configuration changes
5062306a36Sopenharmony_ci * from the software, and adds restrictions like "no altsettings".
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * Unfortunately MSFT's RNDIS drivers are buggy.  They hang or oops, and
5362306a36Sopenharmony_ci * have all sorts of contrary-to-specification oddities that can prevent
5462306a36Sopenharmony_ci * them from working sanely.  Since bugfixes (or accurate specs, letting
5562306a36Sopenharmony_ci * Linux work around those bugs) are unlikely to ever come from MSFT, you
5662306a36Sopenharmony_ci * may want to avoid using RNDIS on purely operational grounds.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Omissions from the RNDIS 1.0 specification include:
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci *   - Power management ... references data that's scattered around lots
6162306a36Sopenharmony_ci *     of other documentation, which is incorrect/incomplete there too.
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci *   - There are various undocumented protocol requirements, like the need
6462306a36Sopenharmony_ci *     to send garbage in some control-OUT messages.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci *   - MS-Windows drivers sometimes emit undocumented requests.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistruct f_rndis {
7062306a36Sopenharmony_ci	struct gether			port;
7162306a36Sopenharmony_ci	u8				ctrl_id, data_id;
7262306a36Sopenharmony_ci	u8				ethaddr[ETH_ALEN];
7362306a36Sopenharmony_ci	u32				vendorID;
7462306a36Sopenharmony_ci	const char			*manufacturer;
7562306a36Sopenharmony_ci	struct rndis_params		*params;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	struct usb_ep			*notify;
7862306a36Sopenharmony_ci	struct usb_request		*notify_req;
7962306a36Sopenharmony_ci	atomic_t			notify_count;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline struct f_rndis *func_to_rndis(struct usb_function *f)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return container_of(f, struct f_rndis, port.func);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define RNDIS_STATUS_INTERVAL_MS	32
9362306a36Sopenharmony_ci#define STATUS_BYTECOUNT		8	/* 8 bytes data */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* interface descriptor: */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct usb_interface_descriptor rndis_control_intf = {
9962306a36Sopenharmony_ci	.bLength =		sizeof rndis_control_intf,
10062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* .bInterfaceNumber = DYNAMIC */
10362306a36Sopenharmony_ci	/* status endpoint is optional; this could be patched later */
10462306a36Sopenharmony_ci	.bNumEndpoints =	1,
10562306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_COMM,
10662306a36Sopenharmony_ci	.bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
10762306a36Sopenharmony_ci	.bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR,
10862306a36Sopenharmony_ci	/* .iInterface = DYNAMIC */
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct usb_cdc_header_desc header_desc = {
11262306a36Sopenharmony_ci	.bLength =		sizeof header_desc,
11362306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
11462306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	.bcdCDC =		cpu_to_le16(0x0110),
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
12062306a36Sopenharmony_ci	.bLength =		sizeof call_mgmt_descriptor,
12162306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
12262306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	.bmCapabilities =	0x00,
12562306a36Sopenharmony_ci	.bDataInterface =	0x01,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
12962306a36Sopenharmony_ci	.bLength =		sizeof rndis_acm_descriptor,
13062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
13162306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	.bmCapabilities =	0x00,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct usb_cdc_union_desc rndis_union_desc = {
13762306a36Sopenharmony_ci	.bLength =		sizeof(rndis_union_desc),
13862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
13962306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
14062306a36Sopenharmony_ci	/* .bMasterInterface0 =	DYNAMIC */
14162306a36Sopenharmony_ci	/* .bSlaveInterface0 =	DYNAMIC */
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* the data interface has two bulk endpoints */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct usb_interface_descriptor rndis_data_intf = {
14762306a36Sopenharmony_ci	.bLength =		sizeof rndis_data_intf,
14862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* .bInterfaceNumber = DYNAMIC */
15162306a36Sopenharmony_ci	.bNumEndpoints =	2,
15262306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_CDC_DATA,
15362306a36Sopenharmony_ci	.bInterfaceSubClass =	0,
15462306a36Sopenharmony_ci	.bInterfaceProtocol =	0,
15562306a36Sopenharmony_ci	/* .iInterface = DYNAMIC */
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic struct usb_interface_assoc_descriptor
16062306a36Sopenharmony_cirndis_iad_descriptor = {
16162306a36Sopenharmony_ci	.bLength =		sizeof rndis_iad_descriptor,
16262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	.bFirstInterface =	0, /* XXX, hardcoded */
16562306a36Sopenharmony_ci	.bInterfaceCount = 	2,	// control + data
16662306a36Sopenharmony_ci	.bFunctionClass =	USB_CLASS_COMM,
16762306a36Sopenharmony_ci	.bFunctionSubClass =	USB_CDC_SUBCLASS_ETHERNET,
16862306a36Sopenharmony_ci	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
16962306a36Sopenharmony_ci	/* .iFunction = DYNAMIC */
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/* full speed support: */
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_notify_desc = {
17562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
17662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
17962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_INT,
18062306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(STATUS_BYTECOUNT),
18162306a36Sopenharmony_ci	.bInterval =		RNDIS_STATUS_INTERVAL_MS,
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_in_desc = {
18562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
18662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
18962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_out_desc = {
19362306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
19462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
19762306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic struct usb_descriptor_header *eth_fs_function[] = {
20162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_iad_descriptor,
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* control interface matches ACM, not Ethernet */
20462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_control_intf,
20562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &header_desc,
20662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &call_mgmt_descriptor,
20762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_acm_descriptor,
20862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_union_desc,
20962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_notify_desc,
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* data interface has no altsetting */
21262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_data_intf,
21362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_in_desc,
21462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_out_desc,
21562306a36Sopenharmony_ci	NULL,
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* high speed support: */
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_notify_desc = {
22162306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
22262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
22562306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_INT,
22662306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(STATUS_BYTECOUNT),
22762306a36Sopenharmony_ci	.bInterval =		USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_in_desc = {
23162306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
23262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
23562306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
23662306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
23762306a36Sopenharmony_ci};
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_out_desc = {
24062306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
24162306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
24462306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
24562306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic struct usb_descriptor_header *eth_hs_function[] = {
24962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_iad_descriptor,
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* control interface matches ACM, not Ethernet */
25262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_control_intf,
25362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &header_desc,
25462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &call_mgmt_descriptor,
25562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_acm_descriptor,
25662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_union_desc,
25762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_notify_desc,
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* data interface has no altsetting */
26062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_data_intf,
26162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_in_desc,
26262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_out_desc,
26362306a36Sopenharmony_ci	NULL,
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* super speed support: */
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_notify_desc = {
26962306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
27062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
27362306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_INT,
27462306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(STATUS_BYTECOUNT),
27562306a36Sopenharmony_ci	.bInterval =		USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
27962306a36Sopenharmony_ci	.bLength =		sizeof ss_intr_comp_desc,
28062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* the following 3 values can be tweaked if necessary */
28362306a36Sopenharmony_ci	/* .bMaxBurst =		0, */
28462306a36Sopenharmony_ci	/* .bmAttributes =	0, */
28562306a36Sopenharmony_ci	.wBytesPerInterval =	cpu_to_le16(STATUS_BYTECOUNT),
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_in_desc = {
28962306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
29062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
29362306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
29462306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_out_desc = {
29862306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
29962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
30262306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
30362306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
30462306a36Sopenharmony_ci};
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
30762306a36Sopenharmony_ci	.bLength =		sizeof ss_bulk_comp_desc,
30862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* the following 2 values can be tweaked if necessary */
31162306a36Sopenharmony_ci	/* .bMaxBurst =		0, */
31262306a36Sopenharmony_ci	/* .bmAttributes =	0, */
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct usb_descriptor_header *eth_ss_function[] = {
31662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_iad_descriptor,
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* control interface matches ACM, not Ethernet */
31962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_control_intf,
32062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &header_desc,
32162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &call_mgmt_descriptor,
32262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_acm_descriptor,
32362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_union_desc,
32462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_notify_desc,
32562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_intr_comp_desc,
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* data interface has no altsetting */
32862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &rndis_data_intf,
32962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_in_desc,
33062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_bulk_comp_desc,
33162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_out_desc,
33262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_bulk_comp_desc,
33362306a36Sopenharmony_ci	NULL,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/* string descriptors: */
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic struct usb_string rndis_string_defs[] = {
33962306a36Sopenharmony_ci	[0].s = "RNDIS Communications Control",
34062306a36Sopenharmony_ci	[1].s = "RNDIS Ethernet Data",
34162306a36Sopenharmony_ci	[2].s = "RNDIS",
34262306a36Sopenharmony_ci	{  } /* end of list */
34362306a36Sopenharmony_ci};
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic struct usb_gadget_strings rndis_string_table = {
34662306a36Sopenharmony_ci	.language =		0x0409,	/* en-us */
34762306a36Sopenharmony_ci	.strings =		rndis_string_defs,
34862306a36Sopenharmony_ci};
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct usb_gadget_strings *rndis_strings[] = {
35162306a36Sopenharmony_ci	&rndis_string_table,
35262306a36Sopenharmony_ci	NULL,
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic struct sk_buff *rndis_add_header(struct gether *port,
35862306a36Sopenharmony_ci					struct sk_buff *skb)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct sk_buff *skb2;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!skb)
36362306a36Sopenharmony_ci		return NULL;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
36662306a36Sopenharmony_ci	rndis_add_hdr(skb2);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	dev_kfree_skb(skb);
36962306a36Sopenharmony_ci	return skb2;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void rndis_response_available(void *_rndis)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct f_rndis			*rndis = _rndis;
37562306a36Sopenharmony_ci	struct usb_request		*req = rndis->notify_req;
37662306a36Sopenharmony_ci	struct usb_composite_dev	*cdev = rndis->port.func.config->cdev;
37762306a36Sopenharmony_ci	__le32				*data = req->buf;
37862306a36Sopenharmony_ci	int				status;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (atomic_inc_return(&rndis->notify_count) != 1)
38162306a36Sopenharmony_ci		return;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* Send RNDIS RESPONSE_AVAILABLE notification; a
38462306a36Sopenharmony_ci	 * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
38562306a36Sopenharmony_ci	 *
38662306a36Sopenharmony_ci	 * This is the only notification defined by RNDIS.
38762306a36Sopenharmony_ci	 */
38862306a36Sopenharmony_ci	data[0] = cpu_to_le32(1);
38962306a36Sopenharmony_ci	data[1] = cpu_to_le32(0);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
39262306a36Sopenharmony_ci	if (status) {
39362306a36Sopenharmony_ci		atomic_dec(&rndis->notify_count);
39462306a36Sopenharmony_ci		DBG(cdev, "notify/0 --> %d\n", status);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct f_rndis			*rndis = req->context;
40162306a36Sopenharmony_ci	struct usb_composite_dev	*cdev = rndis->port.func.config->cdev;
40262306a36Sopenharmony_ci	int				status = req->status;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* after TX:
40562306a36Sopenharmony_ci	 *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
40662306a36Sopenharmony_ci	 *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	switch (status) {
40962306a36Sopenharmony_ci	case -ECONNRESET:
41062306a36Sopenharmony_ci	case -ESHUTDOWN:
41162306a36Sopenharmony_ci		/* connection gone */
41262306a36Sopenharmony_ci		atomic_set(&rndis->notify_count, 0);
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	default:
41562306a36Sopenharmony_ci		DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
41662306a36Sopenharmony_ci			ep->name, status,
41762306a36Sopenharmony_ci			req->actual, req->length);
41862306a36Sopenharmony_ci		fallthrough;
41962306a36Sopenharmony_ci	case 0:
42062306a36Sopenharmony_ci		if (ep != rndis->notify)
42162306a36Sopenharmony_ci			break;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
42462306a36Sopenharmony_ci		 * notifications by resending until we're done
42562306a36Sopenharmony_ci		 */
42662306a36Sopenharmony_ci		if (atomic_dec_and_test(&rndis->notify_count))
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
42962306a36Sopenharmony_ci		if (status) {
43062306a36Sopenharmony_ci			atomic_dec(&rndis->notify_count);
43162306a36Sopenharmony_ci			DBG(cdev, "notify/1 --> %d\n", status);
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci		break;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct f_rndis			*rndis = req->context;
44062306a36Sopenharmony_ci	int				status;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
44362306a36Sopenharmony_ci//	spin_lock(&dev->lock);
44462306a36Sopenharmony_ci	status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
44562306a36Sopenharmony_ci	if (status < 0)
44662306a36Sopenharmony_ci		pr_err("RNDIS command error %d, %d/%d\n",
44762306a36Sopenharmony_ci			status, req->actual, req->length);
44862306a36Sopenharmony_ci//	spin_unlock(&dev->lock);
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int
45262306a36Sopenharmony_cirndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(f);
45562306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
45662306a36Sopenharmony_ci	struct usb_request	*req = cdev->req;
45762306a36Sopenharmony_ci	int			value = -EOPNOTSUPP;
45862306a36Sopenharmony_ci	u16			w_index = le16_to_cpu(ctrl->wIndex);
45962306a36Sopenharmony_ci	u16			w_value = le16_to_cpu(ctrl->wValue);
46062306a36Sopenharmony_ci	u16			w_length = le16_to_cpu(ctrl->wLength);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* composite driver infrastructure handles everything except
46362306a36Sopenharmony_ci	 * CDC class messages; interface activation uses set_alt().
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* RNDIS uses the CDC command encapsulation mechanism to implement
46862306a36Sopenharmony_ci	 * an RPC scheme, with much getting/setting of attributes by OID.
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
47162306a36Sopenharmony_ci			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
47262306a36Sopenharmony_ci		if (w_value || w_index != rndis->ctrl_id)
47362306a36Sopenharmony_ci			goto invalid;
47462306a36Sopenharmony_ci		/* read the request; process it later */
47562306a36Sopenharmony_ci		value = w_length;
47662306a36Sopenharmony_ci		req->complete = rndis_command_complete;
47762306a36Sopenharmony_ci		req->context = rndis;
47862306a36Sopenharmony_ci		/* later, rndis_response_available() sends a notification */
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
48262306a36Sopenharmony_ci			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
48362306a36Sopenharmony_ci		if (w_value || w_index != rndis->ctrl_id)
48462306a36Sopenharmony_ci			goto invalid;
48562306a36Sopenharmony_ci		else {
48662306a36Sopenharmony_ci			u8 *buf;
48762306a36Sopenharmony_ci			u32 n;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci			/* return the result */
49062306a36Sopenharmony_ci			buf = rndis_get_next_response(rndis->params, &n);
49162306a36Sopenharmony_ci			if (buf) {
49262306a36Sopenharmony_ci				memcpy(req->buf, buf, n);
49362306a36Sopenharmony_ci				req->complete = rndis_response_complete;
49462306a36Sopenharmony_ci				req->context = rndis;
49562306a36Sopenharmony_ci				rndis_free_response(rndis->params, buf);
49662306a36Sopenharmony_ci				value = n;
49762306a36Sopenharmony_ci			}
49862306a36Sopenharmony_ci			/* else stalls ... spec says to avoid that */
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci		break;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	default:
50362306a36Sopenharmony_ciinvalid:
50462306a36Sopenharmony_ci		VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
50562306a36Sopenharmony_ci			ctrl->bRequestType, ctrl->bRequest,
50662306a36Sopenharmony_ci			w_value, w_index, w_length);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* respond with data transfer or status phase? */
51062306a36Sopenharmony_ci	if (value >= 0) {
51162306a36Sopenharmony_ci		DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
51262306a36Sopenharmony_ci			ctrl->bRequestType, ctrl->bRequest,
51362306a36Sopenharmony_ci			w_value, w_index, w_length);
51462306a36Sopenharmony_ci		req->zero = (value < w_length);
51562306a36Sopenharmony_ci		req->length = value;
51662306a36Sopenharmony_ci		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
51762306a36Sopenharmony_ci		if (value < 0)
51862306a36Sopenharmony_ci			ERROR(cdev, "rndis response on err %d\n", value);
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* device either stalls (value < 0) or reports success */
52262306a36Sopenharmony_ci	return value;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(f);
52962306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* we know alt == 0 */
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (intf == rndis->ctrl_id) {
53462306a36Sopenharmony_ci		VDBG(cdev, "reset rndis control %d\n", intf);
53562306a36Sopenharmony_ci		usb_ep_disable(rndis->notify);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		if (!rndis->notify->desc) {
53862306a36Sopenharmony_ci			VDBG(cdev, "init rndis ctrl %d\n", intf);
53962306a36Sopenharmony_ci			if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
54062306a36Sopenharmony_ci				goto fail;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci		usb_ep_enable(rndis->notify);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	} else if (intf == rndis->data_id) {
54562306a36Sopenharmony_ci		struct net_device	*net;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		if (rndis->port.in_ep->enabled) {
54862306a36Sopenharmony_ci			DBG(cdev, "reset rndis\n");
54962306a36Sopenharmony_ci			gether_disconnect(&rndis->port);
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
55362306a36Sopenharmony_ci			DBG(cdev, "init rndis\n");
55462306a36Sopenharmony_ci			if (config_ep_by_speed(cdev->gadget, f,
55562306a36Sopenharmony_ci					       rndis->port.in_ep) ||
55662306a36Sopenharmony_ci			    config_ep_by_speed(cdev->gadget, f,
55762306a36Sopenharmony_ci					       rndis->port.out_ep)) {
55862306a36Sopenharmony_ci				rndis->port.in_ep->desc = NULL;
55962306a36Sopenharmony_ci				rndis->port.out_ep->desc = NULL;
56062306a36Sopenharmony_ci				goto fail;
56162306a36Sopenharmony_ci			}
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		/* Avoid ZLPs; they can be troublesome. */
56562306a36Sopenharmony_ci		rndis->port.is_zlp_ok = false;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		/* RNDIS should be in the "RNDIS uninitialized" state,
56862306a36Sopenharmony_ci		 * either never activated or after rndis_uninit().
56962306a36Sopenharmony_ci		 *
57062306a36Sopenharmony_ci		 * We don't want data to flow here until a nonzero packet
57162306a36Sopenharmony_ci		 * filter is set, at which point it enters "RNDIS data
57262306a36Sopenharmony_ci		 * initialized" state ... but we do want the endpoints
57362306a36Sopenharmony_ci		 * to be activated.  It's a strange little state.
57462306a36Sopenharmony_ci		 *
57562306a36Sopenharmony_ci		 * REVISIT the RNDIS gadget code has done this wrong for a
57662306a36Sopenharmony_ci		 * very long time.  We need another call to the link layer
57762306a36Sopenharmony_ci		 * code -- gether_updown(...bool) maybe -- to do it right.
57862306a36Sopenharmony_ci		 */
57962306a36Sopenharmony_ci		rndis->port.cdc_filter = 0;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		DBG(cdev, "RNDIS RX/TX early activation ... \n");
58262306a36Sopenharmony_ci		net = gether_connect(&rndis->port);
58362306a36Sopenharmony_ci		if (IS_ERR(net))
58462306a36Sopenharmony_ci			return PTR_ERR(net);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		rndis_set_param_dev(rndis->params, net,
58762306a36Sopenharmony_ci				&rndis->port.cdc_filter);
58862306a36Sopenharmony_ci	} else
58962306a36Sopenharmony_ci		goto fail;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return 0;
59262306a36Sopenharmony_cifail:
59362306a36Sopenharmony_ci	return -EINVAL;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void rndis_disable(struct usb_function *f)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(f);
59962306a36Sopenharmony_ci	struct usb_composite_dev *cdev = f->config->cdev;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (!rndis->notify->enabled)
60262306a36Sopenharmony_ci		return;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	DBG(cdev, "rndis deactivated\n");
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	rndis_uninit(rndis->params);
60762306a36Sopenharmony_ci	gether_disconnect(&rndis->port);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	usb_ep_disable(rndis->notify);
61062306a36Sopenharmony_ci	rndis->notify->desc = NULL;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci/*
61662306a36Sopenharmony_ci * This isn't quite the same mechanism as CDC Ethernet, since the
61762306a36Sopenharmony_ci * notification scheme passes less data, but the same set of link
61862306a36Sopenharmony_ci * states must be tested.  A key difference is that altsettings are
61962306a36Sopenharmony_ci * not used to tell whether the link should send packets or not.
62062306a36Sopenharmony_ci */
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic void rndis_open(struct gether *geth)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(&geth->func);
62562306a36Sopenharmony_ci	struct usb_composite_dev *cdev = geth->func.config->cdev;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	DBG(cdev, "%s\n", __func__);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
63062306a36Sopenharmony_ci				gether_bitrate(cdev->gadget) / 100);
63162306a36Sopenharmony_ci	rndis_signal_connect(rndis->params);
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void rndis_close(struct gether *geth)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(&geth->func);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	DBG(geth->func.config->cdev, "%s\n", __func__);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
64162306a36Sopenharmony_ci	rndis_signal_disconnect(rndis->params);
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci/* Some controllers can't support RNDIS ... */
64762306a36Sopenharmony_cistatic inline bool can_support_rndis(struct usb_configuration *c)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	/* everything else is *presumably* fine */
65062306a36Sopenharmony_ci	return true;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci/* ethernet function driver setup/binding */
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int
65662306a36Sopenharmony_cirndis_bind(struct usb_configuration *c, struct usb_function *f)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct usb_composite_dev *cdev = c->cdev;
65962306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(f);
66062306a36Sopenharmony_ci	struct usb_string	*us;
66162306a36Sopenharmony_ci	int			status;
66262306a36Sopenharmony_ci	struct usb_ep		*ep;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	struct f_rndis_opts *rndis_opts;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (!can_support_rndis(c))
66762306a36Sopenharmony_ci		return -EINVAL;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (cdev->use_os_string) {
67262306a36Sopenharmony_ci		f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
67362306a36Sopenharmony_ci					   GFP_KERNEL);
67462306a36Sopenharmony_ci		if (!f->os_desc_table)
67562306a36Sopenharmony_ci			return -ENOMEM;
67662306a36Sopenharmony_ci		f->os_desc_n = 1;
67762306a36Sopenharmony_ci		f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
68162306a36Sopenharmony_ci	rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
68262306a36Sopenharmony_ci	rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/*
68562306a36Sopenharmony_ci	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
68662306a36Sopenharmony_ci	 * configurations are bound in sequence with list_for_each_entry,
68762306a36Sopenharmony_ci	 * in each configuration its functions are bound in sequence
68862306a36Sopenharmony_ci	 * with list_for_each_entry, so we assume no race condition
68962306a36Sopenharmony_ci	 * with regard to rndis_opts->bound access
69062306a36Sopenharmony_ci	 */
69162306a36Sopenharmony_ci	if (!rndis_opts->bound) {
69262306a36Sopenharmony_ci		gether_set_gadget(rndis_opts->net, cdev->gadget);
69362306a36Sopenharmony_ci		status = gether_register_netdev(rndis_opts->net);
69462306a36Sopenharmony_ci		if (status)
69562306a36Sopenharmony_ci			goto fail;
69662306a36Sopenharmony_ci		rndis_opts->bound = true;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	us = usb_gstrings_attach(cdev, rndis_strings,
70062306a36Sopenharmony_ci				 ARRAY_SIZE(rndis_string_defs));
70162306a36Sopenharmony_ci	if (IS_ERR(us)) {
70262306a36Sopenharmony_ci		status = PTR_ERR(us);
70362306a36Sopenharmony_ci		goto fail;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci	rndis_control_intf.iInterface = us[0].id;
70662306a36Sopenharmony_ci	rndis_data_intf.iInterface = us[1].id;
70762306a36Sopenharmony_ci	rndis_iad_descriptor.iFunction = us[2].id;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/* allocate instance-specific interface IDs */
71062306a36Sopenharmony_ci	status = usb_interface_id(c, f);
71162306a36Sopenharmony_ci	if (status < 0)
71262306a36Sopenharmony_ci		goto fail;
71362306a36Sopenharmony_ci	rndis->ctrl_id = status;
71462306a36Sopenharmony_ci	rndis_iad_descriptor.bFirstInterface = status;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	rndis_control_intf.bInterfaceNumber = status;
71762306a36Sopenharmony_ci	rndis_union_desc.bMasterInterface0 = status;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (cdev->use_os_string)
72062306a36Sopenharmony_ci		f->os_desc_table[0].if_id =
72162306a36Sopenharmony_ci			rndis_iad_descriptor.bFirstInterface;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	status = usb_interface_id(c, f);
72462306a36Sopenharmony_ci	if (status < 0)
72562306a36Sopenharmony_ci		goto fail;
72662306a36Sopenharmony_ci	rndis->data_id = status;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	rndis_data_intf.bInterfaceNumber = status;
72962306a36Sopenharmony_ci	rndis_union_desc.bSlaveInterface0 = status;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	status = -ENODEV;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* allocate instance-specific endpoints */
73462306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
73562306a36Sopenharmony_ci	if (!ep)
73662306a36Sopenharmony_ci		goto fail;
73762306a36Sopenharmony_ci	rndis->port.in_ep = ep;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
74062306a36Sopenharmony_ci	if (!ep)
74162306a36Sopenharmony_ci		goto fail;
74262306a36Sopenharmony_ci	rndis->port.out_ep = ep;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* NOTE:  a status/notification endpoint is, strictly speaking,
74562306a36Sopenharmony_ci	 * optional.  We don't treat it that way though!  It's simpler,
74662306a36Sopenharmony_ci	 * and some newer profiles don't treat it as optional.
74762306a36Sopenharmony_ci	 */
74862306a36Sopenharmony_ci	ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
74962306a36Sopenharmony_ci	if (!ep)
75062306a36Sopenharmony_ci		goto fail;
75162306a36Sopenharmony_ci	rndis->notify = ep;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	status = -ENOMEM;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* allocate notification request and buffer */
75662306a36Sopenharmony_ci	rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
75762306a36Sopenharmony_ci	if (!rndis->notify_req)
75862306a36Sopenharmony_ci		goto fail;
75962306a36Sopenharmony_ci	rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
76062306a36Sopenharmony_ci	if (!rndis->notify_req->buf)
76162306a36Sopenharmony_ci		goto fail;
76262306a36Sopenharmony_ci	rndis->notify_req->length = STATUS_BYTECOUNT;
76362306a36Sopenharmony_ci	rndis->notify_req->context = rndis;
76462306a36Sopenharmony_ci	rndis->notify_req->complete = rndis_response_complete;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* support all relevant hardware speeds... we expect that when
76762306a36Sopenharmony_ci	 * hardware is dual speed, all bulk-capable endpoints work at
76862306a36Sopenharmony_ci	 * both speeds
76962306a36Sopenharmony_ci	 */
77062306a36Sopenharmony_ci	hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
77162306a36Sopenharmony_ci	hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
77262306a36Sopenharmony_ci	hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
77562306a36Sopenharmony_ci	ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
77662306a36Sopenharmony_ci	ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
77962306a36Sopenharmony_ci			eth_ss_function, eth_ss_function);
78062306a36Sopenharmony_ci	if (status)
78162306a36Sopenharmony_ci		goto fail;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	rndis->port.open = rndis_open;
78462306a36Sopenharmony_ci	rndis->port.close = rndis_close;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
78762306a36Sopenharmony_ci	rndis_set_host_mac(rndis->params, rndis->ethaddr);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (rndis->manufacturer && rndis->vendorID &&
79062306a36Sopenharmony_ci			rndis_set_param_vendor(rndis->params, rndis->vendorID,
79162306a36Sopenharmony_ci					       rndis->manufacturer)) {
79262306a36Sopenharmony_ci		status = -EINVAL;
79362306a36Sopenharmony_ci		goto fail_free_descs;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* NOTE:  all that is done without knowing or caring about
79762306a36Sopenharmony_ci	 * the network link ... which is unavailable to this code
79862306a36Sopenharmony_ci	 * until we're activated via set_alt().
79962306a36Sopenharmony_ci	 */
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	DBG(cdev, "RNDIS: IN/%s OUT/%s NOTIFY/%s\n",
80262306a36Sopenharmony_ci			rndis->port.in_ep->name, rndis->port.out_ep->name,
80362306a36Sopenharmony_ci			rndis->notify->name);
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cifail_free_descs:
80762306a36Sopenharmony_ci	usb_free_all_descriptors(f);
80862306a36Sopenharmony_cifail:
80962306a36Sopenharmony_ci	kfree(f->os_desc_table);
81062306a36Sopenharmony_ci	f->os_desc_n = 0;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (rndis->notify_req) {
81362306a36Sopenharmony_ci		kfree(rndis->notify_req->buf);
81462306a36Sopenharmony_ci		usb_ep_free_request(rndis->notify, rndis->notify_req);
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return status;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_civoid rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct f_rndis_opts *opts;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	opts = container_of(f, struct f_rndis_opts, func_inst);
82762306a36Sopenharmony_ci	if (opts->bound)
82862306a36Sopenharmony_ci		gether_cleanup(netdev_priv(opts->net));
82962306a36Sopenharmony_ci	else
83062306a36Sopenharmony_ci		free_netdev(opts->net);
83162306a36Sopenharmony_ci	opts->borrowed_net = opts->bound = true;
83262306a36Sopenharmony_ci	opts->net = net;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_borrow_net);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_rndis_opts,
83962306a36Sopenharmony_ci			    func_inst.group);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/* f_rndis_item_ops */
84362306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(rndis);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/* f_rndis_opts_dev_addr */
84662306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/* f_rndis_opts_host_addr */
84962306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci/* f_rndis_opts_qmult */
85262306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci/* f_rndis_opts_ifname */
85562306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci/* f_rndis_opts_class */
85862306a36Sopenharmony_ciUSB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, class);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci/* f_rndis_opts_subclass */
86162306a36Sopenharmony_ciUSB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, subclass);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci/* f_rndis_opts_protocol */
86462306a36Sopenharmony_ciUSB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, protocol);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic struct configfs_attribute *rndis_attrs[] = {
86762306a36Sopenharmony_ci	&rndis_opts_attr_dev_addr,
86862306a36Sopenharmony_ci	&rndis_opts_attr_host_addr,
86962306a36Sopenharmony_ci	&rndis_opts_attr_qmult,
87062306a36Sopenharmony_ci	&rndis_opts_attr_ifname,
87162306a36Sopenharmony_ci	&rndis_opts_attr_class,
87262306a36Sopenharmony_ci	&rndis_opts_attr_subclass,
87362306a36Sopenharmony_ci	&rndis_opts_attr_protocol,
87462306a36Sopenharmony_ci	NULL,
87562306a36Sopenharmony_ci};
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic const struct config_item_type rndis_func_type = {
87862306a36Sopenharmony_ci	.ct_item_ops	= &rndis_item_ops,
87962306a36Sopenharmony_ci	.ct_attrs	= rndis_attrs,
88062306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
88162306a36Sopenharmony_ci};
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic void rndis_free_inst(struct usb_function_instance *f)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct f_rndis_opts *opts;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	opts = container_of(f, struct f_rndis_opts, func_inst);
88862306a36Sopenharmony_ci	if (!opts->borrowed_net) {
88962306a36Sopenharmony_ci		if (opts->bound)
89062306a36Sopenharmony_ci			gether_cleanup(netdev_priv(opts->net));
89162306a36Sopenharmony_ci		else
89262306a36Sopenharmony_ci			free_netdev(opts->net);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	kfree(opts->rndis_interf_group);	/* single VLA chunk */
89662306a36Sopenharmony_ci	kfree(opts);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic struct usb_function_instance *rndis_alloc_inst(void)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct f_rndis_opts *opts;
90262306a36Sopenharmony_ci	struct usb_os_desc *descs[1];
90362306a36Sopenharmony_ci	char *names[1];
90462306a36Sopenharmony_ci	struct config_group *rndis_interf_group;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
90762306a36Sopenharmony_ci	if (!opts)
90862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
90962306a36Sopenharmony_ci	opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	mutex_init(&opts->lock);
91262306a36Sopenharmony_ci	opts->func_inst.free_func_inst = rndis_free_inst;
91362306a36Sopenharmony_ci	opts->net = gether_setup_default();
91462306a36Sopenharmony_ci	if (IS_ERR(opts->net)) {
91562306a36Sopenharmony_ci		struct net_device *net = opts->net;
91662306a36Sopenharmony_ci		kfree(opts);
91762306a36Sopenharmony_ci		return ERR_CAST(net);
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci	INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	opts->class = rndis_iad_descriptor.bFunctionClass;
92262306a36Sopenharmony_ci	opts->subclass = rndis_iad_descriptor.bFunctionSubClass;
92362306a36Sopenharmony_ci	opts->protocol = rndis_iad_descriptor.bFunctionProtocol;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	descs[0] = &opts->rndis_os_desc;
92662306a36Sopenharmony_ci	names[0] = "rndis";
92762306a36Sopenharmony_ci	config_group_init_type_name(&opts->func_inst.group, "",
92862306a36Sopenharmony_ci				    &rndis_func_type);
92962306a36Sopenharmony_ci	rndis_interf_group =
93062306a36Sopenharmony_ci		usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
93162306a36Sopenharmony_ci					       names, THIS_MODULE);
93262306a36Sopenharmony_ci	if (IS_ERR(rndis_interf_group)) {
93362306a36Sopenharmony_ci		rndis_free_inst(&opts->func_inst);
93462306a36Sopenharmony_ci		return ERR_CAST(rndis_interf_group);
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci	opts->rndis_interf_group = rndis_interf_group;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return &opts->func_inst;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic void rndis_free(struct usb_function *f)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct f_rndis *rndis;
94462306a36Sopenharmony_ci	struct f_rndis_opts *opts;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	rndis = func_to_rndis(f);
94762306a36Sopenharmony_ci	rndis_deregister(rndis->params);
94862306a36Sopenharmony_ci	opts = container_of(f->fi, struct f_rndis_opts, func_inst);
94962306a36Sopenharmony_ci	kfree(rndis);
95062306a36Sopenharmony_ci	mutex_lock(&opts->lock);
95162306a36Sopenharmony_ci	opts->refcnt--;
95262306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	struct f_rndis		*rndis = func_to_rndis(f);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	kfree(f->os_desc_table);
96062306a36Sopenharmony_ci	f->os_desc_n = 0;
96162306a36Sopenharmony_ci	usb_free_all_descriptors(f);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	kfree(rndis->notify_req->buf);
96462306a36Sopenharmony_ci	usb_ep_free_request(rndis->notify, rndis->notify_req);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic struct usb_function *rndis_alloc(struct usb_function_instance *fi)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	struct f_rndis	*rndis;
97062306a36Sopenharmony_ci	struct f_rndis_opts *opts;
97162306a36Sopenharmony_ci	struct rndis_params *params;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/* allocate and initialize one new instance */
97462306a36Sopenharmony_ci	rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
97562306a36Sopenharmony_ci	if (!rndis)
97662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	opts = container_of(fi, struct f_rndis_opts, func_inst);
97962306a36Sopenharmony_ci	mutex_lock(&opts->lock);
98062306a36Sopenharmony_ci	opts->refcnt++;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	gether_get_host_addr_u8(opts->net, rndis->ethaddr);
98362306a36Sopenharmony_ci	rndis->vendorID = opts->vendor_id;
98462306a36Sopenharmony_ci	rndis->manufacturer = opts->manufacturer;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	rndis->port.ioport = netdev_priv(opts->net);
98762306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
98862306a36Sopenharmony_ci	/* RNDIS activates when the host changes this filter */
98962306a36Sopenharmony_ci	rndis->port.cdc_filter = 0;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* RNDIS has special (and complex) framing */
99262306a36Sopenharmony_ci	rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
99362306a36Sopenharmony_ci	rndis->port.wrap = rndis_add_header;
99462306a36Sopenharmony_ci	rndis->port.unwrap = rndis_rm_hdr;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	rndis->port.func.name = "rndis";
99762306a36Sopenharmony_ci	/* descriptors are per-instance copies */
99862306a36Sopenharmony_ci	rndis->port.func.bind = rndis_bind;
99962306a36Sopenharmony_ci	rndis->port.func.unbind = rndis_unbind;
100062306a36Sopenharmony_ci	rndis->port.func.set_alt = rndis_set_alt;
100162306a36Sopenharmony_ci	rndis->port.func.setup = rndis_setup;
100262306a36Sopenharmony_ci	rndis->port.func.disable = rndis_disable;
100362306a36Sopenharmony_ci	rndis->port.func.free_func = rndis_free;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	params = rndis_register(rndis_response_available, rndis);
100662306a36Sopenharmony_ci	if (IS_ERR(params)) {
100762306a36Sopenharmony_ci		kfree(rndis);
100862306a36Sopenharmony_ci		return ERR_CAST(params);
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci	rndis->params = params;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return &rndis->port.func;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
101662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
101762306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell");
1018