ChangeSet 1.1673.8.47, 2004/03/30 08:56:15-08:00, david-b@pacbell.net [PATCH] USB Gadget: RNDIS/Ethernet Gadget Driver (1/2) This patch adds the RNDIS message engine and kbuild/kconfig support for it. This is currently labeled EXPERIMENTAL. Patch contributed by Robert Schwebel, and developed with support from Auerswald GmbH. drivers/usb/gadget/Kconfig | 17 drivers/usb/gadget/Makefile | 4 drivers/usb/gadget/ndis.h | 187 ++++++ drivers/usb/gadget/rndis.c | 1362 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/rndis.h | 313 ++++++++++ 5 files changed, 1882 insertions(+), 1 deletion(-) diff -Nru a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig --- a/drivers/usb/gadget/Kconfig Wed Apr 14 14:36:13 2004 +++ b/drivers/usb/gadget/Kconfig Wed Apr 14 14:36:13 2004 @@ -219,9 +219,11 @@ favor of simpler vendor-specific hardware, but is widely supported by firmware for smart network devices. - - On hardware can't implement that protocol, a simpler approach + - On hardware can't implement that protocol, a simple CDC subset is used, placing fewer demands on USB. + RNDIS support is a third option, more demanding than that subset. + Within the USB device, this gadget driver exposes a network device "usbX", where X depends on what other networking devices you have. Treat it like a two-node Ethernet link: host, and gadget. @@ -234,6 +236,19 @@ Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_ether". + +config USB_ETH_RNDIS + bool "RNDIS support (EXPERIMENTAL)" + depends on USB_ETH && EXPERIMENTAL + default y + help + Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, + and Microsoft provides redistributable binary RNDIS drivers for + older versions of Windows. + + If you say "y" here, the Ethernet gadget driver will try to provide + a second device configuration, supporting RNDIS to talk to such + Microsoft USB hosts. config USB_GADGETFS tristate "Gadget Filesystem (EXPERIMENTAL)" diff -Nru a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile --- a/drivers/usb/gadget/Makefile Wed Apr 14 14:36:13 2004 +++ b/drivers/usb/gadget/Makefile Wed Apr 14 14:36:13 2004 @@ -14,6 +14,10 @@ g_serial-objs := serial.o usbstring.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o usbstring.o + +ifeq ($(CONFIG_USB_ETH_RNDIS),y) + g_ether-objs += rndis.o +endif obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_ETH) += g_ether.o diff -Nru a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/gadget/ndis.h Wed Apr 14 14:36:13 2004 @@ -0,0 +1,187 @@ +/* + * ndis.h + * + * ntddndis.h modified by Benedikt Spranger + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_NDIS_H +#define _LINUX_NDIS_H + + +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B + +/* NDIS_PNP_CAPABILITIES.Flags constants */ +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 + +/* Required Object IDs (OIDs) */ +#define OID_GEN_SUPPORTED_LIST 0x00010101 +#define OID_GEN_HARDWARE_STATUS 0x00010102 +#define OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define OID_GEN_MEDIA_IN_USE 0x00010104 +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define OID_GEN_LINK_SPEED 0x00010107 +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define OID_GEN_VENDOR_ID 0x0001010C +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define OID_GEN_DRIVER_VERSION 0x00010110 +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define OID_GEN_MAC_OPTIONS 0x00010113 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define OID_GEN_MACHINE_NAME 0x0001021A +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define OID_GEN_VLAN_ID 0x0001021C + +/* Optional OIDs */ +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Required statistics OIDs */ +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Optional statistics OIDs */ +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define OID_GEN_GET_TIME_CAPS 0x0002020F +#define OID_GEN_GET_NETCARD_TIME 0x00020210 +#define OID_GEN_NETCARD_LOAD 0x00020211 +#define OID_GEN_DEVICE_PROFILE 0x00020212 +#define OID_GEN_INIT_TIME_MS 0x00020213 +#define OID_GEN_RESET_COUNTS 0x00020214 +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define OID_GEN_FRIENDLY_NAME 0x00020216 +#define OID_GEN_MINIPORT_INFO 0x00020217 +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* IEEE 802.3 (Ethernet) OIDs */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#define OID_802_3_MULTICAST_LIST 0x01010103 +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define OID_802_3_MAC_OPTIONS 0x01010105 +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* OID_GEN_MINIPORT_INFO constants */ +#define NDIS_MINIPORT_BUS_MASTER 0x00000001 +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 +#define NDIS_MINIPORT_SG_LIST 0x00000004 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 +#define NDIS_MINIPORT_IS_CO 0x00000400 +#define NDIS_MINIPORT_DESERIALIZE 0x00000800 +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 +#define NDIS_MINIPORT_HIDDEN 0x00040000 +#define NDIS_MINIPORT_SWENUM 0x00080000 +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 +#define NDIS_MINIPORT_64BITS_DMA 0x01000000 + +#define NDIS_MEDIUM_802_3 0x00000000 +#define NDIS_MEDIUM_802_5 0x00000001 +#define NDIS_MEDIUM_FDDI 0x00000002 +#define NDIS_MEDIUM_WAN 0x00000003 +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define NDIS_MEDIUM_DIX 0x00000005 +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 +#define NDIS_MEDIUM_ATM 0x00000008 +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 +#define NDIS_MEDIUM_IRDA 0x0000000A +#define NDIS_MEDIUM_BPC 0x0000000B +#define NDIS_MEDIUM_CO_WAN 0x0000000C +#define NDIS_MEDIUM_1394 0x0000000D + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 +#define NDIS_MAC_OPTION_RESERVED 0x80000000 + +#endif /* _LINUX_NDIS_H */ diff -Nru a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/gadget/rndis.c Wed Apr 14 14:36:13 2004 @@ -0,0 +1,1362 @@ +/* + * RNDIS MSG parser + * + * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ + * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This software was originally developed in conformance with + * Microsoft's Remote NDIS Specification License Agreement. + * + * 03/12/2004 Kai-Uwe Bloem + * Fixed message length bug in init_response + * + * 03/25/2004 Kai-Uwe Bloem + * Fixed rndis_rm_hdr length bug. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rndis.h" + + +#define DEBUG if (rndis_debug) printk + +#define RNDIS_MAX_CONFIGS 1 + +static struct proc_dir_entry *rndis_connect_dir; +static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; + +static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; +static int rndis_debug = 0; + +MODULE_PARM (rndis_debug, "i"); +MODULE_PARM_DESC (rndis_debug, "enable debugging"); + +/* Driver Version */ +static const u32 rndis_driver_version = __constant_cpu_to_le32 (1); + +/* Function Prototypes */ +static int rndis_init_response (int configNr, rndis_init_msg_type *buf); +static int rndis_query_response (int configNr, rndis_query_msg_type *buf); +static int rndis_set_response (int configNr, rndis_set_msg_type *buf); +static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf); +static int rndis_keepalive_response (int configNr, + rndis_keepalive_msg_type *buf); + +static rndis_resp_t *rndis_add_response (int configNr, u32 length); + +/* helper functions */ +static u32 devFlags2currentFilter (struct net_device *dev) +{ + u32 filter = 0; + + if (!dev) return 0; + + if (dev->flags & IFF_MULTICAST) + filter |= NDIS_PACKET_TYPE_MULTICAST; + if (dev->flags & IFF_BROADCAST) + filter |= NDIS_PACKET_TYPE_BROADCAST; + if (dev->flags & IFF_ALLMULTI) + filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; + if (dev->flags & IFF_PROMISC) + filter |= NDIS_PACKET_TYPE_PROMISCUOUS; + + return filter; +} + +static void currentFilter2devFlags (u32 currentFilter, struct net_device *dev) +{ + if (!dev) return; + + if (currentFilter & NDIS_PACKET_TYPE_MULTICAST) + dev->flags |= IFF_MULTICAST; + if (currentFilter & NDIS_PACKET_TYPE_BROADCAST) + dev->flags |= IFF_BROADCAST; + if (currentFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) + dev->flags |= IFF_ALLMULTI; + if (currentFilter & NDIS_PACKET_TYPE_PROMISCUOUS) + dev->flags |= IFF_PROMISC; +} + + +/* NDIS Functions */ +static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) +{ + int retval = -ENOTSUPP; + u32 length = 0; + rndis_query_cmplt_type *resp; + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + if (!resp) return -ENOMEM; + + switch (OID) { + /* mandatory */ + case OID_GEN_SUPPORTED_LIST: + DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); + length = sizeof (oid_supported_list); + memcpy ((u8 *) resp + 24, oid_supported_list, length); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_HARDWARE_STATUS: + DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); + length = 4; + /* Bogus question! + * Hardware must be ready to recieve high level protocols. + * BTW: + * reddite ergo quae sunt Caesaris Caesari + * et quae sunt Dei Deo! + */ + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_SUPPORTED: + DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr].medium; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_IN_USE: + DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); + length = 4; + /* one medium, one transport... (maybe you do it better) */ + *((u32 *) resp + 6) = rndis_per_dev_params [configNr].medium; + retval = 0; + break; + + case OID_GEN_MAXIMUM_LOOKAHEAD: + DEBUG("%s: OID_GEN_MAXIMUM_LOOKAHEAD\n", __FUNCTION__); + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_FRAME_SIZE: + DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr] + .dev->mtu; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_LINK_SPEED: + DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); + length = 4; + if (rndis_per_dev_params [configNr].media_state) + *((u32 *) resp + 6) = 0; + else + *((u32 *) resp + 6) = rndis_per_dev_params [configNr].speed; + retval = 0; + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + DEBUG("%s: OID_GEN_TRANSMIT_BUFFER_SPACE\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + DEBUG("%s: OID_GEN_RECEIVE_BUFFER_SPACE\n", __FUNCTION__); + break; + + /* mandatory */ + case OID_GEN_TRANSMIT_BLOCK_SIZE: + DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr] + .dev->mtu; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RECEIVE_BLOCK_SIZE: + DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr] + .dev->mtu; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_VENDOR_ID: + DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr].vendorID; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_VENDOR_DESCRIPTION: + DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); + length = strlen (rndis_per_dev_params [configNr].vendorDescr); + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].vendorDescr, length); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_CURRENT_PACKET_FILTER: + DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = devFlags2currentFilter ( + rndis_per_dev_params [configNr].dev); + retval = 0; + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + DEBUG("%s: OID_GEN_CURRENT_LOOKAHEAD\n", __FUNCTION__); + break; + + case OID_GEN_DRIVER_VERSION: + DEBUG("%s: OID_GEN_DRIVER_VERSION\n", __FUNCTION__); + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_TOTAL_SIZE: + DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = RNDIS_MAX_TOTAL_SIZE; + retval = 0; + break; + + case OID_GEN_PROTOCOL_OPTIONS: + DEBUG("%s: OID_GEN_PROTOCOL_OPTIONS\n", __FUNCTION__); + break; + + case OID_GEN_MAC_OPTIONS: + DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = NDIS_MAC_OPTION_RECEIVE_SERIALIZED | + NDIS_MAC_OPTION_FULL_DUPLEX; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_CONNECT_STATUS: + DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr] + .media_state; + retval = 0; + break; + + case OID_GEN_MAXIMUM_SEND_PACKETS: + DEBUG("%s: OID_GEN_MAXIMUM_SEND_PACKETS\n", __FUNCTION__); + break; + + /* mandatory */ + case OID_GEN_VENDOR_DRIVER_VERSION: + DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = rndis_driver_version; + retval = 0; + break; + + case OID_GEN_SUPPORTED_GUIDS: + DEBUG("%s: OID_GEN_SUPPORTED_GUIDS\n", __FUNCTION__); + break; + + case OID_GEN_NETWORK_LAYER_ADDRESSES: + DEBUG("%s: OID_GEN_NETWORK_LAYER_ADDRESSES\n", __FUNCTION__); + break; + + case OID_GEN_TRANSPORT_HEADER_OFFSET: + DEBUG("%s: OID_GEN_TRANSPORT_HEADER_OFFSET\n", __FUNCTION__); + break; + + case OID_GEN_MACHINE_NAME: + DEBUG("%s: OID_GEN_MACHINE_NAME\n", __FUNCTION__); + break; + + case OID_GEN_RNDIS_CONFIG_PARAMETER: + DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + case OID_GEN_VLAN_ID: + DEBUG("%s: OID_GEN_VLAN_ID\n", __FUNCTION__); + break; + + case OID_GEN_MEDIA_CAPABILITIES: + DEBUG("%s: OID_GEN_MEDIA_CAPABILITIES\n", __FUNCTION__); + break; + + case OID_GEN_PHYSICAL_MEDIUM: + DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_XMIT_OK: + DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr] + .stats->tx_packets - + rndis_per_dev_params [configNr].stats->tx_errors - + rndis_per_dev_params [configNr].stats->tx_dropped; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_OK: + DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->rx_packets - + rndis_per_dev_params [configNr].stats->rx_errors - + rndis_per_dev_params [configNr].stats->rx_dropped; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_XMIT_ERROR: + DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->tx_errors; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_ERROR: + DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->rx_errors; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_NO_BUFFER: + DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->rx_dropped; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_DIRECTED_BYTES_XMIT: + DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); + /* + * Aunt Tilly's size of shoes + * minus antarctica count of penguins + * divided by weight of Alpha Centauri + */ + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = (rndis_per_dev_params [configNr]. + stats->tx_packets - + rndis_per_dev_params [configNr].stats->tx_errors - + rndis_per_dev_params [configNr].stats->tx_dropped) + *123; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_DIRECTED_FRAMES_XMIT: + DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); + /* dito */ + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = (rndis_per_dev_params [configNr]. + stats->tx_packets - + rndis_per_dev_params [configNr].stats->tx_errors - + rndis_per_dev_params [configNr].stats->tx_dropped) + /123; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_MULTICAST_BYTES_XMIT: + DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->multicast*1234; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_MULTICAST_FRAMES_XMIT: + DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->multicast; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_BROADCAST_BYTES_XMIT: + DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->tx_packets/42*255; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_BROADCAST_FRAMES_XMIT: + DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->tx_packets/42; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_DIRECTED_BYTES_RCV: + DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + case OID_GEN_DIRECTED_FRAMES_RCV: + DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + case OID_GEN_MULTICAST_BYTES_RCV: + DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->multicast*1111; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_MULTICAST_FRAMES_RCV: + DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->multicast; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_BROADCAST_BYTES_RCV: + DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->rx_packets/42*255; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_BROADCAST_FRAMES_RCV: + DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->rx_packets/42; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_RCV_CRC_ERROR: + DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = rndis_per_dev_params [configNr]. + stats->rx_crc_errors; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + case OID_GEN_GET_TIME_CAPS: + DEBUG("%s: OID_GEN_GET_TIME_CAPS\n", __FUNCTION__); + break; + + case OID_GEN_GET_NETCARD_TIME: + DEBUG("%s: OID_GEN_GET_NETCARD_TIME\n", __FUNCTION__); + break; + + case OID_GEN_NETCARD_LOAD: + DEBUG("%s: OID_GEN_NETCARD_LOAD\n", __FUNCTION__); + break; + + case OID_GEN_DEVICE_PROFILE: + DEBUG("%s: OID_GEN_DEVICE_PROFILE\n", __FUNCTION__); + break; + + case OID_GEN_INIT_TIME_MS: + DEBUG("%s: OID_GEN_INIT_TIME_MS\n", __FUNCTION__); + break; + + case OID_GEN_RESET_COUNTS: + DEBUG("%s: OID_GEN_RESET_COUNTS\n", __FUNCTION__); + break; + + case OID_GEN_MEDIA_SENSE_COUNTS: + DEBUG("%s: OID_GEN_MEDIA_SENSE_COUNTS\n", __FUNCTION__); + break; + + case OID_GEN_FRIENDLY_NAME: + DEBUG("%s: OID_GEN_FRIENDLY_NAME\n", __FUNCTION__); + break; + + case OID_GEN_MINIPORT_INFO: + DEBUG("%s: OID_GEN_MINIPORT_INFO\n", __FUNCTION__); + break; + + case OID_GEN_RESET_VERIFY_PARAMETERS: + DEBUG("%s: OID_GEN_RESET_VERIFY_PARAMETERS\n", __FUNCTION__); + break; + + /* mandatory */ + case OID_802_3_PERMANENT_ADDRESS: + DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 6; + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].dev->dev_addr, + length); + /* + * we need a MAC address and hope that + * (our MAC + 1) is not in use + */ + *((u8 *) resp + 29) += 1; + retval = 0; + } else { + *((u32 *) resp + 6) = 0; + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_CURRENT_ADDRESS: + DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 6; + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].dev->dev_addr, + length); + /* + * we need a MAC address and hope that + * (our MAC + 1) is not in use + */ + *((u8 *) resp + 29) += 1; + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_MULTICAST_LIST: + DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + length = 4; + /* Multicast base address only */ + *((u32 *) resp + 6) = 0xE0000000; + retval = 0; + break; + + /* mandatory */ + case OID_802_3_MAXIMUM_LIST_SIZE: + DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); + length = 4; + /* Multicast base address only */ + *((u32 *) resp + 6) = 1; + retval = 0; + break; + + case OID_802_3_MAC_OPTIONS: + DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); + break; + + /* mandatory */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) + { + length = 4; + *((u32 *) resp + 6) = rndis_per_dev_params [configNr] + .stats->rx_frame_errors; + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_XMIT_ONE_COLLISION: + DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + /* mandatory */ + case OID_802_3_XMIT_MORE_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = 0; + retval = 0; + break; + + case OID_802_3_XMIT_DEFERRED: + DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_RCV_OVERRUN: + DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_UNDERRUN: + DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_LATE_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); + /* TODO */ + break; + + default: printk (KERN_ERR "%s: unknown OID 0x%08X\n", + __FUNCTION__, OID); + } + + resp->InformationBufferOffset = 16; + resp->InformationBufferLength = length; + resp->MessageLength = 24 + length; + r->length = 24 + length; + return retval; +} + +static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, + rndis_resp_t *r) +{ + rndis_set_cmplt_type *resp; + int i, retval = -ENOTSUPP; + struct rndis_config_parameter *param; + + if (!r) return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + switch (OID) { + case OID_GEN_CURRENT_PACKET_FILTER: + DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); + currentFilter2devFlags ((u32) ((u8 *) resp + 28), + rndis_per_dev_params [configNr].dev); + retval = 0; + if ((u32) ((u8 *) resp + 28)) + rndis_per_dev_params [configNr].state = RNDIS_INITIALIZED; + else + rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; + break; + + case OID_802_3_MULTICAST_LIST: + /* I think we can ignore this */ + DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + retval = 0; + break; + + case OID_GEN_RNDIS_CONFIG_PARAMETER: + DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER\n", __FUNCTION__); + param = (struct rndis_config_parameter *) buf; + if (param) { + for (i = 0; i < param->ParameterNameLength; i++) { + DEBUG ("%c", + *(buf + param->ParameterNameOffset + i)); + } + DEBUG ("\n"); + } + + retval = 0; + break; + + default: printk (KERN_ERR "%s: unknown OID 0x%08X\n", + __FUNCTION__, OID); + } + + return retval; +} + +/* + * Response Functions + */ + +static int rndis_init_response (int configNr, rndis_init_msg_type *buf) +{ + rndis_init_cmplt_type *resp; + rndis_resp_t *r; + + if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + + r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); + + if (!r) return -ENOMEM; + + resp = (rndis_init_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + resp->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT; + resp->MessageLength = 52; + resp->RequestID = buf->RequestID; + resp->Status = RNDIS_STATUS_SUCCESS; + resp->MajorVersion = RNDIS_MAJOR_VERSION; + resp->MinorVersion = RNDIS_MINOR_VERSION; + resp->DeviceFlags = RNDIS_DF_CONNECTIONLESS; + resp->Medium = RNDIS_MEDIUM_802_3; + resp->MaxPacketsPerTransfer = 1; + resp->MaxTransferSize = rndis_per_dev_params [configNr].dev->mtu + + sizeof (struct ethhdr) + + sizeof (struct rndis_packet_msg_type) + + 22; + resp->PacketAlignmentFactor = 0; + resp->AFListOffset = 0; + resp->AFListSize = 0; + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + +static int rndis_query_response (int configNr, rndis_query_msg_type *buf) +{ + rndis_query_cmplt_type *resp; + rndis_resp_t *r; + + DEBUG("%s: OID = %08X\n", __FUNCTION__, buf->OID); + if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + + /* + * we need more memory: + * oid_supported_list is the largest answer + */ + r = rndis_add_response (configNr, sizeof (oid_supported_list)); + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + resp->MessageType = REMOTE_NDIS_QUERY_CMPLT; + resp->MessageLength = 24; + resp->RequestID = buf->RequestID; + + if (gen_ndis_query_resp (configNr, buf->OID, r)) { + /* OID not supported */ + resp->Status = RNDIS_STATUS_NOT_SUPPORTED; + resp->InformationBufferLength = 0; + resp->InformationBufferOffset = 0; + } else + resp->Status = RNDIS_STATUS_SUCCESS; + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + return 0; +} + +static int rndis_set_response (int configNr, rndis_set_msg_type *buf) +{ + rndis_set_cmplt_type *resp; + rndis_resp_t *r; + int i; + + r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); + + if (!r) return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + DEBUG("%s: Length: %d\n", __FUNCTION__, buf->InformationBufferLength); + DEBUG("%s: Offset: %d\n", __FUNCTION__, buf->InformationBufferOffset); + DEBUG("%s: InfoBuffer: ", __FUNCTION__); + + for (i = 0; i < buf->InformationBufferLength; i++) { + DEBUG ("%02x ", *(((u8 *) buf) + i + 12 + + buf->InformationBufferOffset)); + } + + DEBUG ("\n"); + + resp->MessageType = REMOTE_NDIS_SET_CMPLT; + resp->MessageLength = 16; + resp->RequestID = buf->RequestID; + if (gen_ndis_set_resp (configNr, buf->OID, + ((u8 *) buf) + 28, + buf->InformationBufferLength, r)) + resp->Status = RNDIS_STATUS_NOT_SUPPORTED; + else resp->Status = RNDIS_STATUS_SUCCESS; + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack (rndis_per_dev_params [configNr].dev); + + return 0; +} + +static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) +{ + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; + + r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); + + if (!r) return -ENOMEM; + resp = (rndis_reset_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = REMOTE_NDIS_RESET_CMPLT; + resp->MessageLength = 16; + resp->Status = RNDIS_STATUS_SUCCESS; + resp->AddressingReset = 1; /* resent information */ + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + +static int rndis_keepalive_response (int configNr, + rndis_keepalive_msg_type *buf) +{ + rndis_keepalive_cmplt_type *resp; + rndis_resp_t *r; + + /* respond only in RNDIS_INITIALIZED state */ + if (rndis_per_dev_params [configNr].state != RNDIS_INITIALIZED) + return 0; + r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); + resp = (rndis_keepalive_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT; + resp->MessageLength = 16; + resp->RequestID = buf->RequestID; + resp->Status = RNDIS_STATUS_SUCCESS; + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + + +/* + * Device to Host Comunication + */ +static int rndis_indicate_status_msg (int configNr, u32 status) +{ + rndis_indicate_status_msg_type *resp; + rndis_resp_t *r; + + if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) + return -ENOTSUPP; + + r = rndis_add_response (configNr, + sizeof (rndis_indicate_status_msg_type)); + if (!r) return -ENOMEM; + + resp = (rndis_indicate_status_msg_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG; + resp->MessageLength = 20; + resp->Status = status; + resp->StatusBufferLength = 0; + resp->StatusBufferOffset = 0; + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + return 0; +} + +int rndis_signal_connect (int configNr) +{ + rndis_per_dev_params [configNr].media_state + = NDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg (configNr, + RNDIS_STATUS_MEDIA_CONNECT); +} + +int rndis_signal_disconnect (int configNr) +{ + rndis_per_dev_params [configNr].media_state + = NDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg (configNr, + RNDIS_STATUS_MEDIA_DISCONNECT); +} + +/* + * Message Parser + */ +int rndis_msg_parser (u8 configNr, u8 *buf) +{ + u32 MsgType, MsgLength, *tmp; + + if (!buf) return -ENOMEM; + + tmp = (u32 *) buf; + MsgType = *tmp; + MsgLength = *(tmp + 1); + + if (configNr >= RNDIS_MAX_CONFIGS) return -ENOTSUPP; + + switch (MsgType) + { + case REMOTE_NDIS_INIZIALIZE_MSG: + DEBUG(KERN_INFO "%s: REMOTE_NDIS_INIZIALIZE_MSG\n", + __FUNCTION__ ); + rndis_per_dev_params [configNr].state = RNDIS_INITIALIZED; + return rndis_init_response (configNr, + (rndis_init_msg_type *) buf); + break; + + case REMOTE_NDIS_HALT_MSG: + DEBUG(KERN_INFO "%s: REMOTE_NDIS_HALT_MSG\n", + __FUNCTION__ ); + rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; + return 0; + + case REMOTE_NDIS_QUERY_MSG: + DEBUG(KERN_INFO "%s: REMOTE_NDIS_QUERY_MSG\n", + __FUNCTION__ ); + return rndis_query_response (configNr, + (rndis_query_msg_type *) buf); + break; + + case REMOTE_NDIS_SET_MSG: + DEBUG(KERN_INFO "%s: REMOTE_NDIS_SET_MSG\n", + __FUNCTION__ ); + return rndis_set_response (configNr, + (rndis_set_msg_type *) buf); + break; + + case REMOTE_NDIS_RESET_MSG: + DEBUG(KERN_INFO "%s: REMOTE_NDIS_RESET_MSG\n", + __FUNCTION__ ); + return rndis_reset_response (configNr, + (rndis_reset_msg_type *) buf); + break; + + case REMOTE_NDIS_KEEPALIVE_MSG: + DEBUG(KERN_INFO "%s: REMOTE_NDIS_KEEPALIVE_MSG\n", + __FUNCTION__ ); + return rndis_keepalive_response (configNr, + (rndis_keepalive_msg_type *) + buf); + break; + + default: + printk (KERN_ERR "%s: unknown RNDIS Message Type 0x%08X\n", + __FUNCTION__ , MsgType); + break; + } + + return -ENOTSUPP; +} + +int rndis_register (int (* rndis_control_ack) (struct net_device *)) +{ + u8 i; + DEBUG("%s: ", __FUNCTION__ ); + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + if (!rndis_per_dev_params [i].used) { + rndis_per_dev_params [i].used = 1; + rndis_per_dev_params [i].ack = rndis_control_ack; + DEBUG("configNr = %d\n", i); + return i; + } + } + DEBUG("failed\n"); + + return -1; +} + +void rndis_deregister (int configNr) +{ + DEBUG("%s: ", __FUNCTION__ ); + + if (configNr >= RNDIS_MAX_CONFIGS) return; + rndis_per_dev_params [configNr].used = 0; + + return; +} + +int rndis_set_param_dev (u8 configNr, struct net_device *dev, + struct net_device_stats *stats) +{ + DEBUG("%s: ", __FUNCTION__ ); + if (!dev || !stats) return -1; + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].dev = dev; + rndis_per_dev_params [configNr].stats = stats; + + return 0; +} + +int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) +{ + DEBUG("%s: ", __FUNCTION__ ); + if (!vendorDescr) return -1; + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].vendorID = vendorID; + rndis_per_dev_params [configNr].vendorDescr = vendorDescr; + + return 0; +} + +int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) +{ + DEBUG("%s: ", __FUNCTION__ ); + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].medium = medium; + rndis_per_dev_params [configNr].speed = speed; + + return 0; +} + +void rndis_add_hdr (struct sk_buff *skb) +{ + if (!skb) return; + skb_push (skb, sizeof (struct rndis_packet_msg_type)); + memset (skb->data, 0, sizeof (struct rndis_packet_msg_type)); + *((u32 *) skb->data) = 1; + *((u32 *) skb->data + 1) = skb->len; + *((u32 *) skb->data + 2) = 36; + *((u32 *) skb->data + 3) = skb->len - 44; + + return; +} + +void rndis_free_response (int configNr, u8 *buf) +{ + rndis_resp_t *r; + struct list_head *act, *tmp; + + list_for_each_safe (act, tmp, + &(rndis_per_dev_params [configNr].resp_queue)) + { + r = list_entry (act, rndis_resp_t, list); + if (r && r->buf == buf) { + list_del (&r->list); + kfree (r); + } + } +} + +u8 *rndis_get_next_response (int configNr, u32 *length) +{ + rndis_resp_t *r; + struct list_head *act, *tmp; + + if (!length) return NULL; + + list_for_each_safe (act, tmp, + &(rndis_per_dev_params [configNr].resp_queue)) + { + r = list_entry (act, rndis_resp_t, list); + if (!r->send) { + r->send = 1; + *length = r->length; + return r->buf; + } + } + + return NULL; +} + +static rndis_resp_t *rndis_add_response (int configNr, u32 length) +{ + rndis_resp_t *r; + + r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); + if (!r) return NULL; + + r->buf = (u8 *) (r + 1); + r->length = length; + r->send = 0; + + list_add_tail (&r->list, + &(rndis_per_dev_params [configNr].resp_queue)); + return r; +} + +int rndis_rm_hdr (u8 *buf, u32 *length) +{ + u32 i, messageLen, dataOffset; + + if (!buf || !length) return -1; + if (*((u32 *) buf) != 1) return -1; + + messageLen = *((u32 *) buf + 1); + + dataOffset = *((u32 *) buf + 2) + 8; + if (messageLen < dataOffset || messageLen > *length) return -1; + + for (i = dataOffset; i < messageLen; i++) + buf [i - dataOffset] = buf [i]; + + *length = messageLen - dataOffset; + + return 0; +} + +int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + char *out = page; + int len; + rndis_params *param = (rndis_params *) data; + + out += snprintf (out, count, + "Config Nr. %d\n" + "used : %s\n" + "state : %s\n" + "medium : 0x%08X\n" + "speed : %d\n" + "cable : %s\n" + "vendor ID : 0x%08X\n" + "vendor : %s\n", + param->confignr, (param->used) ? "y" : "n", + (param->state) + ? "RNDIS_INITIALIZED" + : "RNDIS_UNINITIALIZED", + param->medium, + (param->media_state) ? 0 : param->speed*100, + (param->media_state) ? "disconnected" : "connected", + param->vendorID, param->vendorDescr); + + len = out - page; + len -= off; + + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; +} + +int rndis_proc_write (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + u32 speed = 0; + int i, fl_speed = 0; + + for (i = 0; i < count; i++) { + switch (*buffer) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + fl_speed = 1; + speed = speed*10 + *buffer - '0'; + break; + case 'C': + case 'c': + rndis_signal_connect (((rndis_params *) data) + ->confignr); + break; + case 'D': + case 'd': + rndis_signal_disconnect (((rndis_params *) data) + ->confignr); + break; + default: + if (fl_speed) ((rndis_params *) data)->speed = speed; + else DEBUG ("%c is not valid\n", *buffer); + break; + } + + buffer++; + } + + return count; +} + +int __init rndis_init (void) +{ + u8 i; + char name [4]; + + /* FIXME this should probably be /proc/driver/rndis, + * and only if debugging is enabled + */ + + if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) { + printk (KERN_ERR "%s: couldn't create /proc/rndis entry", + __FUNCTION__); + return -EIO; + } + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + sprintf (name, "%03d", i); + if (!(rndis_connect_state [i] + = create_proc_entry (name, 0660, + rndis_connect_dir))) + { + DEBUG ("%s :remove entries", __FUNCTION__); + for (i--; i > 0; i--) { + sprintf (name, "%03d", i); + remove_proc_entry (name, rndis_connect_dir); + } + + remove_proc_entry ("000", rndis_connect_dir); + remove_proc_entry ("rndis", NULL); + return -EIO; + } + rndis_connect_state [i]->nlink = 1; + rndis_connect_state [i]->write_proc = rndis_proc_write; + rndis_connect_state [i]->read_proc = rndis_proc_read; + rndis_connect_state [i]->data = (void *) + (rndis_per_dev_params + i); + rndis_per_dev_params [i].confignr = i; + rndis_per_dev_params [i].used = 0; + rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; + rndis_per_dev_params [i].media_state + = NDIS_MEDIA_STATE_DISCONNECTED; + INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue)); + } + + return 0; +} + +void __exit rndis_exit (void) +{ + u8 i; + char name [4]; + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + sprintf (name, "%03d", i); + remove_proc_entry (name, rndis_connect_dir); + } + remove_proc_entry ("rndis", NULL); + return; +} + diff -Nru a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/gadget/rndis.h Wed Apr 14 14:36:13 2004 @@ -0,0 +1,313 @@ +/* + * RNDIS Definitions for Remote NDIS + * + * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ + * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * Due to the Remote NDIS Specification License Agreement this + * program may only be used to interact with a Microsoft Windows + * operating system or a bus/network-connected communication + * device. + */ + +#ifndef _LINUX_RNDIS_H +#define _LINUX_RNDIS_H + +#include "ndis.h" + +#define RNDIS_MAXIMUM_FRAME_SIZE 1518 +#define RNDIS_MAX_TOTAL_SIZE 1558 + +/* Remote NDIS Versions */ +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +/* Status Values */ +#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ +#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */ +#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */ +#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */ +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */ +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */ +/* For all not specified status messages: + * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx + */ + +/* Message Set for Connectionless (802.3) Devices */ +#define REMOTE_NDIS_INIZIALIZE_MSG 0x00000002U /* Initialize device */ +#define REMOTE_NDIS_HALT_MSG 0x00000003U +#define REMOTE_NDIS_QUERY_MSG 0x00000004U +#define REMOTE_NDIS_SET_MSG 0x00000005U +#define REMOTE_NDIS_RESET_MSG 0x00000006U +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U +#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U + +/* Message completion */ +#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U +#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U +#define REMOTE_NDIS_SET_CMPLT 0x80000005U +#define REMOTE_NDIS_RESET_CMPLT 0x80000006U +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U + +/* Device Flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001U +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U + +#define RNDIS_MEDIUM_802_3 0x00000000U + +/* supported OIDs */ +static const u32 oid_supported_list [] = +{ + /* mandatory general */ + /* the general stuff */ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, + OID_GEN_RNDIS_CONFIG_PARAMETER, + + /* the statistical stuff */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_DIRECTED_BYTES_XMIT, + OID_GEN_DIRECTED_FRAMES_XMIT, + OID_GEN_MULTICAST_BYTES_XMIT, + OID_GEN_MULTICAST_FRAMES_XMIT, + OID_GEN_BROADCAST_BYTES_XMIT, + OID_GEN_BROADCAST_FRAMES_XMIT, + OID_GEN_DIRECTED_BYTES_RCV, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_MULTICAST_BYTES_RCV, + OID_GEN_MULTICAST_FRAMES_RCV, + OID_GEN_BROADCAST_BYTES_RCV, + OID_GEN_BROADCAST_FRAMES_RCV, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, + + /* mandatory 802.3 */ + /* the general stuff */ + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAC_OPTIONS, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* the statistical stuff */ + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS +}; + + +typedef struct rndis_init_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 MajorVersion; + u32 MinorVersion; + u32 MaxTransferSize; +} rndis_init_msg_type; + +typedef struct rndis_init_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; + u32 MajorVersion; + u32 MinorVersion; + u32 DeviceFlags; + u32 Medium; + u32 MaxPacketsPerTransfer; + u32 MaxTransferSize; + u32 PacketAlignmentFactor; + u32 AFListOffset; + u32 AFListSize; +} rndis_init_cmplt_type; + +typedef struct rndis_halt_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; +} rndis_halt_msg_type; + +typedef struct rndis_query_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 OID; + u32 InformationBufferLength; + u32 InformationBufferOffset; + u32 DeviceVcHandle; +} rndis_query_msg_type; + +typedef struct rndis_query_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; + u32 InformationBufferLength; + u32 InformationBufferOffset; +} rndis_query_cmplt_type; + +typedef struct rndis_set_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 OID; + u32 InformationBufferLength; + u32 InformationBufferOffset; + u32 DeviceVcHandle; +} rndis_set_msg_type; + +typedef struct rndis_set_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; +} rndis_set_cmplt_type; + +typedef struct rndis_reset_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 Reserved; +} rndis_reset_msg_type; + +typedef struct rndis_reset_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 Status; + u32 AddressingReset; +} rndis_reset_cmplt_type; + +typedef struct rndis_indicate_status_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 Status; + u32 StatusBufferLength; + u32 StatusBufferOffset; +} rndis_indicate_status_msg_type; + +typedef struct rndis_keepalive_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; +} rndis_keepalive_msg_type; + +typedef struct rndis_keepalive_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; +} rndis_keepalive_cmplt_type; + +struct rndis_packet_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 DataOffset; + u32 DataLength; + u32 OOBDataOffset; + u32 OOBDataLength; + u32 NumOOBDataElements; + u32 PerPacketInfoOffset; + u32 PerPacketInfoLength; + u32 VcHandle; + u32 Reserved; +}; + +struct rndis_config_parameter +{ + u32 ParameterNameOffset; + u32 ParameterNameLength; + u32 ParameterType; + u32 ParameterValueOffset; + u32 ParameterValueLength; +}; + +/* implementation specific */ +enum rndis_state +{ + RNDIS_UNINITIALIZED, + RNDIS_INITIALIZED, + RNDIS_DATA_INITIALIZED, +}; + +typedef struct rndis_resp_t +{ + struct list_head list; + u8 *buf; + u32 length; + int send; +} rndis_resp_t; + +typedef struct rndis_params +{ + u8 confignr; + int used; + enum rndis_state state; + u32 medium; + u32 speed; + u32 media_state; + struct net_device *dev; + struct net_device_stats *stats; + u32 vendorID; + const char *vendorDescr; + int (*ack) (struct net_device *); + struct list_head resp_queue; +} rndis_params; + +/* RNDIS Message parser and other useless functions */ +int rndis_msg_parser (u8 configNr, u8 *buf); +int rndis_register (int (*rndis_control_ack) (struct net_device *)); +void rndis_deregister (int configNr); +int rndis_set_param_dev (u8 configNr, struct net_device *dev, + struct net_device_stats *stats); +int rndis_set_param_vendor (u8 configNr, u32 vendorID, + const char *vendorDescr); +int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); +void rndis_add_hdr (struct sk_buff *skb); +int rndis_rm_hdr (u8 *buf, u32 *length); +u8 *rndis_get_next_response (int configNr, u32 *length); +void rndis_free_response (int configNr, u8 *buf); +int rndis_signal_connect (int configNr); +int rndis_signal_disconnect (int configNr); +int rndis_state (int configNr); + +int __init rndis_init (void); +void __exit rndis_exit (void); + +#endif /* _LINUX_RNDIS_H */