blob: d0677f5d3cd5f3f8b88b5c73573113f7fed672d1 [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 * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
David Brownell7e27f182006-06-13 09:54:40 -07005 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Authors: Benedikt Spranger, Pengutronix
David Brownell7e27f182006-06-13 09:54:40 -07007 * Robert Schwebel, Pengutronix
8 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
David Brownell7e27f182006-06-13 09:54:40 -070011 * version 2, as published by the Free Software Foundation.
12 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 * This software was originally developed in conformance with
14 * Microsoft's Remote NDIS Specification License Agreement.
David Brownell7e27f182006-06-13 09:54:40 -070015 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
17 * Fixed message length bug in init_response
David Brownell7e27f182006-06-13 09:54:40 -070018 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
David Brownell7e27f182006-06-13 09:54:40 -070020 * Fixed rndis_rm_hdr length bug.
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 *
22 * Copyright (C) 2004 by David Brownell
23 * updates to merge with Linux 2.6, better match RNDIS spec
24 */
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/kernel.h>
29#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/init.h>
31#include <linux/list.h>
32#include <linux/proc_fs.h>
33#include <linux/netdevice.h>
34
35#include <asm/io.h>
36#include <asm/byteorder.h>
37#include <asm/system.h>
David Brownell6cdee102005-04-18 17:39:34 -070038#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40
41#undef RNDIS_PM
David Brownell340600a2005-04-28 13:45:25 -070042#undef RNDIS_WAKEUP
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#undef VERBOSE
44
45#include "rndis.h"
46
47
48/* The driver for your USB chip needs to support ep0 OUT to work with
49 * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
50 *
51 * Windows hosts need an INF file like Documentation/usb/linux.inf
52 * and will be happier if you provide the host_addr module parameter.
53 */
54
55#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070056static int rndis_debug = 0;
David Brownell340600a2005-04-28 13:45:25 -070057module_param (rndis_debug, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058MODULE_PARM_DESC (rndis_debug, "enable debugging");
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#define rndis_debug 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#endif
62
David Brownell00274922007-11-19 12:58:36 -080063#define DBG(str,args...) do { \
64 if (rndis_debug) \
65 pr_debug(str , ## args); \
66 } while (0)
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#define RNDIS_MAX_CONFIGS 1
69
70
71static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
72
73/* Driver Version */
74static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1);
75
76/* Function Prototypes */
Linus Torvalds1da177e2005-04-16 15:20:36 -070077static rndis_resp_t *rndis_add_response (int configNr, u32 length);
78
79
David Brownell340600a2005-04-28 13:45:25 -070080/* supported OIDs */
David Brownell7e27f182006-06-13 09:54:40 -070081static const u32 oid_supported_list [] =
David Brownell340600a2005-04-28 13:45:25 -070082{
83 /* the general stuff */
84 OID_GEN_SUPPORTED_LIST,
85 OID_GEN_HARDWARE_STATUS,
86 OID_GEN_MEDIA_SUPPORTED,
87 OID_GEN_MEDIA_IN_USE,
88 OID_GEN_MAXIMUM_FRAME_SIZE,
89 OID_GEN_LINK_SPEED,
90 OID_GEN_TRANSMIT_BLOCK_SIZE,
91 OID_GEN_RECEIVE_BLOCK_SIZE,
92 OID_GEN_VENDOR_ID,
93 OID_GEN_VENDOR_DESCRIPTION,
94 OID_GEN_VENDOR_DRIVER_VERSION,
95 OID_GEN_CURRENT_PACKET_FILTER,
96 OID_GEN_MAXIMUM_TOTAL_SIZE,
97 OID_GEN_MEDIA_CONNECT_STATUS,
98 OID_GEN_PHYSICAL_MEDIUM,
99#if 0
100 OID_GEN_RNDIS_CONFIG_PARAMETER,
101#endif
David Brownell7e27f182006-06-13 09:54:40 -0700102
David Brownell340600a2005-04-28 13:45:25 -0700103 /* the statistical stuff */
104 OID_GEN_XMIT_OK,
105 OID_GEN_RCV_OK,
106 OID_GEN_XMIT_ERROR,
107 OID_GEN_RCV_ERROR,
108 OID_GEN_RCV_NO_BUFFER,
109#ifdef RNDIS_OPTIONAL_STATS
110 OID_GEN_DIRECTED_BYTES_XMIT,
111 OID_GEN_DIRECTED_FRAMES_XMIT,
112 OID_GEN_MULTICAST_BYTES_XMIT,
113 OID_GEN_MULTICAST_FRAMES_XMIT,
114 OID_GEN_BROADCAST_BYTES_XMIT,
115 OID_GEN_BROADCAST_FRAMES_XMIT,
116 OID_GEN_DIRECTED_BYTES_RCV,
117 OID_GEN_DIRECTED_FRAMES_RCV,
118 OID_GEN_MULTICAST_BYTES_RCV,
119 OID_GEN_MULTICAST_FRAMES_RCV,
120 OID_GEN_BROADCAST_BYTES_RCV,
121 OID_GEN_BROADCAST_FRAMES_RCV,
122 OID_GEN_RCV_CRC_ERROR,
123 OID_GEN_TRANSMIT_QUEUE_LENGTH,
124#endif /* RNDIS_OPTIONAL_STATS */
125
David Brownell7e27f182006-06-13 09:54:40 -0700126 /* mandatory 802.3 */
David Brownell340600a2005-04-28 13:45:25 -0700127 /* the general stuff */
128 OID_802_3_PERMANENT_ADDRESS,
129 OID_802_3_CURRENT_ADDRESS,
130 OID_802_3_MULTICAST_LIST,
131 OID_802_3_MAC_OPTIONS,
132 OID_802_3_MAXIMUM_LIST_SIZE,
David Brownell7e27f182006-06-13 09:54:40 -0700133
David Brownell340600a2005-04-28 13:45:25 -0700134 /* the statistical stuff */
135 OID_802_3_RCV_ERROR_ALIGNMENT,
136 OID_802_3_XMIT_ONE_COLLISION,
137 OID_802_3_XMIT_MORE_COLLISIONS,
138#ifdef RNDIS_OPTIONAL_STATS
139 OID_802_3_XMIT_DEFERRED,
140 OID_802_3_XMIT_MAX_COLLISIONS,
141 OID_802_3_RCV_OVERRUN,
142 OID_802_3_XMIT_UNDERRUN,
143 OID_802_3_XMIT_HEARTBEAT_FAILURE,
144 OID_802_3_XMIT_TIMES_CRS_LOST,
145 OID_802_3_XMIT_LATE_COLLISIONS,
146#endif /* RNDIS_OPTIONAL_STATS */
147
148#ifdef RNDIS_PM
149 /* PM and wakeup are mandatory for USB: */
150
151 /* power management */
152 OID_PNP_CAPABILITIES,
153 OID_PNP_QUERY_POWER,
154 OID_PNP_SET_POWER,
155
156#ifdef RNDIS_WAKEUP
157 /* wake up host */
158 OID_PNP_ENABLE_WAKE_UP,
159 OID_PNP_ADD_WAKE_UP_PATTERN,
160 OID_PNP_REMOVE_WAKE_UP_PATTERN,
161#endif /* RNDIS_WAKEUP */
162#endif /* RNDIS_PM */
163};
164
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166/* NDIS Functions */
David Brownell340600a2005-04-28 13:45:25 -0700167static int
168gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
169 rndis_resp_t *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
David Brownell7e27f182006-06-13 09:54:40 -0700171 int retval = -ENOTSUPP;
172 u32 length = 4; /* usually */
David Brownell340600a2005-04-28 13:45:25 -0700173 __le32 *outbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 int i, count;
175 rndis_query_cmplt_type *resp;
176
177 if (!r) return -ENOMEM;
178 resp = (rndis_query_cmplt_type *) r->buf;
179
180 if (!resp) return -ENOMEM;
David Brownell340600a2005-04-28 13:45:25 -0700181
182 if (buf_len && rndis_debug > 1) {
David Brownell70790f632007-07-01 17:35:28 -0700183 DBG("query OID %08x value, len %d:\n", OID, buf_len);
David Brownell340600a2005-04-28 13:45:25 -0700184 for (i = 0; i < buf_len; i += 16) {
David Brownell70790f632007-07-01 17:35:28 -0700185 DBG("%03d: %08x %08x %08x %08x\n", i,
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700186 get_unaligned_le32(&buf[i]),
187 get_unaligned_le32(&buf[i + 4]),
188 get_unaligned_le32(&buf[i + 8]),
189 get_unaligned_le32(&buf[i + 12]));
David Brownell340600a2005-04-28 13:45:25 -0700190 }
191 }
192
193 /* response goes here, right after the header */
194 outbuf = (__le32 *) &resp[1];
195 resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
196
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 switch (OID) {
198
199 /* general oids (table 4-1) */
200
201 /* mandatory */
202 case OID_GEN_SUPPORTED_LIST:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800203 DBG("%s: OID_GEN_SUPPORTED_LIST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 length = sizeof (oid_supported_list);
205 count = length / sizeof (u32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 for (i = 0; i < count; i++)
David Brownell340600a2005-04-28 13:45:25 -0700207 outbuf[i] = cpu_to_le32 (oid_supported_list[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 retval = 0;
209 break;
David Brownell7e27f182006-06-13 09:54:40 -0700210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 /* mandatory */
212 case OID_GEN_HARDWARE_STATUS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800213 DBG("%s: OID_GEN_HARDWARE_STATUS\n", __func__);
David Brownell7e27f182006-06-13 09:54:40 -0700214 /* Bogus question!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 * Hardware must be ready to receive high level protocols.
David Brownell7e27f182006-06-13 09:54:40 -0700216 * BTW:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 * reddite ergo quae sunt Caesaris Caesari
218 * et quae sunt Dei Deo!
219 */
David Brownell340600a2005-04-28 13:45:25 -0700220 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 retval = 0;
222 break;
David Brownell7e27f182006-06-13 09:54:40 -0700223
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 /* mandatory */
225 case OID_GEN_MEDIA_SUPPORTED:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800226 DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700227 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 retval = 0;
229 break;
David Brownell7e27f182006-06-13 09:54:40 -0700230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 /* mandatory */
232 case OID_GEN_MEDIA_IN_USE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800233 DBG("%s: OID_GEN_MEDIA_IN_USE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 /* one medium, one transport... (maybe you do it better) */
David Brownell340600a2005-04-28 13:45:25 -0700235 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 retval = 0;
237 break;
David Brownell7e27f182006-06-13 09:54:40 -0700238
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 /* mandatory */
240 case OID_GEN_MAXIMUM_FRAME_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800241 DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 if (rndis_per_dev_params [configNr].dev) {
David Brownell340600a2005-04-28 13:45:25 -0700243 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 rndis_per_dev_params [configNr].dev->mtu);
245 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 }
247 break;
David Brownell7e27f182006-06-13 09:54:40 -0700248
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 /* mandatory */
250 case OID_GEN_LINK_SPEED:
David Brownell340600a2005-04-28 13:45:25 -0700251 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800252 DBG("%s: OID_GEN_LINK_SPEED\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 if (rndis_per_dev_params [configNr].media_state
David Brownell340600a2005-04-28 13:45:25 -0700254 == NDIS_MEDIA_STATE_DISCONNECTED)
255 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 else
David Brownell340600a2005-04-28 13:45:25 -0700257 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 rndis_per_dev_params [configNr].speed);
259 retval = 0;
260 break;
261
262 /* mandatory */
263 case OID_GEN_TRANSMIT_BLOCK_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800264 DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 if (rndis_per_dev_params [configNr].dev) {
David Brownell340600a2005-04-28 13:45:25 -0700266 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 rndis_per_dev_params [configNr].dev->mtu);
268 retval = 0;
269 }
270 break;
David Brownell7e27f182006-06-13 09:54:40 -0700271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 /* mandatory */
273 case OID_GEN_RECEIVE_BLOCK_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800274 DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 if (rndis_per_dev_params [configNr].dev) {
David Brownell340600a2005-04-28 13:45:25 -0700276 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 rndis_per_dev_params [configNr].dev->mtu);
278 retval = 0;
279 }
280 break;
David Brownell7e27f182006-06-13 09:54:40 -0700281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 /* mandatory */
283 case OID_GEN_VENDOR_ID:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800284 DBG("%s: OID_GEN_VENDOR_ID\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700285 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 rndis_per_dev_params [configNr].vendorID);
287 retval = 0;
288 break;
David Brownell7e27f182006-06-13 09:54:40 -0700289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 /* mandatory */
291 case OID_GEN_VENDOR_DESCRIPTION:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800292 DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 length = strlen (rndis_per_dev_params [configNr].vendorDescr);
David Brownell340600a2005-04-28 13:45:25 -0700294 memcpy (outbuf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 rndis_per_dev_params [configNr].vendorDescr, length);
296 retval = 0;
297 break;
298
299 case OID_GEN_VENDOR_DRIVER_VERSION:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800300 DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 /* Created as LE */
David Brownell340600a2005-04-28 13:45:25 -0700302 *outbuf = rndis_driver_version;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 retval = 0;
304 break;
305
306 /* mandatory */
307 case OID_GEN_CURRENT_PACKET_FILTER:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800308 DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700309 *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 retval = 0;
311 break;
312
313 /* mandatory */
314 case OID_GEN_MAXIMUM_TOTAL_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800315 DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700316 *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 retval = 0;
318 break;
319
320 /* mandatory */
321 case OID_GEN_MEDIA_CONNECT_STATUS:
David Brownell340600a2005-04-28 13:45:25 -0700322 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800323 DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700324 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 .media_state);
326 retval = 0;
327 break;
328
329 case OID_GEN_PHYSICAL_MEDIUM:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800330 DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700331 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 retval = 0;
333 break;
334
335 /* The RNDIS specification is incomplete/wrong. Some versions
336 * of MS-Windows expect OIDs that aren't specified there. Other
337 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
338 */
339 case OID_GEN_MAC_OPTIONS: /* from WinME */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800340 DBG("%s: OID_GEN_MAC_OPTIONS\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700341 *outbuf = __constant_cpu_to_le32(
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 NDIS_MAC_OPTION_RECEIVE_SERIALIZED
343 | NDIS_MAC_OPTION_FULL_DUPLEX);
344 retval = 0;
345 break;
346
347 /* statistics OIDs (table 4-2) */
348
349 /* mandatory */
350 case OID_GEN_XMIT_OK:
David Brownell340600a2005-04-28 13:45:25 -0700351 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800352 DBG("%s: OID_GEN_XMIT_OK\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700354 *outbuf = cpu_to_le32 (
David Brownell7e27f182006-06-13 09:54:40 -0700355 rndis_per_dev_params [configNr].stats->tx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 rndis_per_dev_params [configNr].stats->tx_errors -
357 rndis_per_dev_params [configNr].stats->tx_dropped);
358 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 }
360 break;
361
362 /* mandatory */
363 case OID_GEN_RCV_OK:
David Brownell340600a2005-04-28 13:45:25 -0700364 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800365 DBG("%s: OID_GEN_RCV_OK\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700367 *outbuf = cpu_to_le32 (
David Brownell7e27f182006-06-13 09:54:40 -0700368 rndis_per_dev_params [configNr].stats->rx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 rndis_per_dev_params [configNr].stats->rx_errors -
370 rndis_per_dev_params [configNr].stats->rx_dropped);
371 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 }
373 break;
David Brownell7e27f182006-06-13 09:54:40 -0700374
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 /* mandatory */
376 case OID_GEN_XMIT_ERROR:
David Brownell340600a2005-04-28 13:45:25 -0700377 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800378 DBG("%s: OID_GEN_XMIT_ERROR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700380 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 .stats->tx_errors);
382 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 }
384 break;
David Brownell7e27f182006-06-13 09:54:40 -0700385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 /* mandatory */
387 case OID_GEN_RCV_ERROR:
David Brownell340600a2005-04-28 13:45:25 -0700388 if (rndis_debug > 1)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800389 DBG("%s: OID_GEN_RCV_ERROR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700391 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 .stats->rx_errors);
393 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 }
395 break;
David Brownell7e27f182006-06-13 09:54:40 -0700396
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 /* mandatory */
398 case OID_GEN_RCV_NO_BUFFER:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800399 DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700401 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 .stats->rx_dropped);
403 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405 break;
406
407#ifdef RNDIS_OPTIONAL_STATS
408 case OID_GEN_DIRECTED_BYTES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800409 DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__);
David Brownell7e27f182006-06-13 09:54:40 -0700410 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 * Aunt Tilly's size of shoes
412 * minus antarctica count of penguins
413 * divided by weight of Alpha Centauri
414 */
415 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700416 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 (rndis_per_dev_params [configNr]
David Brownell7e27f182006-06-13 09:54:40 -0700418 .stats->tx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 rndis_per_dev_params [configNr]
420 .stats->tx_errors -
421 rndis_per_dev_params [configNr]
422 .stats->tx_dropped)
423 * 123);
424 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 }
426 break;
David Brownell7e27f182006-06-13 09:54:40 -0700427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 case OID_GEN_DIRECTED_FRAMES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800429 DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 /* dito */
431 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700432 *outbuf = cpu_to_le32 (
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 (rndis_per_dev_params [configNr]
David Brownell7e27f182006-06-13 09:54:40 -0700434 .stats->tx_packets -
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 rndis_per_dev_params [configNr]
436 .stats->tx_errors -
437 rndis_per_dev_params [configNr]
438 .stats->tx_dropped)
439 / 123);
440 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 }
442 break;
David Brownell7e27f182006-06-13 09:54:40 -0700443
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 case OID_GEN_MULTICAST_BYTES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800445 DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700447 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 .stats->multicast*1234);
449 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 }
451 break;
David Brownell7e27f182006-06-13 09:54:40 -0700452
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 case OID_GEN_MULTICAST_FRAMES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800454 DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700456 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 .stats->multicast);
458 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 }
460 break;
David Brownell7e27f182006-06-13 09:54:40 -0700461
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 case OID_GEN_BROADCAST_BYTES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800463 DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700465 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 .stats->tx_packets/42*255);
467 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 }
469 break;
David Brownell7e27f182006-06-13 09:54:40 -0700470
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 case OID_GEN_BROADCAST_FRAMES_XMIT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800472 DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700474 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 .stats->tx_packets/42);
476 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
478 break;
David Brownell7e27f182006-06-13 09:54:40 -0700479
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 case OID_GEN_DIRECTED_BYTES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800481 DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700482 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 retval = 0;
484 break;
David Brownell7e27f182006-06-13 09:54:40 -0700485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 case OID_GEN_DIRECTED_FRAMES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800487 DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700488 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 retval = 0;
490 break;
David Brownell7e27f182006-06-13 09:54:40 -0700491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 case OID_GEN_MULTICAST_BYTES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800493 DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700495 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 .stats->multicast * 1111);
497 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 }
499 break;
David Brownell7e27f182006-06-13 09:54:40 -0700500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 case OID_GEN_MULTICAST_FRAMES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800502 DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700504 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 .stats->multicast);
506 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 }
508 break;
David Brownell7e27f182006-06-13 09:54:40 -0700509
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 case OID_GEN_BROADCAST_BYTES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800511 DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700513 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 .stats->rx_packets/42*255);
515 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 }
517 break;
David Brownell7e27f182006-06-13 09:54:40 -0700518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 case OID_GEN_BROADCAST_FRAMES_RCV:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800520 DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700522 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 .stats->rx_packets/42);
524 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 }
526 break;
David Brownell7e27f182006-06-13 09:54:40 -0700527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 case OID_GEN_RCV_CRC_ERROR:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800529 DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 if (rndis_per_dev_params [configNr].stats) {
David Brownell340600a2005-04-28 13:45:25 -0700531 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 .stats->rx_crc_errors);
533 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 }
535 break;
David Brownell7e27f182006-06-13 09:54:40 -0700536
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 case OID_GEN_TRANSMIT_QUEUE_LENGTH:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800538 DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700539 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 retval = 0;
541 break;
542#endif /* RNDIS_OPTIONAL_STATS */
543
544 /* ieee802.3 OIDs (table 4-3) */
545
546 /* mandatory */
547 case OID_802_3_PERMANENT_ADDRESS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800548 DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 if (rndis_per_dev_params [configNr].dev) {
550 length = ETH_ALEN;
David Brownell340600a2005-04-28 13:45:25 -0700551 memcpy (outbuf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 rndis_per_dev_params [configNr].host_mac,
553 length);
554 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
556 break;
David Brownell7e27f182006-06-13 09:54:40 -0700557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 /* mandatory */
559 case OID_802_3_CURRENT_ADDRESS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800560 DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 if (rndis_per_dev_params [configNr].dev) {
562 length = ETH_ALEN;
David Brownell340600a2005-04-28 13:45:25 -0700563 memcpy (outbuf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 rndis_per_dev_params [configNr].host_mac,
565 length);
566 retval = 0;
567 }
568 break;
David Brownell7e27f182006-06-13 09:54:40 -0700569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 /* mandatory */
571 case OID_802_3_MULTICAST_LIST:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800572 DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 /* Multicast base address only */
David Brownell340600a2005-04-28 13:45:25 -0700574 *outbuf = __constant_cpu_to_le32 (0xE0000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 retval = 0;
576 break;
David Brownell7e27f182006-06-13 09:54:40 -0700577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 /* mandatory */
579 case OID_802_3_MAXIMUM_LIST_SIZE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800580 DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 /* Multicast base address only */
David Brownell340600a2005-04-28 13:45:25 -0700582 *outbuf = __constant_cpu_to_le32 (1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 retval = 0;
584 break;
David Brownell7e27f182006-06-13 09:54:40 -0700585
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 case OID_802_3_MAC_OPTIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800587 DBG("%s: OID_802_3_MAC_OPTIONS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 break;
589
590 /* ieee802.3 statistics OIDs (table 4-4) */
591
592 /* mandatory */
593 case OID_802_3_RCV_ERROR_ALIGNMENT:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800594 DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700595 if (rndis_per_dev_params [configNr].stats) {
596 *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 .stats->rx_frame_errors);
598 retval = 0;
599 }
600 break;
David Brownell7e27f182006-06-13 09:54:40 -0700601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 /* mandatory */
603 case OID_802_3_XMIT_ONE_COLLISION:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800604 DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700605 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 retval = 0;
607 break;
David Brownell7e27f182006-06-13 09:54:40 -0700608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 /* mandatory */
610 case OID_802_3_XMIT_MORE_COLLISIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800611 DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
David Brownell340600a2005-04-28 13:45:25 -0700612 *outbuf = __constant_cpu_to_le32 (0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 retval = 0;
614 break;
David Brownell7e27f182006-06-13 09:54:40 -0700615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616#ifdef RNDIS_OPTIONAL_STATS
617 case OID_802_3_XMIT_DEFERRED:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800618 DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 /* TODO */
620 break;
David Brownell7e27f182006-06-13 09:54:40 -0700621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 case OID_802_3_XMIT_MAX_COLLISIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800623 DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 /* TODO */
625 break;
David Brownell7e27f182006-06-13 09:54:40 -0700626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 case OID_802_3_RCV_OVERRUN:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800628 DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 /* TODO */
630 break;
David Brownell7e27f182006-06-13 09:54:40 -0700631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 case OID_802_3_XMIT_UNDERRUN:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800633 DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 /* TODO */
635 break;
David Brownell7e27f182006-06-13 09:54:40 -0700636
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 case OID_802_3_XMIT_HEARTBEAT_FAILURE:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800638 DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 /* TODO */
640 break;
David Brownell7e27f182006-06-13 09:54:40 -0700641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 case OID_802_3_XMIT_TIMES_CRS_LOST:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800643 DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 /* TODO */
645 break;
David Brownell7e27f182006-06-13 09:54:40 -0700646
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 case OID_802_3_XMIT_LATE_COLLISIONS:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800648 DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 /* TODO */
David Brownell7e27f182006-06-13 09:54:40 -0700650 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651#endif /* RNDIS_OPTIONAL_STATS */
652
653#ifdef RNDIS_PM
654 /* power management OIDs (table 4-5) */
655 case OID_PNP_CAPABILITIES:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800656 DBG("%s: OID_PNP_CAPABILITIES\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
David Brownell340600a2005-04-28 13:45:25 -0700658 /* for now, no wakeup capabilities */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 length = sizeof (struct NDIS_PNP_CAPABILITIES);
David Brownell340600a2005-04-28 13:45:25 -0700660 memset(outbuf, 0, length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 retval = 0;
662 break;
663 case OID_PNP_QUERY_POWER:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800664 DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__,
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700665 get_unaligned_le32(buf) - 1);
David Brownell340600a2005-04-28 13:45:25 -0700666 /* only suspend is a real power state, and
667 * it can't be entered by OID_PNP_SET_POWER...
668 */
669 length = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 retval = 0;
671 break;
672#endif
673
674 default:
David Brownell00274922007-11-19 12:58:36 -0800675 pr_warning("%s: query unknown OID 0x%08X\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800676 __func__, OID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 }
David Brownell340600a2005-04-28 13:45:25 -0700678 if (retval < 0)
679 length = 0;
David Brownell7e27f182006-06-13 09:54:40 -0700680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 resp->InformationBufferLength = cpu_to_le32 (length);
David Brownell340600a2005-04-28 13:45:25 -0700682 r->length = length + sizeof *resp;
683 resp->MessageLength = cpu_to_le32 (r->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 return retval;
685}
686
David Brownell7e27f182006-06-13 09:54:40 -0700687static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
688 rndis_resp_t *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689{
690 rndis_set_cmplt_type *resp;
David Brownell7e27f182006-06-13 09:54:40 -0700691 int i, retval = -ENOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 struct rndis_params *params;
693
694 if (!r)
695 return -ENOMEM;
696 resp = (rndis_set_cmplt_type *) r->buf;
697 if (!resp)
698 return -ENOMEM;
699
David Brownell340600a2005-04-28 13:45:25 -0700700 if (buf_len && rndis_debug > 1) {
David Brownell70790f632007-07-01 17:35:28 -0700701 DBG("set OID %08x value, len %d:\n", OID, buf_len);
David Brownell340600a2005-04-28 13:45:25 -0700702 for (i = 0; i < buf_len; i += 16) {
David Brownell70790f632007-07-01 17:35:28 -0700703 DBG("%03d: %08x %08x %08x %08x\n", i,
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700704 get_unaligned_le32(&buf[i]),
705 get_unaligned_le32(&buf[i + 4]),
706 get_unaligned_le32(&buf[i + 8]),
707 get_unaligned_le32(&buf[i + 12]));
David Brownell340600a2005-04-28 13:45:25 -0700708 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 }
710
David Brownell340600a2005-04-28 13:45:25 -0700711 params = &rndis_per_dev_params [configNr];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 switch (OID) {
713 case OID_GEN_CURRENT_PACKET_FILTER:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
David Brownell340600a2005-04-28 13:45:25 -0700715 /* these NDIS_PACKET_TYPE_* bitflags are shared with
716 * cdc_filter; it's not RNDIS-specific
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
718 * PROMISCUOUS, DIRECTED,
719 * MULTICAST, ALL_MULTICAST, BROADCAST
720 */
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700721 *params->filter = (u16)get_unaligned_le32(buf);
David Brownell70790f632007-07-01 17:35:28 -0700722 DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800723 __func__, *params->filter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
725 /* this call has a significant side effect: it's
726 * what makes the packet flow start and stop, like
727 * activating the CDC Ethernet altsetting.
728 */
David Brownell340600a2005-04-28 13:45:25 -0700729#ifdef RNDIS_PM
730update_linkstate:
731#endif
732 retval = 0;
733 if (*params->filter) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 params->state = RNDIS_DATA_INITIALIZED;
735 netif_carrier_on(params->dev);
736 if (netif_running(params->dev))
737 netif_wake_queue (params->dev);
738 } else {
739 params->state = RNDIS_INITIALIZED;
740 netif_carrier_off (params->dev);
741 netif_stop_queue (params->dev);
742 }
743 break;
David Brownell7e27f182006-06-13 09:54:40 -0700744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 case OID_802_3_MULTICAST_LIST:
David Brownell7e27f182006-06-13 09:54:40 -0700746 /* I think we can ignore this */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800747 DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 retval = 0;
749 break;
750#if 0
751 case OID_GEN_RNDIS_CONFIG_PARAMETER:
752 {
753 struct rndis_config_parameter *param;
754 param = (struct rndis_config_parameter *) buf;
David Brownell70790f632007-07-01 17:35:28 -0700755 DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800756 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 min(cpu_to_le32(param->ParameterNameLength),80),
758 buf + param->ParameterNameOffset);
759 retval = 0;
760 }
761 break;
762#endif
763
764#ifdef RNDIS_PM
765 case OID_PNP_SET_POWER:
David Brownell340600a2005-04-28 13:45:25 -0700766 /* The only real power state is USB suspend, and RNDIS requests
767 * can't enter it; this one isn't really about power. After
768 * resuming, Windows forces a reset, and then SET_POWER D0.
769 * FIXME ... then things go batty; Windows wedges itself.
770 */
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700771 i = get_unaligned_le32(buf);
Harvey Harrison441b62c2008-03-03 16:08:34 -0800772 DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1);
David Brownell340600a2005-04-28 13:45:25 -0700773 switch (i) {
774 case NdisDeviceStateD0:
775 *params->filter = params->saved_filter;
776 goto update_linkstate;
777 case NdisDeviceStateD3:
778 case NdisDeviceStateD2:
779 case NdisDeviceStateD1:
780 params->saved_filter = *params->filter;
781 retval = 0;
782 break;
783 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 break;
785
David Brownell340600a2005-04-28 13:45:25 -0700786#ifdef RNDIS_WAKEUP
787 // no wakeup support advertised, so wakeup OIDs always fail:
788 // - OID_PNP_ENABLE_WAKE_UP
789 // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790#endif
791
David Brownell340600a2005-04-28 13:45:25 -0700792#endif /* RNDIS_PM */
793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 default:
David Brownell00274922007-11-19 12:58:36 -0800795 pr_warning("%s: set unknown OID 0x%08X, size %d\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800796 __func__, OID, buf_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
David Brownell7e27f182006-06-13 09:54:40 -0700798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 return retval;
800}
801
David Brownell7e27f182006-06-13 09:54:40 -0700802/*
803 * Response Functions
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 */
805
806static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
807{
David Brownell7e27f182006-06-13 09:54:40 -0700808 rndis_init_cmplt_type *resp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700810
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
David Brownell7e27f182006-06-13 09:54:40 -0700812
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700814 if (!r)
815 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 resp = (rndis_init_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 resp->MessageType = __constant_cpu_to_le32 (
819 REMOTE_NDIS_INITIALIZE_CMPLT);
820 resp->MessageLength = __constant_cpu_to_le32 (52);
821 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
822 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
823 resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION);
824 resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION);
825 resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS);
826 resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3);
827 resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1);
828 resp->MaxTransferSize = cpu_to_le32 (
829 rndis_per_dev_params [configNr].dev->mtu
830 + sizeof (struct ethhdr)
831 + sizeof (struct rndis_packet_msg_type)
832 + 22);
833 resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0);
834 resp->AFListOffset = __constant_cpu_to_le32 (0);
835 resp->AFListSize = __constant_cpu_to_le32 (0);
David Brownell7e27f182006-06-13 09:54:40 -0700836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700838 rndis_per_dev_params [configNr].ack (
839 rndis_per_dev_params [configNr].dev);
840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return 0;
842}
843
844static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
845{
846 rndis_query_cmplt_type *resp;
847 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700848
Harvey Harrison441b62c2008-03-03 16:08:34 -0800849 // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
David Brownell7e27f182006-06-13 09:54:40 -0700851
Shaun Tancheff87637162006-02-22 19:47:19 -0800852 /*
853 * we need more memory:
854 * gen_ndis_query_resp expects enough space for
855 * rndis_query_cmplt_type followed by data.
856 * oid_supported_list is the largest data reply
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 */
Shaun Tancheff87637162006-02-22 19:47:19 -0800858 r = rndis_add_response (configNr,
859 sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700860 if (!r)
861 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 resp = (rndis_query_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700863
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
David Brownell7e27f182006-06-13 09:54:40 -0700866
David Brownell340600a2005-04-28 13:45:25 -0700867 if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
868 le32_to_cpu(buf->InformationBufferOffset)
869 + 8 + (u8 *) buf,
870 le32_to_cpu(buf->InformationBufferLength),
871 r)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 /* OID not supported */
873 resp->Status = __constant_cpu_to_le32 (
874 RNDIS_STATUS_NOT_SUPPORTED);
David Brownell340600a2005-04-28 13:45:25 -0700875 resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 resp->InformationBufferLength = __constant_cpu_to_le32 (0);
877 resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
878 } else
879 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
David Brownell7e27f182006-06-13 09:54:40 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700882 rndis_per_dev_params [configNr].ack (
883 rndis_per_dev_params [configNr].dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 return 0;
885}
886
887static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
888{
889 u32 BufLength, BufOffset;
890 rndis_set_cmplt_type *resp;
891 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700892
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700894 if (!r)
895 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 resp = (rndis_set_cmplt_type *) r->buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
898 BufLength = le32_to_cpu (buf->InformationBufferLength);
899 BufOffset = le32_to_cpu (buf->InformationBufferOffset);
900
901#ifdef VERBOSE
Harvey Harrison441b62c2008-03-03 16:08:34 -0800902 DBG("%s: Length: %d\n", __func__, BufLength);
903 DBG("%s: Offset: %d\n", __func__, BufOffset);
904 DBG("%s: InfoBuffer: ", __func__);
David Brownell7e27f182006-06-13 09:54:40 -0700905
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 for (i = 0; i < BufLength; i++) {
David Brownell70790f632007-07-01 17:35:28 -0700907 DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 }
David Brownell7e27f182006-06-13 09:54:40 -0700909
David Brownell70790f632007-07-01 17:35:28 -0700910 DBG("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911#endif
David Brownell7e27f182006-06-13 09:54:40 -0700912
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
914 resp->MessageLength = __constant_cpu_to_le32 (16);
915 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
David Brownell7e27f182006-06-13 09:54:40 -0700916 if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
917 ((u8 *) buf) + 8 + BufOffset, BufLength, r))
918 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
919 else
920 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
921
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700923 rndis_per_dev_params [configNr].ack (
924 rndis_per_dev_params [configNr].dev);
925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 return 0;
927}
928
929static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
930{
931 rndis_reset_cmplt_type *resp;
932 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700935 if (!r)
936 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 resp = (rndis_reset_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
940 resp->MessageLength = __constant_cpu_to_le32 (16);
941 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
942 /* resent information */
943 resp->AddressingReset = __constant_cpu_to_le32 (1);
David Brownell7e27f182006-06-13 09:54:40 -0700944
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700946 rndis_per_dev_params [configNr].ack (
947 rndis_per_dev_params [configNr].dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 return 0;
950}
951
952static int rndis_keepalive_response (int configNr,
David Brownell7e27f182006-06-13 09:54:40 -0700953 rndis_keepalive_msg_type *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954{
955 rndis_keepalive_cmplt_type *resp;
956 rndis_resp_t *r;
957
958 /* host "should" check only in RNDIS_DATA_INITIALIZED state */
959
960 r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
David Brownell340600a2005-04-28 13:45:25 -0700961 if (!r)
962 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 resp = (rndis_keepalive_cmplt_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700964
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 resp->MessageType = __constant_cpu_to_le32 (
966 REMOTE_NDIS_KEEPALIVE_CMPLT);
967 resp->MessageLength = __constant_cpu_to_le32 (16);
968 resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
969 resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
David Brownell7e27f182006-06-13 09:54:40 -0700970
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 if (rndis_per_dev_params [configNr].ack)
David Brownell7e27f182006-06-13 09:54:40 -0700972 rndis_per_dev_params [configNr].ack (
973 rndis_per_dev_params [configNr].dev);
974
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 return 0;
976}
977
978
David Brownell7e27f182006-06-13 09:54:40 -0700979/*
980 * Device to Host Comunication
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 */
982static int rndis_indicate_status_msg (int configNr, u32 status)
983{
David Brownell7e27f182006-06-13 09:54:40 -0700984 rndis_indicate_status_msg_type *resp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -0700986
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
David Brownell7e27f182006-06-13 09:54:40 -0700988 return -ENOTSUPP;
989
990 r = rndis_add_response (configNr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 sizeof (rndis_indicate_status_msg_type));
David Brownell340600a2005-04-28 13:45:25 -0700992 if (!r)
993 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 resp = (rndis_indicate_status_msg_type *) r->buf;
David Brownell7e27f182006-06-13 09:54:40 -0700995
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 resp->MessageType = __constant_cpu_to_le32 (
997 REMOTE_NDIS_INDICATE_STATUS_MSG);
998 resp->MessageLength = __constant_cpu_to_le32 (20);
999 resp->Status = cpu_to_le32 (status);
1000 resp->StatusBufferLength = __constant_cpu_to_le32 (0);
1001 resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
David Brownell7e27f182006-06-13 09:54:40 -07001002
1003 if (rndis_per_dev_params [configNr].ack)
1004 rndis_per_dev_params [configNr].ack (
1005 rndis_per_dev_params [configNr].dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 return 0;
1007}
1008
1009int rndis_signal_connect (int configNr)
1010{
1011 rndis_per_dev_params [configNr].media_state
1012 = NDIS_MEDIA_STATE_CONNECTED;
David Brownell7e27f182006-06-13 09:54:40 -07001013 return rndis_indicate_status_msg (configNr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 RNDIS_STATUS_MEDIA_CONNECT);
1015}
1016
1017int rndis_signal_disconnect (int configNr)
1018{
1019 rndis_per_dev_params [configNr].media_state
1020 = NDIS_MEDIA_STATE_DISCONNECTED;
1021 return rndis_indicate_status_msg (configNr,
1022 RNDIS_STATUS_MEDIA_DISCONNECT);
1023}
1024
David Brownell340600a2005-04-28 13:45:25 -07001025void rndis_uninit (int configNr)
1026{
David Brownell486e2df2005-05-24 17:51:52 -07001027 u8 *buf;
1028 u32 length;
1029
David Brownell340600a2005-04-28 13:45:25 -07001030 if (configNr >= RNDIS_MAX_CONFIGS)
1031 return;
1032 rndis_per_dev_params [configNr].used = 0;
1033 rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
David Brownell486e2df2005-05-24 17:51:52 -07001034
1035 /* drain the response queue */
1036 while ((buf = rndis_get_next_response(configNr, &length)))
1037 rndis_free_response(configNr, buf);
David Brownell340600a2005-04-28 13:45:25 -07001038}
1039
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040void rndis_set_host_mac (int configNr, const u8 *addr)
1041{
1042 rndis_per_dev_params [configNr].host_mac = addr;
1043}
1044
David Brownell7e27f182006-06-13 09:54:40 -07001045/*
1046 * Message Parser
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 */
1048int rndis_msg_parser (u8 configNr, u8 *buf)
1049{
1050 u32 MsgType, MsgLength;
1051 __le32 *tmp;
1052 struct rndis_params *params;
David Brownell7e27f182006-06-13 09:54:40 -07001053
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 if (!buf)
1055 return -ENOMEM;
David Brownell7e27f182006-06-13 09:54:40 -07001056
1057 tmp = (__le32 *) buf;
Harvey Harrisona5abdea2008-04-29 01:03:40 -07001058 MsgType = get_unaligned_le32(tmp++);
1059 MsgLength = get_unaligned_le32(tmp++);
David Brownell7e27f182006-06-13 09:54:40 -07001060
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 if (configNr >= RNDIS_MAX_CONFIGS)
1062 return -ENOTSUPP;
1063 params = &rndis_per_dev_params [configNr];
David Brownell7e27f182006-06-13 09:54:40 -07001064
David Brownell340600a2005-04-28 13:45:25 -07001065 /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
1066 * rx/tx statistics and link status, in addition to KEEPALIVE traffic
1067 * and normal HC level polling to see if there's any IN traffic.
1068 */
1069
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 /* For USB: responses may take up to 10 seconds */
David Brownell340600a2005-04-28 13:45:25 -07001071 switch (MsgType) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 case REMOTE_NDIS_INITIALIZE_MSG:
David Brownell70790f632007-07-01 17:35:28 -07001073 DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001074 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 params->state = RNDIS_INITIALIZED;
1076 return rndis_init_response (configNr,
David Brownell7e27f182006-06-13 09:54:40 -07001077 (rndis_init_msg_type *) buf);
1078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 case REMOTE_NDIS_HALT_MSG:
David Brownell70790f632007-07-01 17:35:28 -07001080 DBG("%s: REMOTE_NDIS_HALT_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001081 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 params->state = RNDIS_UNINITIALIZED;
1083 if (params->dev) {
1084 netif_carrier_off (params->dev);
1085 netif_stop_queue (params->dev);
1086 }
1087 return 0;
David Brownell7e27f182006-06-13 09:54:40 -07001088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 case REMOTE_NDIS_QUERY_MSG:
David Brownell7e27f182006-06-13 09:54:40 -07001090 return rndis_query_response (configNr,
1091 (rndis_query_msg_type *) buf);
1092
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 case REMOTE_NDIS_SET_MSG:
David Brownell7e27f182006-06-13 09:54:40 -07001094 return rndis_set_response (configNr,
1095 (rndis_set_msg_type *) buf);
1096
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 case REMOTE_NDIS_RESET_MSG:
David Brownell70790f632007-07-01 17:35:28 -07001098 DBG("%s: REMOTE_NDIS_RESET_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001099 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 return rndis_reset_response (configNr,
David Brownell7e27f182006-06-13 09:54:40 -07001101 (rndis_reset_msg_type *) buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
1103 case REMOTE_NDIS_KEEPALIVE_MSG:
1104 /* For USB: host does this every 5 seconds */
David Brownell340600a2005-04-28 13:45:25 -07001105 if (rndis_debug > 1)
David Brownell70790f632007-07-01 17:35:28 -07001106 DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001107 __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 return rndis_keepalive_response (configNr,
David Brownell7e27f182006-06-13 09:54:40 -07001109 (rndis_keepalive_msg_type *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 buf);
David Brownell7e27f182006-06-13 09:54:40 -07001111
1112 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 /* At least Windows XP emits some undefined RNDIS messages.
1114 * In one case those messages seemed to relate to the host
1115 * suspending itself.
1116 */
David Brownell00274922007-11-19 12:58:36 -08001117 pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001118 __func__ , MsgType, MsgLength);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 {
1120 unsigned i;
1121 for (i = 0; i < MsgLength; i += 16) {
David Brownell70790f632007-07-01 17:35:28 -07001122 DBG("%03d: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 " %02x %02x %02x %02x"
1124 " %02x %02x %02x %02x"
1125 " %02x %02x %02x %02x"
1126 " %02x %02x %02x %02x"
1127 "\n",
1128 i,
1129 buf[i], buf [i+1],
1130 buf[i+2], buf[i+3],
1131 buf[i+4], buf [i+5],
1132 buf[i+6], buf[i+7],
1133 buf[i+8], buf [i+9],
1134 buf[i+10], buf[i+11],
1135 buf[i+12], buf [i+13],
1136 buf[i+14], buf[i+15]);
1137 }
1138 }
1139 break;
1140 }
David Brownell7e27f182006-06-13 09:54:40 -07001141
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 return -ENOTSUPP;
1143}
1144
1145int rndis_register (int (* rndis_control_ack) (struct net_device *))
1146{
1147 u8 i;
David Brownell7e27f182006-06-13 09:54:40 -07001148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
1150 if (!rndis_per_dev_params [i].used) {
1151 rndis_per_dev_params [i].used = 1;
1152 rndis_per_dev_params [i].ack = rndis_control_ack;
Harvey Harrison441b62c2008-03-03 16:08:34 -08001153 DBG("%s: configNr = %d\n", __func__, i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 return i;
1155 }
1156 }
David Brownell70790f632007-07-01 17:35:28 -07001157 DBG("failed\n");
David Brownell7e27f182006-06-13 09:54:40 -07001158
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 return -1;
1160}
1161
1162void rndis_deregister (int configNr)
1163{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001164 DBG("%s: \n", __func__ );
David Brownell7e27f182006-06-13 09:54:40 -07001165
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 if (configNr >= RNDIS_MAX_CONFIGS) return;
1167 rndis_per_dev_params [configNr].used = 0;
David Brownell7e27f182006-06-13 09:54:40 -07001168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 return;
1170}
1171
David Brownell7e27f182006-06-13 09:54:40 -07001172int rndis_set_param_dev (u8 configNr, struct net_device *dev,
David Brownell340600a2005-04-28 13:45:25 -07001173 struct net_device_stats *stats,
1174 u16 *cdc_filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001176 DBG("%s:\n", __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 if (!dev || !stats) return -1;
1178 if (configNr >= RNDIS_MAX_CONFIGS) return -1;
David Brownell7e27f182006-06-13 09:54:40 -07001179
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 rndis_per_dev_params [configNr].dev = dev;
1181 rndis_per_dev_params [configNr].stats = stats;
David Brownell340600a2005-04-28 13:45:25 -07001182 rndis_per_dev_params [configNr].filter = cdc_filter;
David Brownell7e27f182006-06-13 09:54:40 -07001183
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 return 0;
1185}
1186
1187int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
1188{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001189 DBG("%s:\n", __func__ );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 if (!vendorDescr) return -1;
1191 if (configNr >= RNDIS_MAX_CONFIGS) return -1;
David Brownell7e27f182006-06-13 09:54:40 -07001192
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 rndis_per_dev_params [configNr].vendorID = vendorID;
1194 rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
David Brownell7e27f182006-06-13 09:54:40 -07001195
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 return 0;
1197}
1198
1199int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
1200{
Harvey Harrison441b62c2008-03-03 16:08:34 -08001201 DBG("%s: %u %u\n", __func__, medium, speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 if (configNr >= RNDIS_MAX_CONFIGS) return -1;
David Brownell7e27f182006-06-13 09:54:40 -07001203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 rndis_per_dev_params [configNr].medium = medium;
1205 rndis_per_dev_params [configNr].speed = speed;
David Brownell7e27f182006-06-13 09:54:40 -07001206
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 return 0;
1208}
1209
1210void rndis_add_hdr (struct sk_buff *skb)
1211{
1212 struct rndis_packet_msg_type *header;
1213
1214 if (!skb)
1215 return;
1216 header = (void *) skb_push (skb, sizeof *header);
1217 memset (header, 0, sizeof *header);
David Brownell6cdee102005-04-18 17:39:34 -07001218 header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 header->MessageLength = cpu_to_le32(skb->len);
1220 header->DataOffset = __constant_cpu_to_le32 (36);
David Brownell6cdee102005-04-18 17:39:34 -07001221 header->DataLength = cpu_to_le32(skb->len - sizeof *header);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222}
1223
1224void rndis_free_response (int configNr, u8 *buf)
1225{
1226 rndis_resp_t *r;
1227 struct list_head *act, *tmp;
David Brownell7e27f182006-06-13 09:54:40 -07001228
1229 list_for_each_safe (act, tmp,
1230 &(rndis_per_dev_params [configNr].resp_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 {
1232 r = list_entry (act, rndis_resp_t, list);
1233 if (r && r->buf == buf) {
1234 list_del (&r->list);
1235 kfree (r);
1236 }
1237 }
1238}
1239
1240u8 *rndis_get_next_response (int configNr, u32 *length)
1241{
1242 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -07001243 struct list_head *act, *tmp;
1244
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 if (!length) return NULL;
David Brownell7e27f182006-06-13 09:54:40 -07001246
1247 list_for_each_safe (act, tmp,
1248 &(rndis_per_dev_params [configNr].resp_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 {
1250 r = list_entry (act, rndis_resp_t, list);
1251 if (!r->send) {
1252 r->send = 1;
1253 *length = r->length;
1254 return r->buf;
1255 }
1256 }
David Brownell7e27f182006-06-13 09:54:40 -07001257
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 return NULL;
1259}
1260
1261static rndis_resp_t *rndis_add_response (int configNr, u32 length)
1262{
1263 rndis_resp_t *r;
David Brownell7e27f182006-06-13 09:54:40 -07001264
David Brownell340600a2005-04-28 13:45:25 -07001265 /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
1267 if (!r) return NULL;
David Brownell7e27f182006-06-13 09:54:40 -07001268
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 r->buf = (u8 *) (r + 1);
1270 r->length = length;
1271 r->send = 0;
David Brownell7e27f182006-06-13 09:54:40 -07001272
1273 list_add_tail (&r->list,
1274 &(rndis_per_dev_params [configNr].resp_queue));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 return r;
1276}
1277
David Brownell6cdee102005-04-18 17:39:34 -07001278int rndis_rm_hdr(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279{
David Brownell6cdee102005-04-18 17:39:34 -07001280 /* tmp points to a struct rndis_packet_msg_type */
1281 __le32 *tmp = (void *) skb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
David Brownell6cdee102005-04-18 17:39:34 -07001283 /* MessageType, MessageLength */
1284 if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
1285 != get_unaligned(tmp++))
1286 return -EINVAL;
1287 tmp++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
David Brownell6cdee102005-04-18 17:39:34 -07001289 /* DataOffset, DataLength */
Harvey Harrisona5abdea2008-04-29 01:03:40 -07001290 if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8))
David Brownell6cdee102005-04-18 17:39:34 -07001291 return -EOVERFLOW;
Harvey Harrisona5abdea2008-04-29 01:03:40 -07001292 skb_trim(skb, get_unaligned_le32(tmp++));
David Brownell6cdee102005-04-18 17:39:34 -07001293
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 return 0;
1295}
1296
1297#ifdef CONFIG_USB_GADGET_DEBUG_FILES
1298
David Brownell7e27f182006-06-13 09:54:40 -07001299static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
1300 void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301{
1302 char *out = page;
1303 int len;
1304 rndis_params *param = (rndis_params *) data;
David Brownell7e27f182006-06-13 09:54:40 -07001305
1306 out += snprintf (out, count,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 "Config Nr. %d\n"
1308 "used : %s\n"
1309 "state : %s\n"
1310 "medium : 0x%08X\n"
1311 "speed : %d\n"
1312 "cable : %s\n"
1313 "vendor ID : 0x%08X\n"
David Brownell7e27f182006-06-13 09:54:40 -07001314 "vendor : %s\n",
1315 param->confignr, (param->used) ? "y" : "n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 ({ char *s = "?";
1317 switch (param->state) {
1318 case RNDIS_UNINITIALIZED:
1319 s = "RNDIS_UNINITIALIZED"; break;
1320 case RNDIS_INITIALIZED:
1321 s = "RNDIS_INITIALIZED"; break;
1322 case RNDIS_DATA_INITIALIZED:
1323 s = "RNDIS_DATA_INITIALIZED"; break;
1324 }; s; }),
David Brownell7e27f182006-06-13 09:54:40 -07001325 param->medium,
1326 (param->media_state) ? 0 : param->speed*100,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 (param->media_state) ? "disconnected" : "connected",
David Brownell7e27f182006-06-13 09:54:40 -07001328 param->vendorID, param->vendorDescr);
1329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 len = out - page;
1331 len -= off;
David Brownell7e27f182006-06-13 09:54:40 -07001332
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 if (len < count) {
1334 *eof = 1;
1335 if (len <= 0)
1336 return 0;
1337 } else
1338 len = count;
David Brownell7e27f182006-06-13 09:54:40 -07001339
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 *start = page + off;
1341 return len;
1342}
1343
David Brownell7e27f182006-06-13 09:54:40 -07001344static int rndis_proc_write (struct file *file, const char __user *buffer,
1345 unsigned long count, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346{
1347 rndis_params *p = data;
1348 u32 speed = 0;
1349 int i, fl_speed = 0;
David Brownell7e27f182006-06-13 09:54:40 -07001350
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 for (i = 0; i < count; i++) {
1352 char c;
1353 if (get_user(c, buffer))
1354 return -EFAULT;
1355 switch (c) {
1356 case '0':
1357 case '1':
1358 case '2':
1359 case '3':
1360 case '4':
1361 case '5':
1362 case '6':
1363 case '7':
1364 case '8':
1365 case '9':
1366 fl_speed = 1;
1367 speed = speed*10 + c - '0';
1368 break;
1369 case 'C':
1370 case 'c':
1371 rndis_signal_connect (p->confignr);
1372 break;
1373 case 'D':
1374 case 'd':
1375 rndis_signal_disconnect(p->confignr);
1376 break;
David Brownell7e27f182006-06-13 09:54:40 -07001377 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 if (fl_speed) p->speed = speed;
David Brownell70790f632007-07-01 17:35:28 -07001379 else DBG("%c is not valid\n", c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 break;
1381 }
David Brownell7e27f182006-06-13 09:54:40 -07001382
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 buffer++;
1384 }
David Brownell7e27f182006-06-13 09:54:40 -07001385
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 return count;
1387}
1388
1389#define NAME_TEMPLATE "driver/rndis-%03d"
1390
1391static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
1392
1393#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
1394
1395
David Brownell0e530b42008-04-05 14:17:14 -07001396int __init rndis_init (void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
1398 u8 i;
1399
1400 for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
1401#ifdef CONFIG_USB_GADGET_DEBUG_FILES
1402 char name [20];
1403
1404 sprintf (name, NAME_TEMPLATE, i);
1405 if (!(rndis_connect_state [i]
David Brownell7e27f182006-06-13 09:54:40 -07001406 = create_proc_entry (name, 0660, NULL)))
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 Brownell70790f632007-07-01 17:35:28 -07001413 DBG("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 return -EIO;
1415 }
1416
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 rndis_connect_state [i]->write_proc = rndis_proc_write;
1418 rndis_connect_state [i]->read_proc = rndis_proc_read;
1419 rndis_connect_state [i]->data = (void *)
1420 (rndis_per_dev_params + i);
1421#endif
1422 rndis_per_dev_params [i].confignr = i;
1423 rndis_per_dev_params [i].used = 0;
1424 rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
1425 rndis_per_dev_params [i].media_state
1426 = NDIS_MEDIA_STATE_DISCONNECTED;
1427 INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
1428 }
David Brownell7e27f182006-06-13 09:54:40 -07001429
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 return 0;
1431}
1432
1433void rndis_exit (void)
1434{
1435#ifdef CONFIG_USB_GADGET_DEBUG_FILES
1436 u8 i;
1437 char name [20];
David Brownell7e27f182006-06-13 09:54:40 -07001438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
1440 sprintf (name, NAME_TEMPLATE, i);
1441 remove_proc_entry (name, NULL);
1442 }
1443#endif
1444}
1445