Commit 166685d0 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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.
parent 21d554cc
...@@ -219,9 +219,11 @@ config USB_ETH ...@@ -219,9 +219,11 @@ config USB_ETH
favor of simpler vendor-specific hardware, but is widely favor of simpler vendor-specific hardware, but is widely
supported by firmware for smart network devices. 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. 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 Within the USB device, this gadget driver exposes a network device
"usbX", where X depends on what other networking devices you have. "usbX", where X depends on what other networking devices you have.
Treat it like a two-node Ethernet link: host, and gadget. Treat it like a two-node Ethernet link: host, and gadget.
...@@ -235,6 +237,19 @@ config USB_ETH ...@@ -235,6 +237,19 @@ config USB_ETH
Say "y" to link the driver statically, or "m" to build a Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_ether". 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 config USB_GADGETFS
tristate "Gadget Filesystem (EXPERIMENTAL)" tristate "Gadget Filesystem (EXPERIMENTAL)"
depends on EXPERIMENTAL depends on EXPERIMENTAL
......
...@@ -15,6 +15,10 @@ g_serial-objs := serial.o usbstring.o ...@@ -15,6 +15,10 @@ g_serial-objs := serial.o usbstring.o
gadgetfs-objs := inode.o gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.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_ZERO) += g_zero.o
obj-$(CONFIG_USB_ETH) += g_ether.o obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
......
/*
* ndis.h
*
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
* 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 */
/*
* 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 <linux-development@auerswald.de>
* Fixed message length bug in init_response
*
* 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
* Fixed rndis_rm_hdr length bug.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#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;
}
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment