blob: d252015576fba105bbd90111aa44b21b818d5702 [file] [log] [blame]
David Brownell7e27f182006-06-13 09:54:40 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * RNDIS MSG parser
David Brownell7e27f182006-06-13 09:54:40 -07003 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Authors: Benedikt Spranger, Pengutronix
David Brownell7e27f182006-06-13 09:54:40 -07005 * Robert Schwebel, Pengutronix
6 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
David Brownell7e27f182006-06-13 09:54:40 -07009 * version 2, as published by the Free Software Foundation.
10 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 * This software was originally developed in conformance with
12 * Microsoft's Remote NDIS Specification License Agreement.
David Brownell7e27f182006-06-13 09:54:40 -070013 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
15 * Fixed message length bug in init_response
David Brownell7e27f182006-06-13 09:54:40 -070016 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
David Brownell7e27f182006-06-13 09:54:40 -070018 * Fixed rndis_rm_hdr length bug.
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 *
20 * Copyright (C) 2004 by David Brownell
21 * updates to merge with Linux 2.6, better match RNDIS spec
22 */
23
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/module.h>
25#include <linux/moduleparam.h>
26#include <linux/kernel.h>
27#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/init.h>
29#include <linux/list.h>
30#include <linux/proc_fs.h>
Alexey Dobriyane184d5f2008-05-14 16:25:13 -070031#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/netdevice.h>
33
34#include <asm/io.h>
35#include <asm/byteorder.h>
36#include <asm/system.h>
David Brownell6cdee102005-04-18 17:39:34 -070037#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
39
40#undef RNDIS_PM
David Brownell340600a2005-04-28 13:45:25 -070041#undef RNDIS_WAKEUP
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#undef VERBOSE
43
44#include "rndis.h"
45
46
47/* The driver for your USB chip needs to support ep0 OUT to work with
48 * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
49 *
50 * Windows hosts need an INF file like Documentation/usb/linux.inf
51 * and will be happier if you provide the host_addr module parameter.
52 */
53
54#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static int rndis_debug = 0;
David Brownell340600a2005-04-28 13:45:25 -070056module_param (rndis_debug, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057MODULE_PARM_DESC (rndis_debug, "enable debugging");
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#define rndis_debug 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#endif
61
David Brownell00274922007-11-19 12:58:36 -080062#define DBG(str,args...) do { \
63 if (rndis_debug) \
64 pr_debug(str , ## args); \
65 } while (0)
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#define RNDIS_MAX_CONFIGS 1
68
69
70static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
71
72/* Driver Version */
73static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1);
74
75/* Function Prototypes */
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static rndis_resp_t *rndis_add_response (int configNr, u32 length);
77
78
David Brownell340600a2005-04-28 13:45:25 -070079/* supported OIDs */
David Brownell7e27f182006-06-13 09:54:40 -070080static const u32 oid_supported_list [] =
David Brownell340600a2005-04-28 13:45:25 -070081{
82 /* the general stuff */
83 OID_GEN_SUPPORTED_LIST,
84 OID_GEN_HARDWARE_STATUS,
85 OID_GEN_MEDIA_SUPPORTED,
86 OID_GEN_MEDIA_IN_USE,
87 OID_GEN_MAXIMUM_FRAME_SIZE,
88 OID_GEN_LINK_SPEED,
89 OID_GEN_TRANSMIT_BLOCK_SIZE,
90 OID_GEN_RECEIVE_BLOCK_SIZE,
91 OID_GEN_VENDOR_ID,
92 OID_GEN_VENDOR_DESCRIPTION,
93 OID_GEN_VENDOR_DRIVER_VERSION,
94 OID_GEN_CURRENT_PACKET_FILTER,
95 OID_GEN_MAXIMUM_TOTAL_SIZE,
96 OID_GEN_MEDIA_CONNECT_STATUS,
97 OID_GEN_PHYSICAL_MEDIUM,
98#if 0
99 OID_GEN_RNDIS_CONFIG_PARAMETER,
100#endif
David Brownell7e27f182006-06-13 09:54:40 -0700101
David Brownell340600a2005-04-28 13:45:25 -0700102 /* the statistical stuff */
103 OID_GEN_XMIT_OK,
104 OID_GEN_RCV_OK,
105 OID_GEN_XMIT_ERROR,
106 OID_GEN_RCV_ERROR,
107 OID_GEN_RCV_NO_BUFFER,
108#ifdef RNDIS_OPTIONAL_STATS
109 OID_GEN_DIRECTED_BYTES_XMIT,
110 OID_GEN_DIRECTED_FRAMES_XMIT,
111 OID_GEN_MULTICAST_BYTES_XMIT,
112 OID_GEN_MULTICAST_FRAMES_XMIT,
113 OID_GEN_BROADCAST_BYTES_XMIT,
114 OID_GEN_BROADCAST_FRAMES_XMIT,
115 OID_GEN_DIRECTED_BYTES_RCV,
116 OID_GEN_DIRECTED_FRAMES_RCV,
117 OID_GEN_MULTICAST_BYTES_RCV,
118 OID_GEN_MULTICAST_FRAMES_RCV,
119 OID_GEN_BROADCAST_BYTES_RCV,
120 OID_GEN_BROADCAST_FRAMES_RCV,
121 OID_GEN_RCV_CRC_ERROR,
122 OID_GEN_TRANSMIT_QUEUE_LENGTH,
123#endif /* RNDIS_OPTIONAL_STATS */
124
David Brownell7e27f182006-06-13 09:54:40 -0700125 /* mandatory 802.3 */
David Brownell340600a2005-04-28 13:45:25 -0700126 /* the general stuff */
127 OID_802_3_PERMANENT_ADDRESS,
128 OID_802_3_CURRENT_ADDRESS,
129 OID_802_3_MULTICAST_LIST,
130 OID_802_3_MAC_OPTIONS,
131 OID_802_3_MAXIMUM_LIST_SIZE,
David Brownell7e27f182006-06-13 09:54:40 -0700132
David Brownell340600a2005-04-28 13:45:25 -0700133 /* the statistical stuff */
134 OID_802_3_RCV_ERROR_ALIGNMENT,
135 OID_802_3_XMIT_ONE_COLLISION,
136 OID_802_3_XMIT_MORE_COLLISIONS,
137#ifdef RNDIS_OPTIONAL_STATS
138 OID_802_3_XMIT_DEFERRED,
139 OID_802_3_XMIT_MAX_COLLISIONS,
140 OID_802_3_RCV_OVERRUN,
141 OID_802_3_XMIT_UNDERRUN,
142 OID_802_3_XMIT_HEARTBEAT_FAILURE,
143 OID_802_3_XMIT_TIMES_CRS_LOST,
144 OID_802_3_XMIT_LATE_COLLISIONS,
145#endif /* RNDIS_OPTIONAL_STATS */
146
147#ifdef RNDIS_PM
148 /* PM and wakeup are mandatory for USB: */
149
150 /* power management */
151 OID_PNP_CAPABILITIES,
152 OID_PNP_QUERY_POWER,
153 OID_PNP_SET_POWER,
154
155#ifdef RNDIS_WAKEUP
156 /* wake up host */
157 OID_PNP_ENABLE_WAKE_UP,
158 OID_PNP_ADD_WAKE_UP_PATTERN,
159 OID_PNP_REMOVE_WAKE_UP_PATTERN,
160#endif /* RNDIS_WAKEUP */
161#endif /* RNDIS_PM */
162};
163
164
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165/* NDIS Functions */
David Brownell340600a2005-04-28 13:45:25 -0700166static int
167gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
168 rndis_resp_t *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169{
David Brownell7e27f182006-06-13 09:54:40 -0700170 int retval = -ENOTSUPP;
171 u32 length = 4; /* usually */
David Brownell340600a2005-04-28 13:45:25 -0700172 __le32 *outbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 int i, count;
174 rndis_query_cmplt_type *resp;
175
176 if (!r) return -ENOMEM;
177 resp = (rndis_query_cmplt_type *) r->buf;
178
179 if (!resp) return -ENOMEM;
David Brownell340600a2005-04-28 13:45:25 -0700180
181 if (buf_len && rndis_debug > 1) {
David Brownell70790f62007-07-01 17:35:28 -0700182 DBG("query OID %08x value, len %d:\n", OID, buf_len);
David Brownell340600a2005-04-28 13:45:25 -0700183 for (i = 0; i < buf_len; i += 16) {
David Brownell70790f62007-07-01 17:35:28 -0700184 DBG("%03d: %08x %08x %08x %08x\n", i,
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700185 get_unaligned_le32(&buf[i]),
186 get_unaligned_le32(&buf[i + 4]),
187 get_unaligned_le32(&buf[i + 8]),
188 get_unaligned_le32(&buf[i + 12]));
David Brownell340600a2005-04-28 13:45:25 -0700189 }
190 }
191
192 /* response goes here, right after the header */
193 outbuf = (__le32 *) &resp[1];
194 resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 switch (OID) {
197
198 /* general oids (table 4-1) */
199
200 /* mandatory */
201 case OID_GEN_SUPPORTED_LIST:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800202 DBG("%s: OID_GEN_SUPPORTED_LIST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 length = sizeof (oid_supported_list);
204 count = length / sizeof (u32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 for (i = 0; i < count; i++)
David Brownell340600a2005-04-28 13:45:25 -0700206 outbuf[i] = cpu_to_le32 (oid_supported_list[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 retval = 0;
208 break;
David Brownell7e27f182006-06-13 09:54:40 -0700209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 /* mandatory */
211 case OID_GEN_HARDWARE_STATUS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800212 DBG("%s: OID_GEN_HARDWARE_STATUS\n", __func__);
David Brownell7e27f182006-06-13 09:54:40 -0700213 /* Bogus question!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 * Hardware must be ready to receive high level protocols.
David Brownell7e27f182006-06-13 09:54:40 -0700215 * BTW:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 * reddite ergo quae sunt Caesaris Caesari
217 * et quae sunt Dei Deo!
218 */
David Brownell340600a2005-04-28 13:45:25 -0700219 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 retval = 0;
221 break;
David Brownell7e27f182006-06-13 09:54:40 -0700222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 /* mandatory */
224 case OID_GEN_MEDIA_SUPPORTED:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800225 DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700226 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 retval = 0;
228 break;
David Brownell7e27f182006-06-13 09:54:40 -0700229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 /* mandatory */
231 case OID_GEN_MEDIA_IN_USE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800232 DBG("%s: OID_GEN_MEDIA_IN_USE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 /* one medium, one transport... (maybe you do it better) */
David Brownell340600a2005-04-28 13:45:25 -0700234 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 retval = 0;
236 break;
David Brownell7e27f182006-06-13 09:54:40 -0700237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 /* mandatory */
239 case OID_GEN_MAXIMUM_FRAME_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800240 DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 if (rndis_per_dev_params [configNr].dev) {
David Brownell340600a2005-04-28 13:45:25 -0700242 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 rndis_per_dev_params [configNr].dev->mtu);
244 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 }
246 break;
David Brownell7e27f182006-06-13 09:54:40 -0700247
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 /* mandatory */
249 case OID_GEN_LINK_SPEED:
David Brownell340600a2005-04-28 13:45:25 -0700250 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800251 DBG("%s: OID_GEN_LINK_SPEED\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 if (rndis_per_dev_params [configNr].media_state
David Brownell340600a2005-04-28 13:45:25 -0700253 == NDIS_MEDIA_STATE_DISCONNECTED)
254 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 else
David Brownell340600a2005-04-28 13:45:25 -0700256 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 rndis_per_dev_params [configNr].speed);
258 retval = 0;
259 break;
260
261 /* mandatory */
262 case OID_GEN_TRANSMIT_BLOCK_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800263 DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 if (rndis_per_dev_params [configNr].dev) {
David Brownell340600a2005-04-28 13:45:25 -0700265 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 rndis_per_dev_params [configNr].dev->mtu);
267 retval = 0;
268 }
269 break;
David Brownell7e27f182006-06-13 09:54:40 -0700270
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 /* mandatory */
272 case OID_GEN_RECEIVE_BLOCK_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800273 DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 if (rndis_per_dev_params [configNr].dev) {
David Brownell340600a2005-04-28 13:45:25 -0700275 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 rndis_per_dev_params [configNr].dev->mtu);
277 retval = 0;
278 }
279 break;
David Brownell7e27f182006-06-13 09:54:40 -0700280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 /* mandatory */
282 case OID_GEN_VENDOR_ID:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800283 DBG("%s: OID_GEN_VENDOR_ID\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700284 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 rndis_per_dev_params [configNr].vendorID);
286 retval = 0;
287 break;
David Brownell7e27f182006-06-13 09:54:40 -0700288
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 /* mandatory */
290 case OID_GEN_VENDOR_DESCRIPTION:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800291 DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 length = strlen (rndis_per_dev_params [configNr].vendorDescr);
David Brownell340600a2005-04-28 13:45:25 -0700293 memcpy (outbuf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 rndis_per_dev_params [configNr].vendorDescr, length);
295 retval = 0;
296 break;
297
298 case OID_GEN_VENDOR_DRIVER_VERSION:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800299 DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 /* Created as LE */
David Brownell340600a2005-04-28 13:45:25 -0700301 *outbuf = rndis_driver_version;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 retval = 0;
303 break;
304
305 /* mandatory */
306 case OID_GEN_CURRENT_PACKET_FILTER:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800307 DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700308 *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 retval = 0;
310 break;
311
312 /* mandatory */
313 case OID_GEN_MAXIMUM_TOTAL_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800314 DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700315 *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 retval = 0;
317 break;
318
319 /* mandatory */
320 case OID_GEN_MEDIA_CONNECT_STATUS:
David Brownell340600a2005-04-28 13:45:25 -0700321 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800322 DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700323 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 .media_state);
325 retval = 0;
326 break;
327
328 case OID_GEN_PHYSICAL_MEDIUM:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800329 DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700330 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 retval = 0;
332 break;
333
334 /* The RNDIS specification is incomplete/wrong. Some versions
335 * of MS-Windows expect OIDs that aren't specified there. Other
336 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
337 */
338 case OID_GEN_MAC_OPTIONS: /* from WinME */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800339 DBG("%s: OID_GEN_MAC_OPTIONS\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700340 *outbuf = __constant_cpu_to_le32(
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 NDIS_MAC_OPTION_RECEIVE_SERIALIZED
342 | NDIS_MAC_OPTION_FULL_DUPLEX);
343 retval = 0;
344 break;
345
346 /* statistics OIDs (table 4-2) */
347
348 /* mandatory */
349 case OID_GEN_XMIT_OK:
David Brownell340600a2005-04-28 13:45:25 -0700350 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800351 DBG("%s: OID_GEN_XMIT_OK\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700353 *outbuf = cpu_to_le32 (
David Brownell7e27f182006-06-13 09:54:40 -0700354 rndis_per_dev_params [configNr].stats->tx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 rndis_per_dev_params [configNr].stats->tx_errors -
356 rndis_per_dev_params [configNr].stats->tx_dropped);
357 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 }
359 break;
360
361 /* mandatory */
362 case OID_GEN_RCV_OK:
David Brownell340600a2005-04-28 13:45:25 -0700363 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800364 DBG("%s: OID_GEN_RCV_OK\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700366 *outbuf = cpu_to_le32 (
David Brownell7e27f182006-06-13 09:54:40 -0700367 rndis_per_dev_params [configNr].stats->rx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 rndis_per_dev_params [configNr].stats->rx_errors -
369 rndis_per_dev_params [configNr].stats->rx_dropped);
370 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 }
372 break;
David Brownell7e27f182006-06-13 09:54:40 -0700373
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 /* mandatory */
375 case OID_GEN_XMIT_ERROR:
David Brownell340600a2005-04-28 13:45:25 -0700376 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800377 DBG("%s: OID_GEN_XMIT_ERROR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700379 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 .stats->tx_errors);
381 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
383 break;
David Brownell7e27f182006-06-13 09:54:40 -0700384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 /* mandatory */
386 case OID_GEN_RCV_ERROR:
David Brownell340600a2005-04-28 13:45:25 -0700387 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800388 DBG("%s: OID_GEN_RCV_ERROR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700390 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 .stats->rx_errors);
392 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 }
394 break;
David Brownell7e27f182006-06-13 09:54:40 -0700395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 /* mandatory */
397 case OID_GEN_RCV_NO_BUFFER:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800398 DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700400 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 .stats->rx_dropped);
402 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 }
404 break;
405
406#ifdef RNDIS_OPTIONAL_STATS
407 case OID_GEN_DIRECTED_BYTES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800408 DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__);
David Brownell7e27f182006-06-13 09:54:40 -0700409 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 * Aunt Tilly's size of shoes
411 * minus antarctica count of penguins
412 * divided by weight of Alpha Centauri
413 */
414 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700415 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 (rndis_per_dev_params [configNr]
David Brownell7e27f182006-06-13 09:54:40 -0700417 .stats->tx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 rndis_per_dev_params [configNr]
419 .stats->tx_errors -
420 rndis_per_dev_params [configNr]
421 .stats->tx_dropped)
422 * 123);
423 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 }
425 break;
David Brownell7e27f182006-06-13 09:54:40 -0700426
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 case OID_GEN_DIRECTED_FRAMES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800428 DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 /* dito */
430 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700431 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 (rndis_per_dev_params [configNr]
David Brownell7e27f182006-06-13 09:54:40 -0700433 .stats->tx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 rndis_per_dev_params [configNr]
435 .stats->tx_errors -
436 rndis_per_dev_params [configNr]
437 .stats->tx_dropped)
438 / 123);
439 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 }
441 break;
David Brownell7e27f182006-06-13 09:54:40 -0700442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 case OID_GEN_MULTICAST_BYTES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800444 DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700446 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 .stats->multicast*1234);
448 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450 break;
David Brownell7e27f182006-06-13 09:54:40 -0700451
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 case OID_GEN_MULTICAST_FRAMES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800453 DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700455 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 .stats->multicast);
457 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 }
459 break;
David Brownell7e27f182006-06-13 09:54:40 -0700460
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 case OID_GEN_BROADCAST_BYTES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800462 DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700464 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 .stats->tx_packets/42*255);
466 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
468 break;
David Brownell7e27f182006-06-13 09:54:40 -0700469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 case OID_GEN_BROADCAST_FRAMES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800471 DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700473 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 .stats->tx_packets/42);
475 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 }
477 break;
David Brownell7e27f182006-06-13 09:54:40 -0700478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 case OID_GEN_DIRECTED_BYTES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800480 DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700481 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 retval = 0;
483 break;
David Brownell7e27f182006-06-13 09:54:40 -0700484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 case OID_GEN_DIRECTED_FRAMES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800486 DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700487 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 retval = 0;
489 break;
David Brownell7e27f182006-06-13 09:54:40 -0700490
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 case OID_GEN_MULTICAST_BYTES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800492 DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700494 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 .stats->multicast * 1111);
496 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 }
498 break;
David Brownell7e27f182006-06-13 09:54:40 -0700499
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 case OID_GEN_MULTICAST_FRAMES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800501 DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700503 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 .stats->multicast);
505 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 }
507 break;
David Brownell7e27f182006-06-13 09:54:40 -0700508
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 case OID_GEN_BROADCAST_BYTES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800510 DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700512 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 .stats->rx_packets/42*255);
514 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 }
516 break;
David Brownell7e27f182006-06-13 09:54:40 -0700517
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 case OID_GEN_BROADCAST_FRAMES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800519 DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700521 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 .stats->rx_packets/42);
523 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 }
525 break;
David Brownell7e27f182006-06-13 09:54:40 -0700526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 case OID_GEN_RCV_CRC_ERROR:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800528 DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700530 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 .stats->rx_crc_errors);
532 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 }
534 break;
David Brownell7e27f182006-06-13 09:54:40 -0700535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 case OID_GEN_TRANSMIT_QUEUE_LENGTH:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800537 DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700538 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 retval = 0;
540 break;
541#endif /* RNDIS_OPTIONAL_STATS */
542
543 /* ieee802.3 OIDs (table 4-3) */
544
545 /* mandatory */
546 case OID_802_3_PERMANENT_ADDRESS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800547 DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 if (rndis_per_dev_params [configNr].dev) {
549 length = ETH_ALEN;
David Brownell340600a2005-04-28 13:45:25 -0700550 memcpy (outbuf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 rndis_per_dev_params [configNr].host_mac,
552 length);
553 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 }
555 break;
David Brownell7e27f182006-06-13 09:54:40 -0700556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 /* mandatory */
558 case OID_802_3_CURRENT_ADDRESS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800559 DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 if (rndis_per_dev_params [configNr].dev) {
561 length = ETH_ALEN;
David Brownell340600a2005-04-28 13:45:25 -0700562 memcpy (outbuf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 rndis_per_dev_params [configNr].host_mac,
564 length);
565 retval = 0;
566 }
567 break;
David Brownell7e27f182006-06-13 09:54:40 -0700568
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 /* mandatory */
570 case OID_802_3_MULTICAST_LIST:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800571 DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 /* Multicast base address only */
David Brownell340600a2005-04-28 13:45:25 -0700573 *outbuf = __constant_cpu_to_le32 (0xE0000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 retval = 0;
575 break;
David Brownell7e27f182006-06-13 09:54:40 -0700576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 /* mandatory */
578 case OID_802_3_MAXIMUM_LIST_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800579 DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 /* Multicast base address only */
David Brownell340600a2005-04-28 13:45:25 -0700581 *outbuf = __constant_cpu_to_le32 (1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 retval = 0;
583 break;
David Brownell7e27f182006-06-13 09:54:40 -0700584
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 case OID_802_3_MAC_OPTIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800586 DBG("%s: OID_802_3_MAC_OPTIONS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 break;
588
589 /* ieee802.3 statistics OIDs (table 4-4) */
590
591 /* mandatory */
592 case OID_802_3_RCV_ERROR_ALIGNMENT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800593 DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700594 if (rndis_per_dev_params [configNr].stats) {
595 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 .stats->rx_frame_errors);
597 retval = 0;
598 }
599 break;
David Brownell7e27f182006-06-13 09:54:40 -0700600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 /* mandatory */
602 case OID_802_3_XMIT_ONE_COLLISION:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800603 DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700604 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 retval = 0;
606 break;
David Brownell7e27f182006-06-13 09:54:40 -0700607
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 /* mandatory */
609 case OID_802_3_XMIT_MORE_COLLISIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800610 DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700611 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 retval = 0;
613 break;
David Brownell7e27f182006-06-13 09:54:40 -0700614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615#ifdef RNDIS_OPTIONAL_STATS
616 case OID_802_3_XMIT_DEFERRED:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800617 DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /* TODO */
619 break;
David Brownell7e27f182006-06-13 09:54:40 -0700620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 case OID_802_3_XMIT_MAX_COLLISIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800622 DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 /* TODO */
624 break;
David Brownell7e27f182006-06-13 09:54:40 -0700625
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 case OID_802_3_RCV_OVERRUN:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800627 DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 /* TODO */
629 break;
David Brownell7e27f182006-06-13 09:54:40 -0700630
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 case OID_802_3_XMIT_UNDERRUN:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800632 DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 /* TODO */
634 break;
David Brownell7e27f182006-06-13 09:54:40 -0700635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 case OID_802_3_XMIT_HEARTBEAT_FAILURE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800637 DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 /* TODO */
639 break;
David Brownell7e27f182006-06-13 09:54:40 -0700640
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 case OID_802_3_XMIT_TIMES_CRS_LOST:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800642 DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 /* TODO */
644 break;
David Brownell7e27f182006-06-13 09:54:40 -0700645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 case OID_802_3_XMIT_LATE_COLLISIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800647 DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 /* TODO */
David Brownell7e27f182006-06-13 09:54:40 -0700649 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650#endif /* RNDIS_OPTIONAL_STATS */
651
652#ifdef RNDIS_PM
653 /* power management OIDs (table 4-5) */
654 case OID_PNP_CAPABILITIES:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800655 DBG("%s: OID_PNP_CAPABILITIES\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
David Brownell340600a2005-04-28 13:45:25 -0700657 /* for now, no wakeup capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 length = sizeof (struct NDIS_PNP_CAPABILITIES);
David Brownell340600a2005-04-28 13:45:25 -0700659 memset(outbuf, 0, length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 retval = 0;
661 break;
662 case OID_PNP_QUERY_POWER:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800663 DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__,
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700664 get_unaligned_le32(buf) - 1);
David Brownell340600a2005-04-28 13:45:25 -0700665 /* only suspend is a real power state, and
666 * it can't be entered by OID_PNP_SET_POWER...
667 */
668 length = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 retval = 0;
670 break;
671#endif
672
673 default:
David Brownell00274922007-11-19 12:58:36 -0800674 pr_warning("%s: query unknown OID 0x%08X\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800675 __func__, OID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 }
David Brownell340600a2005-04-28 13:45:25 -0700677 if (retval < 0)
678 length = 0;
David Brownell7e27f182006-06-13 09:54:40 -0700679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 resp->InformationBufferLength = cpu_to_le32 (length);
David Brownell340600a2005-04-28 13:45:25 -0700681 r->length = length + sizeof *resp;
682 resp->MessageLength = cpu_to_le32 (r->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return retval;
684}
685
David Brownell7e27f182006-06-13 09:54:40 -0700686static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
687 rndis_resp_t *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
689 rndis_set_cmplt_type *resp;
David Brownell7e27f182006-06-13 09:54:40 -0700690 int i, retval = -ENOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 struct rndis_params *params;
692
693 if (!r)
694 return -ENOMEM;
695 resp = (rndis_set_cmplt_type *) r->buf;
696 if (!resp)
697 return -ENOMEM;
698
David Brownell340600a2005-04-28 13:45:25 -0700699 if (buf_len && rndis_debug > 1) {
David Brownell70790f62007-07-01 17:35:28 -0700700 DBG("set OID %08x value, len %d:\n", OID, buf_len);
David Brownell340600a2005-04-28 13:45:25 -0700701 for (i = 0; i < buf_len; i += 16) {
David Brownell70790f62007-07-01 17:35:28 -0700702 DBG("%03d: %08x %08x %08x %08x\n", i,
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700703 get_unaligned_le32(&buf[i]),
704 get_unaligned_le32(&buf[i + 4]),
705 get_unaligned_le32(&buf[i + 8]),
706 get_unaligned_le32(&buf[i + 12]));
David Brownell340600a2005-04-28 13:45:25 -0700707 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
709
David Brownell340600a2005-04-28 13:45:25 -0700710 params = &rndis_per_dev_params [configNr];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 switch (OID) {
712 case OID_GEN_CURRENT_PACKET_FILTER:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
David Brownell340600a2005-04-28 13:45:25 -0700714 /* these NDIS_PACKET_TYPE_* bitflags are shared with
715 * cdc_filter; it's not RNDIS-specific
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
717 * PROMISCUOUS, DIRECTED,
718 * MULTICAST, ALL_MULTICAST, BROADCAST
719 */
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700720 *params->filter = (u16)get_unaligned_le32(buf);
David Brownell70790f62007-07-01 17:35:28 -0700721 DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800722 __func__, *params->filter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 /* this call has a significant side effect: it's
725 * what makes the packet flow start and stop, like
726 * activating the CDC Ethernet altsetting.
727 */
David Brownell340600a2005-04-28 13:45:25 -0700728#ifdef RNDIS_PM
729update_linkstate:
730#endif
731 retval = 0;
732 if (*params->filter) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 params->state = RNDIS_DATA_INITIALIZED;
734 netif_carrier_on(params->dev);
735 if (netif_running(params->dev))
736 netif_wake_queue (params->dev);
737 } else {
738 params->state = RNDIS_INITIALIZED;
739 netif_carrier_off (params->dev);
740 netif_stop_queue (params->dev);
741 }
742 break;
David Brownell7e27f182006-06-13 09:54:40 -0700743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 case OID_802_3_MULTICAST_LIST:
David Brownell7e27f182006-06-13 09:54:40 -0700745 /* I think we can ignore this */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800746 DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 retval = 0;
748 break;
749#if 0
750 case OID_GEN_RNDIS_CONFIG_PARAMETER:
751 {
752 struct rndis_config_parameter *param;
753 param = (struct rndis_config_parameter *) buf;
David Brownell70790f62007-07-01 17:35:28 -0700754 DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800755 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 min(cpu_to_le32(param->ParameterNameLength),80),
757 buf + param->ParameterNameOffset);
758 retval = 0;
759 }
760 break;
761#endif
762
763#ifdef RNDIS_PM
764 case OID_PNP_SET_POWER:
David Brownell340600a2005-04-28 13:45:25 -0700765 /* The only real power state is USB suspend, and RNDIS requests
766 * can't enter it; this one isn't really about power. After
767 * resuming, Windows forces a reset, and then SET_POWER D0.
768 * FIXME ... then things go batty; Windows wedges itself.
769 */
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700770 i = get_unaligned_le32(buf);
Harvey Harrison441b62c2008-03-03 16:08:34 -0800771 DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1);
David Brownell340600a2005-04-28 13:45:25 -0700772 switch (i) {
773 case NdisDeviceStateD0:
774 *params->filter = params->saved_filter;
775 goto update_linkstate;
776 case NdisDeviceStateD3:
777 case NdisDeviceStateD2:
778 case NdisDeviceStateD1:
779 params->saved_filter = *params->filter;
780 retval = 0;
781 break;
782 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 break;
784
David Brownell340600a2005-04-28 13:45:25 -0700785#ifdef RNDIS_WAKEUP
786 // no wakeup support advertised, so wakeup OIDs always fail:
787 // - OID_PNP_ENABLE_WAKE_UP
788 // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789#endif
790
David Brownell340600a2005-04-28 13:45:25 -0700791#endif /* RNDIS_PM */
792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 default:
David Brownell00274922007-11-19 12:58:36 -0800794 pr_warning("%s: set unknown OID 0x%08X, size %d\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800795 __func__, OID, buf_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 }
David Brownell7e27f182006-06-13 09:54:40 -0700797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 return retval;
799}
800
David Brownell7e27f182006-06-13 09:54:40 -0700801/*
802 * Response Functions
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 */
804
805static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
806{
David Brownell7e27f182006-06-13 09:54:40 -0700807 rndis_init_cmplt_type *resp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
David Brownell7e27f182006-06-13 09:54:40 -0700811
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700813 if (!r)
814 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 resp = (rndis_init_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700816
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 resp->MessageType = __constant_cpu_to_le32 (
818 REMOTE_NDIS_INITIALIZE_CMPLT);
819 resp->MessageLength = __constant_cpu_to_le32 (52);
820 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
821 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
822 resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION);
823 resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION);
824 resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS);
825 resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3);
826 resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1);
827 resp->MaxTransferSize = cpu_to_le32 (
828 rndis_per_dev_params [configNr].dev->mtu
829 + sizeof (struct ethhdr)
830 + sizeof (struct rndis_packet_msg_type)
831 + 22);
832 resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0);
833 resp->AFListOffset = __constant_cpu_to_le32 (0);
834 resp->AFListSize = __constant_cpu_to_le32 (0);
David Brownell7e27f182006-06-13 09:54:40 -0700835
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700837 rndis_per_dev_params [configNr].ack (
838 rndis_per_dev_params [configNr].dev);
839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 return 0;
841}
842
843static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
844{
845 rndis_query_cmplt_type *resp;
846 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700847
Harvey Harrison441b62c2008-03-03 16:08:34 -0800848 // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
David Brownell7e27f182006-06-13 09:54:40 -0700850
Shaun Tancheff87637162006-02-22 19:47:19 -0800851 /*
852 * we need more memory:
853 * gen_ndis_query_resp expects enough space for
854 * rndis_query_cmplt_type followed by data.
855 * oid_supported_list is the largest data reply
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 */
Shaun Tancheff87637162006-02-22 19:47:19 -0800857 r = rndis_add_response (configNr,
858 sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700859 if (!r)
860 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 resp = (rndis_query_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
David Brownell7e27f182006-06-13 09:54:40 -0700865
David Brownell340600a2005-04-28 13:45:25 -0700866 if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
867 le32_to_cpu(buf->InformationBufferOffset)
868 + 8 + (u8 *) buf,
869 le32_to_cpu(buf->InformationBufferLength),
870 r)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 /* OID not supported */
872 resp->Status = __constant_cpu_to_le32 (
873 RNDIS_STATUS_NOT_SUPPORTED);
David Brownell340600a2005-04-28 13:45:25 -0700874 resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 resp->InformationBufferLength = __constant_cpu_to_le32 (0);
876 resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
877 } else
878 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
David Brownell7e27f182006-06-13 09:54:40 -0700879
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700881 rndis_per_dev_params [configNr].ack (
882 rndis_per_dev_params [configNr].dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 return 0;
884}
885
886static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
887{
888 u32 BufLength, BufOffset;
889 rndis_set_cmplt_type *resp;
890 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700891
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700893 if (!r)
894 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 resp = (rndis_set_cmplt_type *) r->buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
897 BufLength = le32_to_cpu (buf->InformationBufferLength);
898 BufOffset = le32_to_cpu (buf->InformationBufferOffset);
899
900#ifdef VERBOSE
Harvey Harrison441b62c2008-03-03 16:08:34 -0800901 DBG("%s: Length: %d\n", __func__, BufLength);
902 DBG("%s: Offset: %d\n", __func__, BufOffset);
903 DBG("%s: InfoBuffer: ", __func__);
David Brownell7e27f182006-06-13 09:54:40 -0700904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 for (i = 0; i < BufLength; i++) {
David Brownell70790f62007-07-01 17:35:28 -0700906 DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 }
David Brownell7e27f182006-06-13 09:54:40 -0700908
David Brownell70790f62007-07-01 17:35:28 -0700909 DBG("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910#endif
David Brownell7e27f182006-06-13 09:54:40 -0700911
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
913 resp->MessageLength = __constant_cpu_to_le32 (16);
914 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
David Brownell7e27f182006-06-13 09:54:40 -0700915 if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
916 ((u8 *) buf) + 8 + BufOffset, BufLength, r))
917 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
918 else
919 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
920
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700922 rndis_per_dev_params [configNr].ack (
923 rndis_per_dev_params [configNr].dev);
924
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 return 0;
926}
927
928static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
929{
930 rndis_reset_cmplt_type *resp;
931 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700932
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700934 if (!r)
935 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 resp = (rndis_reset_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700937
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
939 resp->MessageLength = __constant_cpu_to_le32 (16);
940 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
941 /* resent information */
942 resp->AddressingReset = __constant_cpu_to_le32 (1);
David Brownell7e27f182006-06-13 09:54:40 -0700943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700945 rndis_per_dev_params [configNr].ack (
946 rndis_per_dev_params [configNr].dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947
948 return 0;
949}
950
951static int rndis_keepalive_response (int configNr,
David Brownell7e27f182006-06-13 09:54:40 -0700952 rndis_keepalive_msg_type *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953{
954 rndis_keepalive_cmplt_type *resp;
955 rndis_resp_t *r;
956
957 /* host "should" check only in RNDIS_DATA_INITIALIZED state */
958
959 r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700960 if (!r)
961 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 resp = (rndis_keepalive_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700963
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 resp->MessageType = __constant_cpu_to_le32 (
965 REMOTE_NDIS_KEEPALIVE_CMPLT);
966 resp->MessageLength = __constant_cpu_to_le32 (16);
967 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
968 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
David Brownell7e27f182006-06-13 09:54:40 -0700969
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700971 rndis_per_dev_params [configNr].ack (
972 rndis_per_dev_params [configNr].dev);
973
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 return 0;
975}
976
977
David Brownell7e27f182006-06-13 09:54:40 -0700978/*
979 * Device to Host Comunication
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 */
981static int rndis_indicate_status_msg (int configNr, u32 status)
982{
David Brownell7e27f182006-06-13 09:54:40 -0700983 rndis_indicate_status_msg_type *resp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700985
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
David Brownell7e27f182006-06-13 09:54:40 -0700987 return -ENOTSUPP;
988
989 r = rndis_add_response (configNr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 sizeof (rndis_indicate_status_msg_type));
David Brownell340600a2005-04-28 13:45:25 -0700991 if (!r)
992 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 resp = (rndis_indicate_status_msg_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700994
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 resp->MessageType = __constant_cpu_to_le32 (
996 REMOTE_NDIS_INDICATE_STATUS_MSG);
997 resp->MessageLength = __constant_cpu_to_le32 (20);
998 resp->Status = cpu_to_le32 (status);
999 resp->StatusBufferLength = __constant_cpu_to_le32 (0);
1000 resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
David Brownell7e27f182006-06-13 09:54:40 -07001001
1002 if (rndis_per_dev_params [configNr].ack)
1003 rndis_per_dev_params [configNr].ack (
1004 rndis_per_dev_params [configNr].dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 return 0;
1006}
1007
1008int rndis_signal_connect (int configNr)
1009{
1010 rndis_per_dev_params [configNr].media_state
1011 = NDIS_MEDIA_STATE_CONNECTED;
David Brownell7e27f182006-06-13 09:54:40 -07001012 return rndis_indicate_status_msg (configNr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 RNDIS_STATUS_MEDIA_CONNECT);
1014}
1015
1016int rndis_signal_disconnect (int configNr)
1017{
1018 rndis_per_dev_params [configNr].media_state
1019 = NDIS_MEDIA_STATE_DISCONNECTED;
1020 return rndis_indicate_status_msg (configNr,
1021 RNDIS_STATUS_MEDIA_DISCONNECT);
1022}
1023
David Brownell340600a2005-04-28 13:45:25 -07001024void rndis_uninit (int configNr)
1025{
David Brownell486e2df2005-05-24 17:51:52 -07001026 u8 *buf;
1027 u32 length;
1028
David Brownell340600a2005-04-28 13:45:25 -07001029 if (configNr >= RNDIS_MAX_CONFIGS)
1030 return;
1031 rndis_per_dev_params [configNr].used = 0;
1032 rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
David Brownell486e2df2005-05-24 17:51:52 -07001033
1034 /* drain the response queue */
1035 while ((buf = rndis_get_next_response(configNr, &length)))
1036 rndis_free_response(configNr, buf);
David Brownell340600a2005-04-28 13:45:25 -07001037}
1038
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039void rndis_set_host_mac (int configNr, const u8 *addr)
1040{
1041 rndis_per_dev_params [configNr].host_mac = addr;
1042}
1043
David Brownell7e27f182006-06-13 09:54:40 -07001044/*
1045 * Message Parser
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 */
1047int rndis_msg_parser (u8 configNr, u8 *buf)
1048{
1049 u32 MsgType, MsgLength;
1050 __le32 *tmp;
1051 struct rndis_params *params;
David Brownell7e27f182006-06-13 09:54:40 -07001052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 if (!buf)
1054 return -ENOMEM;
David Brownell7e27f182006-06-13 09:54:40 -07001055
1056 tmp = (__le32 *) buf;
Harvey Harrisona5abdea2008-04-29 01:03:40 -07001057 MsgType = get_unaligned_le32(tmp++);
1058 MsgLength = get_unaligned_le32(tmp++);
David Brownell7e27f182006-06-13 09:54:40 -07001059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 if (configNr >= RNDIS_MAX_CONFIGS)
1061 return -ENOTSUPP;
1062 params = &rndis_per_dev_params [configNr];
David Brownell7e27f182006-06-13 09:54:40 -07001063
David Brownell340600a2005-04-28 13:45:25 -07001064 /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
1065 * rx/tx statistics and link status, in addition to KEEPALIVE traffic
1066 * and normal HC level polling to see if there's any IN traffic.
1067 */
1068
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 /* For USB: responses may take up to 10 seconds */
David Brownell340600a2005-04-28 13:45:25 -07001070 switch (MsgType) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 case REMOTE_NDIS_INITIALIZE_MSG:
David Brownell70790f62007-07-01 17:35:28 -07001072 DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001073 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 params->state = RNDIS_INITIALIZED;
1075 return rndis_init_response (configNr,
David Brownell7e27f182006-06-13 09:54:40 -07001076 (rndis_init_msg_type *) buf);
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 case REMOTE_NDIS_HALT_MSG:
David Brownell70790f62007-07-01 17:35:28 -07001079 DBG("%s: REMOTE_NDIS_HALT_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001080 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 params->state = RNDIS_UNINITIALIZED;
1082 if (params->dev) {
1083 netif_carrier_off (params->dev);
1084 netif_stop_queue (params->dev);
1085 }
1086 return 0;
David Brownell7e27f182006-06-13 09:54:40 -07001087
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 case REMOTE_NDIS_QUERY_MSG:
David Brownell7e27f182006-06-13 09:54:40 -07001089 return rndis_query_response (configNr,
1090 (rndis_query_msg_type *) buf);
1091
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 case REMOTE_NDIS_SET_MSG:
David Brownell7e27f182006-06-13 09:54:40 -07001093 return rndis_set_response (configNr,
1094 (rndis_set_msg_type *) buf);
1095
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 case REMOTE_NDIS_RESET_MSG:
David Brownell70790f62007-07-01 17:35:28 -07001097 DBG("%s: REMOTE_NDIS_RESET_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001098 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 return rndis_reset_response (configNr,
David Brownell7e27f182006-06-13 09:54:40 -07001100 (rndis_reset_msg_type *) buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
1102 case REMOTE_NDIS_KEEPALIVE_MSG:
1103 /* For USB: host does this every 5 seconds */
David Brownell340600a2005-04-28 13:45:25 -07001104 if (rndis_debug > 1)
David Brownell70790f62007-07-01 17:35:28 -07001105 DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001106 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 return rndis_keepalive_response (configNr,
David Brownell7e27f182006-06-13 09:54:40 -07001108 (rndis_keepalive_msg_type *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 buf);
David Brownell7e27f182006-06-13 09:54:40 -07001110
1111 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 /* At least Windows XP emits some undefined RNDIS messages.
1113 * In one case those messages seemed to relate to the host
1114 * suspending itself.
1115 */
David Brownell00274922007-11-19 12:58:36 -08001116 pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001117 __func__ , MsgType, MsgLength);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 {
1119 unsigned i;
1120 for (i = 0; i < MsgLength; i += 16) {
David Brownell70790f62007-07-01 17:35:28 -07001121 DBG("%03d: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 " %02x %02x %02x %02x"
1123 " %02x %02x %02x %02x"
1124 " %02x %02x %02x %02x"
1125 " %02x %02x %02x %02x"
1126 "\n",
1127 i,
1128 buf[i], buf [i+1],
1129 buf[i+2], buf[i+3],
1130 buf[i+4], buf [i+5],
1131 buf[i+6], buf[i+7],
1132 buf[i+8], buf [i+9],
1133 buf[i+10], buf[i+11],
1134 buf[i+12], buf [i+13],
1135 buf[i+14], buf[i+15]);
1136 }
1137 }
1138 break;
1139 }
David Brownell7e27f182006-06-13 09:54:40 -07001140
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 return -ENOTSUPP;
1142}
1143
1144int rndis_register (int (* rndis_control_ack) (struct net_device *))
1145{
1146 u8 i;
David Brownell7e27f182006-06-13 09:54:40 -07001147
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
1149 if (!rndis_per_dev_params [i].used) {
1150 rndis_per_dev_params [i].used = 1;
1151 rndis_per_dev_params [i].ack = rndis_control_ack;
Harvey Harrison441b62c2008-03-03 16:08:34 -08001152 DBG("%s: configNr = %d\n", __func__, i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 return i;
1154 }
1155 }
David Brownell70790f62007-07-01 17:35:28 -07001156 DBG("failed\n");
David Brownell7e27f182006-06-13 09:54:40 -07001157
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 return -1;
1159}
1160
1161void rndis_deregister (int configNr)
1162{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001163 DBG("%s: \n", __func__ );
David Brownell7e27f182006-06-13 09:54:40 -07001164
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 if (configNr >= RNDIS_MAX_CONFIGS) return;
1166 rndis_per_dev_params [configNr].used = 0;
David Brownell7e27f182006-06-13 09:54:40 -07001167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 return;
1169}
1170
David Brownell7e27f182006-06-13 09:54:40 -07001171int rndis_set_param_dev (u8 configNr, struct net_device *dev,
David Brownell340600a2005-04-28 13:45:25 -07001172 struct net_device_stats *stats,
1173 u16 *cdc_filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001175 DBG("%s:\n", __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 if (!dev || !stats) return -1;
1177 if (configNr >= RNDIS_MAX_CONFIGS) return -1;
David Brownell7e27f182006-06-13 09:54:40 -07001178
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 rndis_per_dev_params [configNr].dev = dev;
1180 rndis_per_dev_params [configNr].stats = stats;
David Brownell340600a2005-04-28 13:45:25 -07001181 rndis_per_dev_params [configNr].filter = cdc_filter;
David Brownell7e27f182006-06-13 09:54:40 -07001182
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 return 0;
1184}
1185
1186int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
1187{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001188 DBG("%s:\n", __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 if (!vendorDescr) return -1;
1190 if (configNr >= RNDIS_MAX_CONFIGS) return -1;
David Brownell7e27f182006-06-13 09:54:40 -07001191
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 rndis_per_dev_params [configNr].vendorID = vendorID;
1193 rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
David Brownell7e27f182006-06-13 09:54:40 -07001194
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 return 0;
1196}
1197
1198int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
1199{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001200 DBG("%s: %u %u\n", __func__, medium, speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 if (configNr >= RNDIS_MAX_CONFIGS) return -1;
David Brownell7e27f182006-06-13 09:54:40 -07001202
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 rndis_per_dev_params [configNr].medium = medium;
1204 rndis_per_dev_params [configNr].speed = speed;
David Brownell7e27f182006-06-13 09:54:40 -07001205
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 return 0;
1207}
1208
1209void rndis_add_hdr (struct sk_buff *skb)
1210{
1211 struct rndis_packet_msg_type *header;
1212
1213 if (!skb)
1214 return;
1215 header = (void *) skb_push (skb, sizeof *header);
1216 memset (header, 0, sizeof *header);
David Brownell6cdee102005-04-18 17:39:34 -07001217 header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 header->MessageLength = cpu_to_le32(skb->len);
1219 header->DataOffset = __constant_cpu_to_le32 (36);
David Brownell6cdee102005-04-18 17:39:34 -07001220 header->DataLength = cpu_to_le32(skb->len - sizeof *header);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221}
1222
1223void rndis_free_response (int configNr, u8 *buf)
1224{
1225 rndis_resp_t *r;
1226 struct list_head *act, *tmp;
David Brownell7e27f182006-06-13 09:54:40 -07001227
1228 list_for_each_safe (act, tmp,
1229 &(rndis_per_dev_params [configNr].resp_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 {
1231 r = list_entry (act, rndis_resp_t, list);
1232 if (r && r->buf == buf) {
1233 list_del (&r->list);
1234 kfree (r);
1235 }
1236 }
1237}
1238
1239u8 *rndis_get_next_response (int configNr, u32 *length)
1240{
1241 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -07001242 struct list_head *act, *tmp;
1243
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 if (!length) return NULL;
David Brownell7e27f182006-06-13 09:54:40 -07001245
1246 list_for_each_safe (act, tmp,
1247 &(rndis_per_dev_params [configNr].resp_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 {
1249 r = list_entry (act, rndis_resp_t, list);
1250 if (!r->send) {
1251 r->send = 1;
1252 *length = r->length;
1253 return r->buf;
1254 }
1255 }
David Brownell7e27f182006-06-13 09:54:40 -07001256
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 return NULL;
1258}
1259
1260static rndis_resp_t *rndis_add_response (int configNr, u32 length)
1261{
1262 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -07001263
David Brownell340600a2005-04-28 13:45:25 -07001264 /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
1266 if (!r) return NULL;
David Brownell7e27f182006-06-13 09:54:40 -07001267
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 r->buf = (u8 *) (r + 1);
1269 r->length = length;
1270 r->send = 0;
David Brownell7e27f182006-06-13 09:54:40 -07001271
1272 list_add_tail (&r->list,
1273 &(rndis_per_dev_params [configNr].resp_queue));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 return r;
1275}
1276
David Brownell6cdee102005-04-18 17:39:34 -07001277int rndis_rm_hdr(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278{
David Brownell6cdee102005-04-18 17:39:34 -07001279 /* tmp points to a struct rndis_packet_msg_type */
1280 __le32 *tmp = (void *) skb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281
David Brownell6cdee102005-04-18 17:39:34 -07001282 /* MessageType, MessageLength */
1283 if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
1284 != get_unaligned(tmp++))
1285 return -EINVAL;
1286 tmp++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
David Brownell6cdee102005-04-18 17:39:34 -07001288 /* DataOffset, DataLength */
Harvey Harrisona5abdea2008-04-29 01:03:40 -07001289 if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8))
David Brownell6cdee102005-04-18 17:39:34 -07001290 return -EOVERFLOW;
Harvey Harrisona5abdea2008-04-29 01:03:40 -07001291 skb_trim(skb, get_unaligned_le32(tmp++));
David Brownell6cdee102005-04-18 17:39:34 -07001292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 return 0;
1294}
1295
1296#ifdef CONFIG_USB_GADGET_DEBUG_FILES
1297
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001298static int rndis_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299{
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001300 rndis_params *param = m->private;
David Brownell7e27f182006-06-13 09:54:40 -07001301
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001302 seq_printf(m,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 "Config Nr. %d\n"
1304 "used : %s\n"
1305 "state : %s\n"
1306 "medium : 0x%08X\n"
1307 "speed : %d\n"
1308 "cable : %s\n"
1309 "vendor ID : 0x%08X\n"
David Brownell7e27f182006-06-13 09:54:40 -07001310 "vendor : %s\n",
1311 param->confignr, (param->used) ? "y" : "n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 ({ char *s = "?";
1313 switch (param->state) {
1314 case RNDIS_UNINITIALIZED:
1315 s = "RNDIS_UNINITIALIZED"; break;
1316 case RNDIS_INITIALIZED:
1317 s = "RNDIS_INITIALIZED"; break;
1318 case RNDIS_DATA_INITIALIZED:
1319 s = "RNDIS_DATA_INITIALIZED"; break;
1320 }; s; }),
David Brownell7e27f182006-06-13 09:54:40 -07001321 param->medium,
1322 (param->media_state) ? 0 : param->speed*100,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 (param->media_state) ? "disconnected" : "connected",
David Brownell7e27f182006-06-13 09:54:40 -07001324 param->vendorID, param->vendorDescr);
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001325 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326}
1327
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001328static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
1329 size_t count, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330{
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001331 rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 u32 speed = 0;
1333 int i, fl_speed = 0;
David Brownell7e27f182006-06-13 09:54:40 -07001334
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 for (i = 0; i < count; i++) {
1336 char c;
1337 if (get_user(c, buffer))
1338 return -EFAULT;
1339 switch (c) {
1340 case '0':
1341 case '1':
1342 case '2':
1343 case '3':
1344 case '4':
1345 case '5':
1346 case '6':
1347 case '7':
1348 case '8':
1349 case '9':
1350 fl_speed = 1;
1351 speed = speed*10 + c - '0';
1352 break;
1353 case 'C':
1354 case 'c':
1355 rndis_signal_connect (p->confignr);
1356 break;
1357 case 'D':
1358 case 'd':
1359 rndis_signal_disconnect(p->confignr);
1360 break;
David Brownell7e27f182006-06-13 09:54:40 -07001361 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 if (fl_speed) p->speed = speed;
David Brownell70790f62007-07-01 17:35:28 -07001363 else DBG("%c is not valid\n", c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 break;
1365 }
David Brownell7e27f182006-06-13 09:54:40 -07001366
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 buffer++;
1368 }
David Brownell7e27f182006-06-13 09:54:40 -07001369
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 return count;
1371}
1372
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001373static int rndis_proc_open(struct inode *inode, struct file *file)
1374{
1375 return single_open(file, rndis_proc_show, PDE(inode)->data);
1376}
1377
1378static const struct file_operations rndis_proc_fops = {
1379 .owner = THIS_MODULE,
1380 .open = rndis_proc_open,
1381 .read = seq_read,
1382 .llseek = seq_lseek,
1383 .release = single_release,
1384 .write = rndis_proc_write,
1385};
1386
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387#define NAME_TEMPLATE "driver/rndis-%03d"
1388
1389static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
1390
1391#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
1392
1393
David Brownell0e530b42008-04-05 14:17:14 -07001394int __init rndis_init (void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395{
1396 u8 i;
1397
1398 for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
1399#ifdef CONFIG_USB_GADGET_DEBUG_FILES
1400 char name [20];
1401
1402 sprintf (name, NAME_TEMPLATE, i);
1403 if (!(rndis_connect_state [i]
Alexey Dobriyane184d5f2008-05-14 16:25:13 -07001404 = proc_create_data(name, 0660, NULL,
1405 &rndis_proc_fops,
1406 (void *)(rndis_per_dev_params + i))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001408 DBG("%s :remove entries", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 while (i) {
1410 sprintf (name, NAME_TEMPLATE, --i);
1411 remove_proc_entry (name, NULL);
1412 }
David Brownell70790f62007-07-01 17:35:28 -07001413 DBG("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 return -EIO;
1415 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416#endif
1417 rndis_per_dev_params [i].confignr = i;
1418 rndis_per_dev_params [i].used = 0;
1419 rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
1420 rndis_per_dev_params [i].media_state
1421 = NDIS_MEDIA_STATE_DISCONNECTED;
1422 INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
1423 }
David Brownell7e27f182006-06-13 09:54:40 -07001424
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 return 0;
1426}
1427
1428void rndis_exit (void)
1429{
1430#ifdef CONFIG_USB_GADGET_DEBUG_FILES
1431 u8 i;
1432 char name [20];
David Brownell7e27f182006-06-13 09:54:40 -07001433
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
1435 sprintf (name, NAME_TEMPLATE, i);
1436 remove_proc_entry (name, NULL);
1437 }
1438#endif
1439}
1440