Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / usb / gadget / rndis.c
index 7457268..6d6eaad 100644 (file)
@@ -28,7 +28,6 @@
 #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>
@@ -41,6 +40,7 @@
 
 
 #undef RNDIS_PM
+#undef RNDIS_WAKEUP
 #undef VERBOSE
 
 #include "rndis.h"
@@ -60,7 +60,7 @@
        } while (0)
 static int rndis_debug = 0;
 
-module_param (rndis_debug, bool, 0);
+module_param (rndis_debug, int, 0);
 MODULE_PARM_DESC (rndis_debug, "enable debugging");
 
 #else
@@ -78,22 +78,103 @@ static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
 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);
 
 
+/* supported OIDs */
+static const u32 oid_supported_list [] = 
+{
+       /* 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_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_MEDIA_CONNECT_STATUS,
+       OID_GEN_PHYSICAL_MEDIUM,
+#if 0
+       OID_GEN_RNDIS_CONFIG_PARAMETER,
+#endif
+       
+       /* the statistical stuff */
+       OID_GEN_XMIT_OK,
+       OID_GEN_RCV_OK,
+       OID_GEN_XMIT_ERROR,
+       OID_GEN_RCV_ERROR,
+       OID_GEN_RCV_NO_BUFFER,
+#ifdef RNDIS_OPTIONAL_STATS
+       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,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+       /* 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,
+#ifdef RNDIS_OPTIONAL_STATS
+       OID_802_3_XMIT_DEFERRED,
+       OID_802_3_XMIT_MAX_COLLISIONS,
+       OID_802_3_RCV_OVERRUN,
+       OID_802_3_XMIT_UNDERRUN,
+       OID_802_3_XMIT_HEARTBEAT_FAILURE,
+       OID_802_3_XMIT_TIMES_CRS_LOST,
+       OID_802_3_XMIT_LATE_COLLISIONS,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+       /* PM and wakeup are mandatory for USB: */
+
+       /* power management */
+       OID_PNP_CAPABILITIES,
+       OID_PNP_QUERY_POWER,
+       OID_PNP_SET_POWER,
+
+#ifdef RNDIS_WAKEUP
+       /* wake up host */
+       OID_PNP_ENABLE_WAKE_UP,
+       OID_PNP_ADD_WAKE_UP_PATTERN,
+       OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif /* RNDIS_WAKEUP */
+#endif /* RNDIS_PM */
+};
+
+
 /* NDIS Functions */
-static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
+static int
+gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+               rndis_resp_t *r)
 {
        int                     retval = -ENOTSUPP;
-       u32                     length = 0;
-       __le32                  *tmp;
+       u32                     length = 4;     /* usually */
+       __le32                  *outbuf;
        int                     i, count;
        rndis_query_cmplt_type  *resp;
 
@@ -101,7 +182,22 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        resp = (rndis_query_cmplt_type *) r->buf;
 
        if (!resp) return -ENOMEM;
-       
+
+       if (buf_len && rndis_debug > 1) {
+               DEBUG("query OID %08x value, len %d:\n", OID, buf_len);
+               for (i = 0; i < buf_len; i += 16) {
+                       DEBUG ("%03d: %08x %08x %08x %08x\n", i,
+                               le32_to_cpup((__le32 *)&buf[i]),
+                               le32_to_cpup((__le32 *)&buf[i + 4]),
+                               le32_to_cpup((__le32 *)&buf[i + 8]),
+                               le32_to_cpup((__le32 *)&buf[i + 12]));
+               }
+       }
+
+       /* response goes here, right after the header */
+       outbuf = (__le32 *) &resp[1];
+       resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
+
        switch (OID) {
 
        /* general oids (table 4-1) */
@@ -111,42 +207,36 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                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]);
+                       outbuf[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);
+               *outbuf = __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);
+               *outbuf = 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);
+               *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
                retval = 0;
                break;
                
