diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/gadget/rndis.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/gadget/rndis.c')
-rw-r--r-- | drivers/usb/gadget/rndis.c | 1428 |
1 files changed, 1428 insertions, 0 deletions
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c new file mode 100644 index 00000000000..6c5197850ed --- /dev/null +++ b/drivers/usb/gadget/rndis.c @@ -0,0 +1,1428 @@ +/* + * 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. + * + * Copyright (C) 2004 by David Brownell + * updates to merge with Linux 2.6, better match RNDIS spec + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/moduleparam.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> + + +#undef RNDIS_PM +#undef VERBOSE + +#include "rndis.h" + + +/* The driver for your USB chip needs to support ep0 OUT to work with + * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). + * + * Windows hosts need an INF file like Documentation/usb/linux.inf + * and will be happier if you provide the host_addr module parameter. + */ + +#if 0 +#define DEBUG(str,args...) do { \ + if (rndis_debug) \ + printk(KERN_DEBUG str , ## args ); \ + } while (0) +static int rndis_debug = 0; + +module_param (rndis_debug, bool, 0); +MODULE_PARM_DESC (rndis_debug, "enable debugging"); + +#else + +#define rndis_debug 0 +#define DEBUG(str,args...) do{}while(0) +#endif + +#define RNDIS_MAX_CONFIGS 1 + + +static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; + +/* Driver Version */ +static const __le32 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); + + +/* NDIS Functions */ +static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) +{ + int retval = -ENOTSUPP; + u32 length = 0; + __le32 *tmp; + int i, count; + rndis_query_cmplt_type *resp; + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + switch (OID) { + + /* general oids (table 4-1) */ + + /* mandatory */ + case OID_GEN_SUPPORTED_LIST: + DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); + length = sizeof (oid_supported_list); + count = length / sizeof (u32); + tmp = (__le32 *) ((u8 *)resp + 24); + for (i = 0; i < count; i++) + tmp[i] = cpu_to_le32 (oid_supported_list[i]); + 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 receive high level protocols. + * BTW: + * reddite ergo quae sunt Caesaris Caesari + * et quae sunt Dei Deo! + */ + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_SUPPORTED: + DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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) */ + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].medium); + retval = 0; + 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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].dev->mtu); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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 + == NDIS_MEDIA_STATE_DISCONNECTED) + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + else + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].speed); + retval = 0; + 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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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; + + case OID_GEN_VENDOR_DRIVER_VERSION: + DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); + length = 4; + /* Created as LE */ + *((__le32 *) resp + 6) = rndis_driver_version; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_CURRENT_PACKET_FILTER: + DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params[configNr].filter); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_TOTAL_SIZE: + DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = __constant_cpu_to_le32( + RNDIS_MAX_TOTAL_SIZE); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_CONNECT_STATUS: + DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .media_state); + retval = 0; + break; + + case OID_GEN_PHYSICAL_MEDIUM: + DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + /* The RNDIS specification is incomplete/wrong. Some versions + * of MS-Windows expect OIDs that aren't specified there. Other + * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! + */ + case OID_GEN_MAC_OPTIONS: /* from WinME */ + DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = __constant_cpu_to_le32( + NDIS_MAC_OPTION_RECEIVE_SERIALIZED + | NDIS_MAC_OPTION_FULL_DUPLEX); + retval = 0; + break; + + /* statistics OIDs (table 4-2) */ + + /* mandatory */ + case OID_GEN_XMIT_OK: + DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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 { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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 { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->tx_errors); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_errors); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_dropped); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + +#ifdef RNDIS_OPTIONAL_STATS + 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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + (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 { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + (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 { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast*1234); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->tx_packets/42*255); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->tx_packets/42); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_DIRECTED_BYTES_RCV: + DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + case OID_GEN_DIRECTED_FRAMES_RCV: + DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast * 1111); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_packets/42*255); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_packets/42); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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) { + *((__le32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_crc_errors); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; +#endif /* RNDIS_OPTIONAL_STATS */ + + /* ieee802.3 OIDs (table 4-3) */ + + /* mandatory */ + case OID_802_3_PERMANENT_ADDRESS: + DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = ETH_ALEN; + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].host_mac, + length); + retval = 0; + } else { + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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 = ETH_ALEN; + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].host_mac, + length); + 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 */ + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (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 */ + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (1); + retval = 0; + break; + + case OID_802_3_MAC_OPTIONS: + DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); + break; + + /* ieee802.3 statistics OIDs (table 4-4) */ + + /* 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; + *((__le32 *) resp + 6) = cpu_to_le32 ( + 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; + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + /* mandatory */ + case OID_802_3_XMIT_MORE_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); + length = 4; + *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + +#ifdef RNDIS_OPTIONAL_STATS + 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; +#endif /* RNDIS_OPTIONAL_STATS */ + +#ifdef RNDIS_PM + /* power management OIDs (table 4-5) */ + case OID_PNP_CAPABILITIES: + DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); + + /* just PM, and remote wakeup on link status change + * (not magic packet or pattern match) + */ + length = sizeof (struct NDIS_PNP_CAPABILITIES); + memset (resp, 0, length); + { + struct NDIS_PNP_CAPABILITIES *caps = (void *) resp; + + caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE; + caps->WakeUpCapabilities.MinLinkChangeWakeUp + = NdisDeviceStateD3; + + /* FIXME then use usb_gadget_wakeup(), and + * set USB_CONFIG_ATT_WAKEUP in config desc + */ + } + retval = 0; + break; + case OID_PNP_QUERY_POWER: + DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__); + /* sure, handle any power state that maps to USB suspend */ + retval = 0; + break; +#endif + + default: + printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", + __FUNCTION__, OID); + } + + resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + resp->InformationBufferLength = cpu_to_le32 (length); + resp->MessageLength = cpu_to_le32 (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_params *params; + + if (!r) + return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + if (!resp) + return -ENOMEM; + + DEBUG("set OID %08x value, len %d:\n", OID, buf_len); + for (i = 0; i < buf_len; i += 16) { + DEBUG ("%03d: " + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + "\n", + i, + buf[i], buf [i+1], + buf[i+2], buf[i+3], + buf[i+4], buf [i+5], + buf[i+6], buf[i+7], + buf[i+8], buf [i+9], + buf[i+10], buf[i+11], + buf[i+12], buf [i+13], + buf[i+14], buf[i+15]); + } + + switch (OID) { + case OID_GEN_CURRENT_PACKET_FILTER: + params = &rndis_per_dev_params [configNr]; + retval = 0; + + /* FIXME use these NDIS_PACKET_TYPE_* bitflags to + * filter packets in hard_start_xmit() + * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: + * PROMISCUOUS, DIRECTED, + * MULTICAST, ALL_MULTICAST, BROADCAST + */ + params->filter = le32_to_cpup((__le32 *)buf); + DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", + __FUNCTION__, params->filter); + + /* this call has a significant side effect: it's + * what makes the packet flow start and stop, like + * activating the CDC Ethernet altsetting. + */ + if (params->filter) { + params->state = RNDIS_DATA_INITIALIZED; + netif_carrier_on(params->dev); + if (netif_running(params->dev)) + netif_wake_queue (params->dev); + } else { + params->state = RNDIS_INITIALIZED; + netif_carrier_off (params->dev); + netif_stop_queue (params->dev); + } + 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; +#if 0 + case OID_GEN_RNDIS_CONFIG_PARAMETER: + { + struct rndis_config_parameter *param; + param = (struct rndis_config_parameter *) buf; + DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", + __FUNCTION__, + min(cpu_to_le32(param->ParameterNameLength),80), + buf + param->ParameterNameOffset); + retval = 0; + } + break; +#endif + +#ifdef RNDIS_PM + case OID_PNP_SET_POWER: + DEBUG ("OID_PNP_SET_POWER\n"); + /* sure, handle any power state that maps to USB suspend */ + retval = 0; + break; + + case OID_PNP_ENABLE_WAKE_UP: + /* always-connected ... */ + DEBUG ("OID_PNP_ENABLE_WAKE_UP\n"); + retval = 0; + break; + + // no PM resume patterns supported (specified where?) + // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails +#endif + + default: + printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", + __FUNCTION__, OID, buf_len); + } + + 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 = __constant_cpu_to_le32 ( + REMOTE_NDIS_INITIALIZE_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (52); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION); + resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION); + resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); + resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); + resp->MaxTransferSize = cpu_to_le32 ( + rndis_per_dev_params [configNr].dev->mtu + + sizeof (struct ethhdr) + + sizeof (struct rndis_packet_msg_type) + + 22); + resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0); + resp->AFListOffset = __constant_cpu_to_le32 (0); + resp->AFListSize = __constant_cpu_to_le32 (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__, cpu_to_le32(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 = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (24); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + + if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), r)) { + /* OID not supported */ + resp->Status = __constant_cpu_to_le32 ( + RNDIS_STATUS_NOT_SUPPORTED); + resp->InformationBufferLength = __constant_cpu_to_le32 (0); + resp->InformationBufferOffset = __constant_cpu_to_le32 (0); + } else + resp->Status = __constant_cpu_to_le32 (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) +{ + u32 BufLength, BufOffset; + rndis_set_cmplt_type *resp; + rndis_resp_t *r; + + 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; + + BufLength = le32_to_cpu (buf->InformationBufferLength); + BufOffset = le32_to_cpu (buf->InformationBufferOffset); + +#ifdef VERBOSE + DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength); + DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset); + DEBUG("%s: InfoBuffer: ", __FUNCTION__); + + for (i = 0; i < BufLength; i++) { + DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); + } + + DEBUG ("\n"); +#endif + + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (16); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID), + ((u8 *) buf) + 8 + BufOffset, BufLength, r)) + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); + else resp->Status = __constant_cpu_to_le32 (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 = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (16); + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + /* resent information */ + resp->AddressingReset = __constant_cpu_to_le32 (1); + + 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; + + /* host "should" check only in RNDIS_DATA_INITIALIZED state */ + + r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); + resp = (rndis_keepalive_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = __constant_cpu_to_le32 ( + REMOTE_NDIS_KEEPALIVE_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (16); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32 (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 = __constant_cpu_to_le32 ( + REMOTE_NDIS_INDICATE_STATUS_MSG); + resp->MessageLength = __constant_cpu_to_le32 (20); + resp->Status = cpu_to_le32 (status); + resp->StatusBufferLength = __constant_cpu_to_le32 (0); + resp->StatusBufferOffset = __constant_cpu_to_le32 (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); +} + +void rndis_set_host_mac (int configNr, const u8 *addr) +{ + rndis_per_dev_params [configNr].host_mac = addr; +} + +/* + * Message Parser + */ +int rndis_msg_parser (u8 configNr, u8 *buf) +{ + u32 MsgType, MsgLength; + __le32 *tmp; + struct rndis_params *params; + + if (!buf) + return -ENOMEM; + + tmp = (__le32 *) buf; + MsgType = le32_to_cpup(tmp++); + MsgLength = le32_to_cpup(tmp++); + + if (configNr >= RNDIS_MAX_CONFIGS) + return -ENOTSUPP; + params = &rndis_per_dev_params [configNr]; + + /* For USB: responses may take up to 10 seconds */ + switch (MsgType) + { + case REMOTE_NDIS_INITIALIZE_MSG: + DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", + __FUNCTION__ ); + params->state = RNDIS_INITIALIZED; + return rndis_init_response (configNr, + (rndis_init_msg_type *) buf); + + case REMOTE_NDIS_HALT_MSG: + DEBUG("%s: REMOTE_NDIS_HALT_MSG\n", + __FUNCTION__ ); + params->state = RNDIS_UNINITIALIZED; + if (params->dev) { + netif_carrier_off (params->dev); + netif_stop_queue (params->dev); + } + return 0; + + case REMOTE_NDIS_QUERY_MSG: + return rndis_query_response (configNr, + (rndis_query_msg_type *) buf); + + case REMOTE_NDIS_SET_MSG: + return rndis_set_response (configNr, + (rndis_set_msg_type *) buf); + + case REMOTE_NDIS_RESET_MSG: + DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", + __FUNCTION__ ); + return rndis_reset_response (configNr, + (rndis_reset_msg_type *) buf); + + case REMOTE_NDIS_KEEPALIVE_MSG: + /* For USB: host does this every 5 seconds */ +#ifdef VERBOSE + DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", + __FUNCTION__ ); +#endif + return rndis_keepalive_response (configNr, + (rndis_keepalive_msg_type *) + buf); + + default: + /* At least Windows XP emits some undefined RNDIS messages. + * In one case those messages seemed to relate to the host + * suspending itself. + */ + printk (KERN_WARNING + "%s: unknown RNDIS message 0x%08X len %d\n", + __FUNCTION__ , MsgType, MsgLength); + { + unsigned i; + for (i = 0; i < MsgLength; i += 16) { + DEBUG ("%03d: " + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + "\n", + i, + buf[i], buf [i+1], + buf[i+2], buf[i+3], + buf[i+4], buf [i+5], + buf[i+6], buf[i+7], + buf[i+8], buf [i+9], + buf[i+10], buf[i+11], + buf[i+12], buf [i+13], + buf[i+14], buf[i+15]); + } + } + break; + } + + return -ENOTSUPP; +} + +int rndis_register (int (* rndis_control_ack) (struct net_device *)) +{ + u8 i; + + 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("%s: configNr = %d\n", __FUNCTION__, i); + return i; + } + } + DEBUG("failed\n"); + + return -1; +} + +void rndis_deregister (int configNr) +{ + DEBUG("%s: \n", __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:\n", __FUNCTION__ ); + if (!dev || !stats) return -1; |