rndis_host.c

来自「linux 内核源代码」· C语言 代码 · 共 737 行 · 第 1/2 页

C
737
字号
/* * Host Side support for RNDIS Networking Links * Copyright (C) 2005 by David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */// #define	DEBUG			// error path messages, extra info// #define	VERBOSE			// more; success messages#include <linux/module.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/workqueue.h>#include <linux/mii.h>#include <linux/usb.h>#include <linux/usb/cdc.h>#include "usbnet.h"/* * RNDIS is NDIS remoted over USB.  It's a MSFT variant of CDC ACM ... of * course ACM was intended for modems, not Ethernet links!  USB's standard * for Ethernet links is "CDC Ethernet", which is significantly simpler. * * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete.  Issues * include: *    - Power management in particular relies on information that's scattered *	through other documentation, and which is incomplete or incorrect even *	there. *    - There are various undocumented protocol requirements, such as the *	need to send unused garbage in control-OUT messages. *    - In some cases, MS-Windows will emit undocumented requests; this *	matters more to peripheral implementations than host ones. * * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". * * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and * currently rare) "Ethernet Emulation Model" (EEM). *//* * CONTROL uses CDC "encapsulated commands" with funky notifications. *  - control-out:  SEND_ENCAPSULATED *  - interrupt-in:  RESPONSE_AVAILABLE *  - control-in:  GET_ENCAPSULATED * * We'll try to ignore the RESPONSE_AVAILABLE notifications. * * REVISIT some RNDIS implementations seem to have curious issues still * to be resolved. */struct rndis_msg_hdr {	__le32	msg_type;			/* RNDIS_MSG_* */	__le32	msg_len;	// followed by data that varies between messages	__le32	request_id;	__le32	status;	// ... and more} __attribute__ ((packed));/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */#define	CONTROL_BUFFER_SIZE		1025/* RNDIS defines an (absurdly huge) 10 second control timeout, * but ActiveSync seems to use a more usual 5 second timeout * (which matches the USB 2.0 spec). */#define	RNDIS_CONTROL_TIMEOUT_MS	(5 * 1000)#define ccpu2 __constant_cpu_to_le32#define RNDIS_MSG_COMPLETION	ccpu2(0x80000000)/* codes for "msg_type" field of rndis messages; * only the data channel uses packet messages (maybe batched); * everything else goes on the control channel. */#define RNDIS_MSG_PACKET	ccpu2(0x00000001)	/* 1-N packets */#define RNDIS_MSG_INIT		ccpu2(0x00000002)#define RNDIS_MSG_INIT_C	(RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION)#define RNDIS_MSG_HALT		ccpu2(0x00000003)#define RNDIS_MSG_QUERY		ccpu2(0x00000004)#define RNDIS_MSG_QUERY_C	(RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION)#define RNDIS_MSG_SET		ccpu2(0x00000005)#define RNDIS_MSG_SET_C		(RNDIS_MSG_SET|RNDIS_MSG_COMPLETION)#define RNDIS_MSG_RESET		ccpu2(0x00000006)#define RNDIS_MSG_RESET_C	(RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION)#define RNDIS_MSG_INDICATE	ccpu2(0x00000007)#define RNDIS_MSG_KEEPALIVE	ccpu2(0x00000008)#define RNDIS_MSG_KEEPALIVE_C	(RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)/* codes for "status" field of completion messages */#define	RNDIS_STATUS_SUCCESS		ccpu2(0x00000000)#define	RNDIS_STATUS_FAILURE		ccpu2(0xc0000001)#define	RNDIS_STATUS_INVALID_DATA	ccpu2(0xc0010015)#define	RNDIS_STATUS_NOT_SUPPORTED	ccpu2(0xc00000bb)#define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)#define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)struct rndis_data_hdr {	__le32	msg_type;		/* RNDIS_MSG_PACKET */	__le32	msg_len;		// rndis_data_hdr + data_len + pad	__le32	data_offset;		// 36 -- right after header	__le32	data_len;		// ... real packet size	__le32	oob_data_offset;	// zero	__le32	oob_data_len;		// zero	__le32	num_oob;		// zero	__le32	packet_data_offset;	// zero	__le32	packet_data_len;	// zero	__le32	vc_handle;		// zero	__le32	reserved;		// zero} __attribute__ ((packed));struct rndis_init {		/* OUT */	// header and:	__le32	msg_type;			/* RNDIS_MSG_INIT */	__le32	msg_len;			// 24	__le32	request_id;	__le32	major_version;			// of rndis (1.0)	__le32	minor_version;	__le32	max_transfer_size;} __attribute__ ((packed));struct rndis_init_c {		/* IN */	// header and:	__le32	msg_type;			/* RNDIS_MSG_INIT_C */	__le32	msg_len;	__le32	request_id;	__le32	status;	__le32	major_version;			// of rndis (1.0)	__le32	minor_version;	__le32	device_flags;	__le32	medium;				// zero == 802.3	__le32	max_packets_per_message;	__le32	max_transfer_size;	__le32	packet_alignment;		// max 7; (1<<n) bytes	__le32	af_list_offset;			// zero	__le32	af_list_size;			// zero} __attribute__ ((packed));struct rndis_halt {		/* OUT (no reply) */	// header and:	__le32	msg_type;			/* RNDIS_MSG_HALT */	__le32	msg_len;	__le32	request_id;} __attribute__ ((packed));struct rndis_query {		/* OUT */	// header and:	__le32	msg_type;			/* RNDIS_MSG_QUERY */	__le32	msg_len;	__le32	request_id;	__le32	oid;	__le32	len;	__le32	offset;/*?*/	__le32	handle;				// zero} __attribute__ ((packed));struct rndis_query_c {		/* IN */	// header and:	__le32	msg_type;			/* RNDIS_MSG_QUERY_C */	__le32	msg_len;	__le32	request_id;	__le32	status;	__le32	len;	__le32	offset;} __attribute__ ((packed));struct rndis_set {		/* OUT */	// header and:	__le32	msg_type;			/* RNDIS_MSG_SET */	__le32	msg_len;	__le32	request_id;	__le32	oid;	__le32	len;	__le32	offset;/*?*/	__le32	handle;				// zero} __attribute__ ((packed));struct rndis_set_c {		/* IN */	// header and:	__le32	msg_type;			/* RNDIS_MSG_SET_C */	__le32	msg_len;	__le32	request_id;	__le32	status;} __attribute__ ((packed));struct rndis_reset {		/* IN */	// header and:	__le32	msg_type;			/* RNDIS_MSG_RESET */	__le32	msg_len;	__le32	reserved;} __attribute__ ((packed));struct rndis_reset_c {		/* OUT */	// header and:	__le32	msg_type;			/* RNDIS_MSG_RESET_C */	__le32	msg_len;	__le32	status;	__le32	addressing_lost;} __attribute__ ((packed));struct rndis_indicate {		/* IN (unrequested) */	// header and:	__le32	msg_type;			/* RNDIS_MSG_INDICATE */	__le32	msg_len;	__le32	status;	__le32	length;	__le32	offset;/**/	__le32	diag_status;	__le32	error_offset;/**/	__le32	message;} __attribute__ ((packed));struct rndis_keepalive {	/* OUT (optionally IN) */	// header and:	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE */	__le32	msg_len;	__le32	request_id;} __attribute__ ((packed));struct rndis_keepalive_c {	/* IN (optionally OUT) */	// header and:	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE_C */	__le32	msg_len;	__le32	request_id;	__le32	status;} __attribute__ ((packed));/* NOTE:  about 30 OIDs are "mandatory" for peripherals to support ... and * there are gobs more that may optionally be supported.  We'll avoid as much * of that mess as possible. */#define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)#define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)#define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)/* * RNDIS notifications from device: command completion; "reverse" * keepalives; etc */static void rndis_status(struct usbnet *dev, struct urb *urb){	devdbg(dev, "rndis status urb, len %d stat %d",		urb->actual_length, urb->status);	// FIXME for keepalives, respond immediately (asynchronously)	// if not an RNDIS status, do like cdc_status(dev,urb) does}/* * RPC done RNDIS-style.  Caller guarantees: * - message is properly byteswapped * - there's no other request pending * - buf can hold up to 1KB response (required by RNDIS spec) * On return, the first few entries are already byteswapped. * * Call context is likely probe(), before interface name is known, * which is why we won't try to use it in the diagnostics. */static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf){	struct cdc_state	*info = (void *) &dev->data;	int			master_ifnum;	int			retval;	unsigned		count;	__le32			rsp;	u32			xid = 0, msg_len, request_id;	/* REVISIT when this gets called from contexts other than probe() or	 * disconnect(): either serialize, or dispatch responses on xid	 */	/* Issue the request; xid is unique, don't bother byteswapping it */	if (likely(buf->msg_type != RNDIS_MSG_HALT			&& buf->msg_type != RNDIS_MSG_RESET)) {		xid = dev->xid++;		if (!xid)			xid = dev->xid++;		buf->request_id = (__force __le32) xid;	}	master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber;	retval = usb_control_msg(dev->udev,		usb_sndctrlpipe(dev->udev, 0),		USB_CDC_SEND_ENCAPSULATED_COMMAND,		USB_TYPE_CLASS | USB_RECIP_INTERFACE,		0, master_ifnum,		buf, le32_to_cpu(buf->msg_len),		RNDIS_CONTROL_TIMEOUT_MS);	if (unlikely(retval < 0 || xid == 0))		return retval;	// FIXME Seems like some devices discard responses when	// we time out and cancel our "get response" requests...	// so, this is fragile.  Probably need to poll for status.	/* ignore status endpoint, just poll the control channel;	 * the request probably completed immediately	 */	rsp = buf->msg_type | RNDIS_MSG_COMPLETION;	for (count = 0; count < 10; count++) {		memset(buf, 0, CONTROL_BUFFER_SIZE);		retval = usb_control_msg(dev->udev,			usb_rcvctrlpipe(dev->udev, 0),			USB_CDC_GET_ENCAPSULATED_RESPONSE,			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,			0, master_ifnum,			buf, CONTROL_BUFFER_SIZE,			RNDIS_CONTROL_TIMEOUT_MS);		if (likely(retval >= 8)) {			msg_len = le32_to_cpu(buf->msg_len);			request_id = (__force u32) buf->request_id;			if (likely(buf->msg_type == rsp)) {				if (likely(request_id == xid)) {					if (unlikely(rsp == RNDIS_MSG_RESET_C))						return 0;					if (likely(RNDIS_STATUS_SUCCESS							== buf->status))						return 0;					dev_dbg(&info->control->dev,						"rndis reply status %08x\n",						le32_to_cpu(buf->status));					return -EL3RST;				}				dev_dbg(&info->control->dev,					"rndis reply id %d expected %d\n",					request_id, xid);				/* then likely retry */			} else switch (buf->msg_type) {			case RNDIS_MSG_INDICATE: {	/* fault */				// struct rndis_indicate *msg = (void *)buf;				dev_info(&info->control->dev,					"rndis fault indication\n");				}				break;			case RNDIS_MSG_KEEPALIVE: {	/* ping */				struct rndis_keepalive_c *msg = (void *)buf;				msg->msg_type = RNDIS_MSG_KEEPALIVE_C;				msg->msg_len = ccpu2(sizeof *msg);				msg->status = RNDIS_STATUS_SUCCESS;				retval = usb_control_msg(dev->udev,					usb_sndctrlpipe(dev->udev, 0),					USB_CDC_SEND_ENCAPSULATED_COMMAND,					USB_TYPE_CLASS | USB_RECIP_INTERFACE,					0, master_ifnum,					msg, sizeof *msg,					RNDIS_CONTROL_TIMEOUT_MS);				if (unlikely(retval < 0))

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?