@@ -154,25 +244,21 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        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 (
+                       *outbuf = 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_debug > 1)
+                       DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].media_state
-                       == NDIS_MEDIA_STATE_DISCONNECTED)
-                   *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+                               == NDIS_MEDIA_STATE_DISCONNECTED)
+                       *outbuf = __constant_cpu_to_le32 (0);
                else
-                   *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].speed);
                retval = 0;
                break;
@@ -181,8 +267,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        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 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].dev->mtu);
                        retval = 0;
                }
@@ -192,8 +277,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        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 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].dev->mtu);
                        retval = 0;
                }
@@ -202,8 +286,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* mandatory */
        case OID_GEN_VENDOR_ID:
                DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = cpu_to_le32 (
+               *outbuf = cpu_to_le32 (
                        rndis_per_dev_params [configNr].vendorID);
                retval = 0;
                break;
@@ -212,51 +295,44 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        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, 
+               memcpy (outbuf,
                        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;
+               *outbuf = 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);
+               *outbuf = 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);
+               *outbuf = __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]
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
+               *outbuf = 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);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
 
@@ -266,8 +342,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
         */
        case OID_GEN_MAC_OPTIONS:               /* from WinME */
                DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32(
+               *outbuf = __constant_cpu_to_le32(
                          NDIS_MAC_OPTION_RECEIVE_SERIALIZED
                        | NDIS_MAC_OPTION_FULL_DUPLEX);
                retval = 0;
@@ -277,62 +352,49 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
 
        /* mandatory */
        case OID_GEN_XMIT_OK:
-               DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = 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_debug > 1)
+                       DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = 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_debug > 1)
+                       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]
+                       *outbuf = 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_debug > 1)
+                       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]
+                       *outbuf = 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;
                
@@ -340,13 +402,9 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        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]
+                       *outbuf = 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;
 
@@ -359,8 +417,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                 * divided by weight of Alpha Centauri
                 */
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                (rndis_per_dev_params [configNr]
                                        .stats->tx_packets - 
                                 rndis_per_dev_params [configNr]
@@ -369,9 +426,6 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                                         .stats->tx_dropped)
                                * 123);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
@@ -379,8 +433,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                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 (
+                       *outbuf = cpu_to_le32 (
                                (rndis_per_dev_params [configNr]
                                        .stats->tx_packets - 
                                 rndis_per_dev_params [configNr]
@@ -389,144 +442,105 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                                         .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]
+                       *outbuf = 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]
+                       *outbuf = 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]
+                       *outbuf = 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]
+                       *outbuf = 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);
+               *outbuf = __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);
+               *outbuf = __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]
+                       *outbuf = 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]
+                       *outbuf = 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]
+                       *outbuf = 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]
+                       *outbuf = 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]
+                       *outbuf = 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);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
 #endif /* RNDIS_OPTIONAL_STATS */
@@ -538,13 +552,10 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
                        length = ETH_ALEN;
-                       memcpy ((u8 *) resp + 24,
+                       memcpy (outbuf,
                                rndis_per_dev_params [configNr].host_mac,
                                length);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
@@ -553,7 +564,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
                        length = ETH_ALEN;
-                       memcpy ((u8 *) resp + 24,
+                       memcpy (outbuf,
                                rndis_per_dev_params [configNr].host_mac,
                                length);
                        retval = 0;
@@ -563,18 +574,16 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* 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);
+               *outbuf = __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);
+               *outbuf = __constant_cpu_to_le32 (1);
                retval = 0;
                break;
                
@@ -587,11 +596,8 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* 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]
+               if (rndis_per_dev_params [configNr].stats) {
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_frame_errors);
                        retval = 0;
                }
@@ -600,16 +606,14 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* 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);
+               *outbuf = __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);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
                
@@ -655,27 +659,18 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        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)
-                */
+               /* for now, no wakeup capabilities */
                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
-                        */
-               }
+               memset(outbuf, 0, length);
                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 */
+               DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
+                               le32_to_cpup((__le32 *) buf) - 1);
+               /* only suspend is a real power state, and
+                * it can't be entered by OID_PNP_SET_POWER...
+                */
+               length = 0;
                retval = 0;
                break;
 #endif
@@ -684,11 +679,12 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", 
                         __FUNCTION__, OID);
        }
+       if (retval < 0)
+               length = 0;
        
-       resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
        resp->InformationBufferLength = cpu_to_le32 (length);
-       resp->MessageLength = cpu_to_le32 (24 + length);
-       r->length = 24 + length;
+       r->length = length + sizeof *resp;
+       resp->MessageLength = cpu_to_le32 (r->length);
        return retval;
 }
 
@@ -705,45 +701,40 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
        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]);
+       if (buf_len && rndis_debug > 1) {
+               DEBUG("set OID %08x value, len %d:\n", OID, buf_len);
+               for (i = 0; i < buf_len; i += 16) {
+                       DEBUG ("%03d: %08x %08x %08x %08x\n", i,
+                               le32_to_cpup((__le32 *)&buf[i]),
+                               le32_to_cpup((__le32 *)&buf[i + 4]),
+                               le32_to_cpup((__le32 *)&buf[i + 8]),
+                               le32_to_cpup((__le32 *)&buf[i + 12]));
+               }
        }
 
+       params = &rndis_per_dev_params [configNr];
        switch (OID) {
        case OID_GEN_CURRENT_PACKET_FILTER:
-               params = &rndis_per_dev_params [configNr];
-               retval = 0;
 
-               /* FIXME use these NDIS_PACKET_TYPE_* bitflags to
-                * set the cdc_filter; it's not RNDIS-specific
+               /* these NDIS_PACKET_TYPE_* bitflags are shared with
+                * cdc_filter; it's not RNDIS-specific
                 * 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);
+               *params->filter = (u16) le32_to_cpup((__le32 *)buf);
                DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
-                       __FUNCTION__, params->filter);
+                       __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) {
+#ifdef RNDIS_PM
+update_linkstate:
+#endif
+               retval = 0;
+               if (*params->filter) {
                        params->state = RNDIS_DATA_INITIALIZED;
                        netif_carrier_on(params->dev);
                        if (netif_running(params->dev))
@@ -776,21 +767,34 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
 
 #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;
+               /* The only real power state is USB suspend, and RNDIS requests
+                * can't enter it; this one isn't really about power.  After
+                * resuming, Windows forces a reset, and then SET_POWER D0.
+                * FIXME ... then things go batty; Windows wedges itself.
+                */
+               i = le32_to_cpup((__force __le32 *)buf);
+               DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
+               switch (i) {
+               case NdisDeviceStateD0:
+                       *params->filter = params->saved_filter;
+                       goto update_linkstate;
+               case NdisDeviceStateD3:
+               case NdisDeviceStateD2:
+               case NdisDeviceStateD1:
+                       params->saved_filter = *params->filter;
+                       retval = 0;
+                       break;
+               }
                break;
 
-       // no PM resume patterns supported (specified where?)
-       // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails
+#ifdef RNDIS_WAKEUP
+       // no wakeup support advertised, so wakeup OIDs always fail:
+       //  - OID_PNP_ENABLE_WAKE_UP
+       //  - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
 #endif
 
+#endif /* RNDIS_PM */
+
        default:
                printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", 
                         __FUNCTION__, OID, buf_len);
@@ -811,13 +815,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
        if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
        
        r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
-       
-       if (!r) return -ENOMEM;
-       
+       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);
@@ -852,25 +853,30 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
        // 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 
+       /*
+        * we need more memory:
+        * gen_ndis_query_resp expects enough space for
+        * rndis_query_cmplt_type followed by data.
+        * oid_supported_list is the largest data reply
         */
-       r = rndis_add_response (configNr, sizeof (oid_supported_list));
-       
-       if (!r) return -ENOMEM;
+       r = rndis_add_response (configNr,
+               sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type));
+       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)) {
+       
+       if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
+                       le32_to_cpu(buf->InformationBufferOffset)
+                                       + 8 + (u8 *) buf,
+                       le32_to_cpu(buf->InformationBufferLength),
+                       r)) {
                /* OID not supported */
                resp->Status = __constant_cpu_to_le32 (
                                RNDIS_STATUS_NOT_SUPPORTED);
+               resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp);
                resp->InformationBufferLength = __constant_cpu_to_le32 (0);
                resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
        } else
@@ -889,10 +895,9 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
        rndis_resp_t            *r;
        
        r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
-       
-       if (!r) return -ENOMEM;
+       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);
@@ -930,10 +935,9 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
        rndis_resp_t            *r;
        
        r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
-       
-       if (!r) return -ENOMEM;
+       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);
@@ -957,8 +961,9 @@ static int rndis_keepalive_response (int configNr,
        /* host "should" check only in RNDIS_DATA_INITIALIZED state */
 
        r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_keepalive_cmplt_type *) r->buf;
-       if (!resp) return -ENOMEM;
                
        resp->MessageType = __constant_cpu_to_le32 (
                        REMOTE_NDIS_KEEPALIVE_CMPLT);
@@ -987,10 +992,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status)
        
        r = rndis_add_response (configNr, 
                                sizeof (rndis_indicate_status_msg_type));
-       if (!r) return -ENOMEM;
-       
+       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);
@@ -1021,6 +1025,21 @@ int rndis_signal_disconnect (int configNr)
                                          RNDIS_STATUS_MEDIA_DISCONNECT);
 }
 
+void rndis_uninit (int configNr)
+{
+       u8 *buf;
+       u32 length;
+
+       if (configNr >= RNDIS_MAX_CONFIGS)
+               return;
+       rndis_per_dev_params [configNr].used = 0;
+       rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
+
+       /* drain the response queue */
+       while ((buf = rndis_get_next_response(configNr, &length)))
+               rndis_free_response(configNr, buf);
+}
+
 void rndis_set_host_mac (int configNr, const u8 *addr)
 {
        rndis_per_dev_params [configNr].host_mac = addr;
@@ -1046,9 +1065,13 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
                return -ENOTSUPP;
        params = &rndis_per_dev_params [configNr];
        
+       /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+        * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+        * and normal HC level polling to see if there's any IN traffic.
+        */
+
        /* For USB: responses may take up to 10 seconds */
-       switch (MsgType)
-       {
+       switch (MsgType) {
        case REMOTE_NDIS_INITIALIZE_MSG:
                DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", 
                        __FUNCTION__ );
@@ -1082,10 +1105,9 @@ int rndis_msg_parser (u8 configNr, u8 *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
+               if (rndis_debug > 1)
+                       DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", 
+                               __FUNCTION__ );
                return rndis_keepalive_response (configNr,
                                                 (rndis_keepalive_msg_type *) 
                                                 buf);
@@ -1152,7 +1174,8 @@ void rndis_deregister (int configNr)
 }
 
 int rndis_set_param_dev (u8 configNr, struct net_device *dev, 
-                        struct net_device_stats *stats)
+                        struct net_device_stats *stats,
+                        u16 *cdc_filter)
 {
        DEBUG("%s:\n", __FUNCTION__ );
        if (!dev || !stats) return -1;
@@ -1160,6 +1183,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev,
        
        rndis_per_dev_params [configNr].dev = dev;
        rndis_per_dev_params [configNr].stats = stats;
+       rndis_per_dev_params [configNr].filter = cdc_filter;
        
        return 0;
 }
@@ -1178,7 +1202,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
 
 int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
 {
-       DEBUG("%s:\n", __FUNCTION__ );
+       DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed);
        if (configNr >= RNDIS_MAX_CONFIGS) return -1;
        
        rndis_per_dev_params [configNr].medium = medium;
@@ -1242,6 +1266,7 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length)
 {
        rndis_resp_t    *r;
        
+       /* NOTE:  this gets copied into ether.c USB_BUFSIZ bytes ... */
        r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
        if (!r) return NULL;