]>
Commit | Line | Data |
---|---|---|
7e27f18c | 1 | /* |
1da177e4 | 2 | * RNDIS MSG parser |
7e27f18c | 3 | * |
1da177e4 | 4 | * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ |
7e27f18c | 5 | * |
1da177e4 | 6 | * Authors: Benedikt Spranger, Pengutronix |
7e27f18c DB |
7 | * Robert Schwebel, Pengutronix |
8 | * | |
1da177e4 LT |
9 | * This program is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU General Public License | |
7e27f18c DB |
11 | * version 2, as published by the Free Software Foundation. |
12 | * | |
1da177e4 LT |
13 | * This software was originally developed in conformance with |
14 | * Microsoft's Remote NDIS Specification License Agreement. | |
7e27f18c | 15 | * |
1da177e4 LT |
16 | * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de> |
17 | * Fixed message length bug in init_response | |
7e27f18c | 18 | * |
1da177e4 | 19 | * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de> |
7e27f18c | 20 | * Fixed rndis_rm_hdr length bug. |
1da177e4 LT |
21 | * |
22 | * Copyright (C) 2004 by David Brownell | |
23 | * updates to merge with Linux 2.6, better match RNDIS spec | |
24 | */ | |
25 | ||
1da177e4 LT |
26 | #include <linux/module.h> |
27 | #include <linux/moduleparam.h> | |
28 | #include <linux/kernel.h> | |
29 | #include <linux/errno.h> | |
1da177e4 LT |
30 | #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> | |
6cdee106 | 38 | #include <asm/unaligned.h> |
1da177e4 LT |
39 | |
40 | ||
41 | #undef RNDIS_PM | |
340600ab | 42 | #undef RNDIS_WAKEUP |
1da177e4 LT |
43 | #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 | |
1da177e4 | 56 | static int rndis_debug = 0; |
340600ab | 57 | module_param (rndis_debug, int, 0); |
1da177e4 | 58 | MODULE_PARM_DESC (rndis_debug, "enable debugging"); |
1da177e4 | 59 | #else |
1da177e4 | 60 | #define rndis_debug 0 |
1da177e4 LT |
61 | #endif |
62 | ||
00274921 DB |
63 | #define DBG(str,args...) do { \ |
64 | if (rndis_debug) \ | |
65 | pr_debug(str , ## args); \ | |
66 | } while (0) | |
67 | ||
1da177e4 LT |
68 | #define RNDIS_MAX_CONFIGS 1 |
69 | ||
70 | ||
71 | static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; | |
72 | ||
73 | /* Driver Version */ | |
74 | static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1); | |
75 | ||
76 | /* Function Prototypes */ | |
1da177e4 LT |
77 | static rndis_resp_t *rndis_add_response (int configNr, u32 length); |
78 | ||
79 | ||
340600ab | 80 | /* supported OIDs */ |
7e27f18c | 81 | static const u32 oid_supported_list [] = |
340600ab DB |
82 | { |
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 | |
7e27f18c | 102 | |
340600ab DB |
103 | /* 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 | ||
7e27f18c | 126 | /* mandatory 802.3 */ |
340600ab DB |
127 | /* 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, | |
7e27f18c | 133 | |
340600ab DB |
134 | /* 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 | ||
1da177e4 | 166 | /* NDIS Functions */ |
340600ab DB |
167 | static int |
168 | gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, | |
169 | rndis_resp_t *r) | |
1da177e4 | 170 | { |
7e27f18c DB |
171 | int retval = -ENOTSUPP; |
172 | u32 length = 4; /* usually */ | |
340600ab | 173 | __le32 *outbuf; |
1da177e4 LT |
174 | 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; | |
340600ab DB |
181 | |
182 | if (buf_len && rndis_debug > 1) { | |
70790f63 | 183 | DBG("query OID %08x value, len %d:\n", OID, buf_len); |
340600ab | 184 | for (i = 0; i < buf_len; i += 16) { |
70790f63 | 185 | DBG("%03d: %08x %08x %08x %08x\n", i, |
a5abdeaf HH |
186 | 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])); | |
340600ab DB |
190 | } |
191 | } | |
192 | ||
193 | /* response goes here, right after the header */ | |
194 | outbuf = (__le32 *) &resp[1]; | |
195 | resp->InformationBufferOffset = __constant_cpu_to_le32 (16); | |
196 | ||
1da177e4 LT |
197 | switch (OID) { |
198 | ||
199 | /* general oids (table 4-1) */ | |
200 | ||
201 | /* mandatory */ | |
202 | case OID_GEN_SUPPORTED_LIST: | |
441b62c1 | 203 | DBG("%s: OID_GEN_SUPPORTED_LIST\n", __func__); |
1da177e4 LT |
204 | length = sizeof (oid_supported_list); |
205 | count = length / sizeof (u32); | |
1da177e4 | 206 | for (i = 0; i < count; i++) |
340600ab | 207 | outbuf[i] = cpu_to_le32 (oid_supported_list[i]); |
1da177e4 LT |
208 | retval = 0; |
209 | break; | |
7e27f18c | 210 | |
1da177e4 LT |
211 | /* mandatory */ |
212 | case OID_GEN_HARDWARE_STATUS: | |
441b62c1 | 213 | DBG("%s: OID_GEN_HARDWARE_STATUS\n", __func__); |
7e27f18c | 214 | /* Bogus question! |
1da177e4 | 215 | * Hardware must be ready to receive high level protocols. |
7e27f18c | 216 | * BTW: |
1da177e4 LT |
217 | * reddite ergo quae sunt Caesaris Caesari |
218 | * et quae sunt Dei Deo! | |
219 | */ | |
340600ab | 220 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
221 | retval = 0; |
222 | break; | |
7e27f18c | 223 | |
1da177e4 LT |
224 | /* mandatory */ |
225 | case OID_GEN_MEDIA_SUPPORTED: | |
441b62c1 | 226 | DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__); |
340600ab | 227 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); |
1da177e4 LT |
228 | retval = 0; |
229 | break; | |
7e27f18c | 230 | |
1da177e4 LT |
231 | /* mandatory */ |
232 | case OID_GEN_MEDIA_IN_USE: | |
441b62c1 | 233 | DBG("%s: OID_GEN_MEDIA_IN_USE\n", __func__); |
1da177e4 | 234 | /* one medium, one transport... (maybe you do it better) */ |
340600ab | 235 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); |
1da177e4 LT |
236 | retval = 0; |
237 | break; | |
7e27f18c | 238 | |
1da177e4 LT |
239 | /* mandatory */ |
240 | case OID_GEN_MAXIMUM_FRAME_SIZE: | |
441b62c1 | 241 | DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); |
1da177e4 | 242 | if (rndis_per_dev_params [configNr].dev) { |
340600ab | 243 | *outbuf = cpu_to_le32 ( |
1da177e4 LT |
244 | rndis_per_dev_params [configNr].dev->mtu); |
245 | retval = 0; | |
1da177e4 LT |
246 | } |
247 | break; | |
7e27f18c | 248 | |
1da177e4 LT |
249 | /* mandatory */ |
250 | case OID_GEN_LINK_SPEED: | |
340600ab | 251 | if (rndis_debug > 1) |
441b62c1 | 252 | DBG("%s: OID_GEN_LINK_SPEED\n", __func__); |
1da177e4 | 253 | if (rndis_per_dev_params [configNr].media_state |
340600ab DB |
254 | == NDIS_MEDIA_STATE_DISCONNECTED) |
255 | *outbuf = __constant_cpu_to_le32 (0); | |
1da177e4 | 256 | else |
340600ab | 257 | *outbuf = cpu_to_le32 ( |
1da177e4 LT |
258 | rndis_per_dev_params [configNr].speed); |
259 | retval = 0; | |
260 | break; | |
261 | ||
262 | /* mandatory */ | |
263 | case OID_GEN_TRANSMIT_BLOCK_SIZE: | |
441b62c1 | 264 | DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); |
1da177e4 | 265 | if (rndis_per_dev_params [configNr].dev) { |
340600ab | 266 | *outbuf = cpu_to_le32 ( |
1da177e4 LT |
267 | rndis_per_dev_params [configNr].dev->mtu); |
268 | retval = 0; | |
269 | } | |
270 | break; | |
7e27f18c | 271 | |
1da177e4 LT |
272 | /* mandatory */ |
273 | case OID_GEN_RECEIVE_BLOCK_SIZE: | |
441b62c1 | 274 | DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); |
1da177e4 | 275 | if (rndis_per_dev_params [configNr].dev) { |
340600ab | 276 | *outbuf = cpu_to_le32 ( |
1da177e4 LT |
277 | rndis_per_dev_params [configNr].dev->mtu); |
278 | retval = 0; | |
279 | } | |
280 | break; | |
7e27f18c | 281 | |
1da177e4 LT |
282 | /* mandatory */ |
283 | case OID_GEN_VENDOR_ID: | |
441b62c1 | 284 | DBG("%s: OID_GEN_VENDOR_ID\n", __func__); |
340600ab | 285 | *outbuf = cpu_to_le32 ( |
1da177e4 LT |
286 | rndis_per_dev_params [configNr].vendorID); |
287 | retval = 0; | |
288 | break; | |
7e27f18c | 289 | |
1da177e4 LT |
290 | /* mandatory */ |
291 | case OID_GEN_VENDOR_DESCRIPTION: | |
441b62c1 | 292 | DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__); |
1da177e4 | 293 | length = strlen (rndis_per_dev_params [configNr].vendorDescr); |
340600ab | 294 | memcpy (outbuf, |
1da177e4 LT |
295 | rndis_per_dev_params [configNr].vendorDescr, length); |
296 | retval = 0; | |
297 | break; | |
298 | ||
299 | case OID_GEN_VENDOR_DRIVER_VERSION: | |
441b62c1 | 300 | DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__); |
1da177e4 | 301 | /* Created as LE */ |
340600ab | 302 | *outbuf = rndis_driver_version; |
1da177e4 LT |
303 | retval = 0; |
304 | break; | |
305 | ||
306 | /* mandatory */ | |
307 | case OID_GEN_CURRENT_PACKET_FILTER: | |
441b62c1 | 308 | DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__); |
340600ab | 309 | *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter); |
1da177e4 LT |
310 | retval = 0; |
311 | break; | |
312 | ||
313 | /* mandatory */ | |
314 | case OID_GEN_MAXIMUM_TOTAL_SIZE: | |
441b62c1 | 315 | DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__); |
340600ab | 316 | *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); |
1da177e4 LT |
317 | retval = 0; |
318 | break; | |
319 | ||
320 | /* mandatory */ | |
321 | case OID_GEN_MEDIA_CONNECT_STATUS: | |
340600ab | 322 | if (rndis_debug > 1) |
441b62c1 | 323 | DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); |
340600ab | 324 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
325 | .media_state); |
326 | retval = 0; | |
327 | break; | |
328 | ||
329 | case OID_GEN_PHYSICAL_MEDIUM: | |
441b62c1 | 330 | DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__); |
340600ab | 331 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
332 | 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 */ | |
441b62c1 | 340 | DBG("%s: OID_GEN_MAC_OPTIONS\n", __func__); |
340600ab | 341 | *outbuf = __constant_cpu_to_le32( |
1da177e4 LT |
342 | 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: | |
340600ab | 351 | if (rndis_debug > 1) |
441b62c1 | 352 | DBG("%s: OID_GEN_XMIT_OK\n", __func__); |
1da177e4 | 353 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 354 | *outbuf = cpu_to_le32 ( |
7e27f18c | 355 | rndis_per_dev_params [configNr].stats->tx_packets - |
1da177e4 LT |
356 | rndis_per_dev_params [configNr].stats->tx_errors - |
357 | rndis_per_dev_params [configNr].stats->tx_dropped); | |
358 | retval = 0; | |
1da177e4 LT |
359 | } |
360 | break; | |
361 | ||
362 | /* mandatory */ | |
363 | case OID_GEN_RCV_OK: | |
340600ab | 364 | if (rndis_debug > 1) |
441b62c1 | 365 | DBG("%s: OID_GEN_RCV_OK\n", __func__); |
1da177e4 | 366 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 367 | *outbuf = cpu_to_le32 ( |
7e27f18c | 368 | rndis_per_dev_params [configNr].stats->rx_packets - |
1da177e4 LT |
369 | rndis_per_dev_params [configNr].stats->rx_errors - |
370 | rndis_per_dev_params [configNr].stats->rx_dropped); | |
371 | retval = 0; | |
1da177e4 LT |
372 | } |
373 | break; | |
7e27f18c | 374 | |
1da177e4 LT |
375 | /* mandatory */ |
376 | case OID_GEN_XMIT_ERROR: | |
340600ab | 377 | if (rndis_debug > 1) |
441b62c1 | 378 | DBG("%s: OID_GEN_XMIT_ERROR\n", __func__); |
1da177e4 | 379 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 380 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
381 | .stats->tx_errors); |
382 | retval = 0; | |
1da177e4 LT |
383 | } |
384 | break; | |
7e27f18c | 385 | |
1da177e4 LT |
386 | /* mandatory */ |
387 | case OID_GEN_RCV_ERROR: | |
340600ab | 388 | if (rndis_debug > 1) |
441b62c1 | 389 | DBG("%s: OID_GEN_RCV_ERROR\n", __func__); |
1da177e4 | 390 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 391 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
392 | .stats->rx_errors); |
393 | retval = 0; | |
1da177e4 LT |
394 | } |
395 | break; | |
7e27f18c | 396 | |
1da177e4 LT |
397 | /* mandatory */ |
398 | case OID_GEN_RCV_NO_BUFFER: | |
441b62c1 | 399 | DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); |
1da177e4 | 400 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 401 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
402 | .stats->rx_dropped); |
403 | retval = 0; | |
1da177e4 LT |
404 | } |
405 | break; | |
406 | ||
407 | #ifdef RNDIS_OPTIONAL_STATS | |
408 | case OID_GEN_DIRECTED_BYTES_XMIT: | |
441b62c1 | 409 | DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__); |
7e27f18c | 410 | /* |
1da177e4 LT |
411 | * 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) { | |
340600ab | 416 | *outbuf = cpu_to_le32 ( |
1da177e4 | 417 | (rndis_per_dev_params [configNr] |
7e27f18c | 418 | .stats->tx_packets - |
1da177e4 LT |
419 | rndis_per_dev_params [configNr] |
420 | .stats->tx_errors - | |
421 | rndis_per_dev_params [configNr] | |
422 | .stats->tx_dropped) | |
423 | * 123); | |
424 | retval = 0; | |
1da177e4 LT |
425 | } |
426 | break; | |
7e27f18c | 427 | |
1da177e4 | 428 | case OID_GEN_DIRECTED_FRAMES_XMIT: |
441b62c1 | 429 | DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__); |
1da177e4 LT |
430 | /* dito */ |
431 | if (rndis_per_dev_params [configNr].stats) { | |
340600ab | 432 | *outbuf = cpu_to_le32 ( |
1da177e4 | 433 | (rndis_per_dev_params [configNr] |
7e27f18c | 434 | .stats->tx_packets - |
1da177e4 LT |
435 | rndis_per_dev_params [configNr] |
436 | .stats->tx_errors - | |
437 | rndis_per_dev_params [configNr] | |
438 | .stats->tx_dropped) | |
439 | / 123); | |
440 | retval = 0; | |
1da177e4 LT |
441 | } |
442 | break; | |
7e27f18c | 443 | |
1da177e4 | 444 | case OID_GEN_MULTICAST_BYTES_XMIT: |
441b62c1 | 445 | DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__); |
1da177e4 | 446 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 447 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
448 | .stats->multicast*1234); |
449 | retval = 0; | |
1da177e4 LT |
450 | } |
451 | break; | |
7e27f18c | 452 | |
1da177e4 | 453 | case OID_GEN_MULTICAST_FRAMES_XMIT: |
441b62c1 | 454 | DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__); |
1da177e4 | 455 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 456 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
457 | .stats->multicast); |
458 | retval = 0; | |
1da177e4 LT |
459 | } |
460 | break; | |
7e27f18c | 461 | |
1da177e4 | 462 | case OID_GEN_BROADCAST_BYTES_XMIT: |
441b62c1 | 463 | DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__); |
1da177e4 | 464 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 465 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
466 | .stats->tx_packets/42*255); |
467 | retval = 0; | |
1da177e4 LT |
468 | } |
469 | break; | |
7e27f18c | 470 | |
1da177e4 | 471 | case OID_GEN_BROADCAST_FRAMES_XMIT: |
441b62c1 | 472 | DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__); |
1da177e4 | 473 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 474 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
475 | .stats->tx_packets/42); |
476 | retval = 0; | |
1da177e4 LT |
477 | } |
478 | break; | |
7e27f18c | 479 | |
1da177e4 | 480 | case OID_GEN_DIRECTED_BYTES_RCV: |
441b62c1 | 481 | DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__); |
340600ab | 482 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
483 | retval = 0; |
484 | break; | |
7e27f18c | 485 | |
1da177e4 | 486 | case OID_GEN_DIRECTED_FRAMES_RCV: |
441b62c1 | 487 | DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__); |
340600ab | 488 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
489 | retval = 0; |
490 | break; | |
7e27f18c | 491 | |
1da177e4 | 492 | case OID_GEN_MULTICAST_BYTES_RCV: |
441b62c1 | 493 | DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__); |
1da177e4 | 494 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 495 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
496 | .stats->multicast * 1111); |
497 | retval = 0; | |
1da177e4 LT |
498 | } |
499 | break; | |
7e27f18c | 500 | |
1da177e4 | 501 | case OID_GEN_MULTICAST_FRAMES_RCV: |
441b62c1 | 502 | DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__); |
1da177e4 | 503 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 504 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
505 | .stats->multicast); |
506 | retval = 0; | |
1da177e4 LT |
507 | } |
508 | break; | |
7e27f18c | 509 | |
1da177e4 | 510 | case OID_GEN_BROADCAST_BYTES_RCV: |
441b62c1 | 511 | DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__); |
1da177e4 | 512 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 513 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
514 | .stats->rx_packets/42*255); |
515 | retval = 0; | |
1da177e4 LT |
516 | } |
517 | break; | |
7e27f18c | 518 | |
1da177e4 | 519 | case OID_GEN_BROADCAST_FRAMES_RCV: |
441b62c1 | 520 | DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__); |
1da177e4 | 521 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 522 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
523 | .stats->rx_packets/42); |
524 | retval = 0; | |
1da177e4 LT |
525 | } |
526 | break; | |
7e27f18c | 527 | |
1da177e4 | 528 | case OID_GEN_RCV_CRC_ERROR: |
441b62c1 | 529 | DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__); |
1da177e4 | 530 | if (rndis_per_dev_params [configNr].stats) { |
340600ab | 531 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] |
1da177e4 LT |
532 | .stats->rx_crc_errors); |
533 | retval = 0; | |
1da177e4 LT |
534 | } |
535 | break; | |
7e27f18c | 536 | |
1da177e4 | 537 | case OID_GEN_TRANSMIT_QUEUE_LENGTH: |
441b62c1 | 538 | DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__); |
340600ab | 539 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
540 | 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: | |
441b62c1 | 548 | DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__); |
1da177e4 LT |
549 | if (rndis_per_dev_params [configNr].dev) { |
550 | length = ETH_ALEN; | |
340600ab | 551 | memcpy (outbuf, |
1da177e4 LT |
552 | rndis_per_dev_params [configNr].host_mac, |
553 | length); | |
554 | retval = 0; | |
1da177e4 LT |
555 | } |
556 | break; | |
7e27f18c | 557 | |
1da177e4 LT |
558 | /* mandatory */ |
559 | case OID_802_3_CURRENT_ADDRESS: | |
441b62c1 | 560 | DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __func__); |
1da177e4 LT |
561 | if (rndis_per_dev_params [configNr].dev) { |
562 | length = ETH_ALEN; | |
340600ab | 563 | memcpy (outbuf, |
1da177e4 LT |
564 | rndis_per_dev_params [configNr].host_mac, |
565 | length); | |
566 | retval = 0; | |
567 | } | |
568 | break; | |
7e27f18c | 569 | |
1da177e4 LT |
570 | /* mandatory */ |
571 | case OID_802_3_MULTICAST_LIST: | |
441b62c1 | 572 | DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); |
1da177e4 | 573 | /* Multicast base address only */ |
340600ab | 574 | *outbuf = __constant_cpu_to_le32 (0xE0000000); |
1da177e4 LT |
575 | retval = 0; |
576 | break; | |
7e27f18c | 577 | |
1da177e4 LT |
578 | /* mandatory */ |
579 | case OID_802_3_MAXIMUM_LIST_SIZE: | |
441b62c1 | 580 | DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__); |
1da177e4 | 581 | /* Multicast base address only */ |
340600ab | 582 | *outbuf = __constant_cpu_to_le32 (1); |
1da177e4 LT |
583 | retval = 0; |
584 | break; | |
7e27f18c | 585 | |
1da177e4 | 586 | case OID_802_3_MAC_OPTIONS: |
441b62c1 | 587 | DBG("%s: OID_802_3_MAC_OPTIONS\n", __func__); |
1da177e4 LT |
588 | break; |
589 | ||
590 | /* ieee802.3 statistics OIDs (table 4-4) */ | |
591 | ||
592 | /* mandatory */ | |
593 | case OID_802_3_RCV_ERROR_ALIGNMENT: | |
441b62c1 | 594 | DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); |
340600ab DB |
595 | if (rndis_per_dev_params [configNr].stats) { |
596 | *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] | |
1da177e4 LT |
597 | .stats->rx_frame_errors); |
598 | retval = 0; | |
599 | } | |
600 | break; | |
7e27f18c | 601 | |
1da177e4 LT |
602 | /* mandatory */ |
603 | case OID_802_3_XMIT_ONE_COLLISION: | |
441b62c1 | 604 | DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__); |
340600ab | 605 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
606 | retval = 0; |
607 | break; | |
7e27f18c | 608 | |
1da177e4 LT |
609 | /* mandatory */ |
610 | case OID_802_3_XMIT_MORE_COLLISIONS: | |
441b62c1 | 611 | DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__); |
340600ab | 612 | *outbuf = __constant_cpu_to_le32 (0); |
1da177e4 LT |
613 | retval = 0; |
614 | break; | |
7e27f18c | 615 | |
1da177e4 LT |
616 | #ifdef RNDIS_OPTIONAL_STATS |
617 | case OID_802_3_XMIT_DEFERRED: | |
441b62c1 | 618 | DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__); |
1da177e4 LT |
619 | /* TODO */ |
620 | break; | |
7e27f18c | 621 | |
1da177e4 | 622 | case OID_802_3_XMIT_MAX_COLLISIONS: |
441b62c1 | 623 | DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__); |
1da177e4 LT |
624 | /* TODO */ |
625 | break; | |
7e27f18c | 626 | |
1da177e4 | 627 | case OID_802_3_RCV_OVERRUN: |
441b62c1 | 628 | DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__); |
1da177e4 LT |
629 | /* TODO */ |
630 | break; | |
7e27f18c | 631 | |
1da177e4 | 632 | case OID_802_3_XMIT_UNDERRUN: |
441b62c1 | 633 | DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__); |
1da177e4 LT |
634 | /* TODO */ |
635 | break; | |
7e27f18c | 636 | |
1da177e4 | 637 | case OID_802_3_XMIT_HEARTBEAT_FAILURE: |
441b62c1 | 638 | DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__); |
1da177e4 LT |
639 | /* TODO */ |
640 | break; | |
7e27f18c | 641 | |
1da177e4 | 642 | case OID_802_3_XMIT_TIMES_CRS_LOST: |
441b62c1 | 643 | DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__); |
1da177e4 LT |
644 | /* TODO */ |
645 | break; | |
7e27f18c | 646 | |
1da177e4 | 647 | case OID_802_3_XMIT_LATE_COLLISIONS: |
441b62c1 | 648 | DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__); |
1da177e4 | 649 | /* TODO */ |
7e27f18c | 650 | break; |
1da177e4 LT |
651 | #endif /* RNDIS_OPTIONAL_STATS */ |
652 | ||
653 | #ifdef RNDIS_PM | |
654 | /* power management OIDs (table 4-5) */ | |
655 | case OID_PNP_CAPABILITIES: | |
441b62c1 | 656 | DBG("%s: OID_PNP_CAPABILITIES\n", __func__); |
1da177e4 | 657 | |
340600ab | 658 | /* for now, no wakeup capabilities */ |
1da177e4 | 659 | length = sizeof (struct NDIS_PNP_CAPABILITIES); |
340600ab | 660 | memset(outbuf, 0, length); |
1da177e4 LT |
661 | retval = 0; |
662 | break; | |
663 | case OID_PNP_QUERY_POWER: | |
441b62c1 | 664 | DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__, |
a5abdeaf | 665 | get_unaligned_le32(buf) - 1); |
340600ab DB |
666 | /* only suspend is a real power state, and |
667 | * it can't be entered by OID_PNP_SET_POWER... | |
668 | */ | |
669 | length = 0; | |
1da177e4 LT |
670 | retval = 0; |
671 | break; | |
672 | #endif | |
673 | ||
674 | default: | |
00274921 | 675 | pr_warning("%s: query unknown OID 0x%08X\n", |
441b62c1 | 676 | __func__, OID); |
1da177e4 | 677 | } |
340600ab DB |
678 | if (retval < 0) |
679 | length = 0; | |
7e27f18c | 680 | |
1da177e4 | 681 | resp->InformationBufferLength = cpu_to_le32 (length); |
340600ab DB |
682 | r->length = length + sizeof *resp; |
683 | resp->MessageLength = cpu_to_le32 (r->length); | |
1da177e4 LT |
684 | return retval; |
685 | } | |
686 | ||
7e27f18c DB |
687 | static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, |
688 | rndis_resp_t *r) | |
1da177e4 LT |
689 | { |
690 | rndis_set_cmplt_type *resp; | |
7e27f18c | 691 | int i, retval = -ENOTSUPP; |
1da177e4 LT |
692 | 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 | ||
340600ab | 700 | if (buf_len && rndis_debug > 1) { |
70790f63 | 701 | DBG("set OID %08x value, len %d:\n", OID, buf_len); |
340600ab | 702 | for (i = 0; i < buf_len; i += 16) { |
70790f63 | 703 | DBG("%03d: %08x %08x %08x %08x\n", i, |
a5abdeaf HH |
704 | 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])); | |
340600ab | 708 | } |
1da177e4 LT |
709 | } |
710 | ||
340600ab | 711 | params = &rndis_per_dev_params [configNr]; |
1da177e4 LT |
712 | switch (OID) { |
713 | case OID_GEN_CURRENT_PACKET_FILTER: | |
1da177e4 | 714 | |
340600ab DB |
715 | /* these NDIS_PACKET_TYPE_* bitflags are shared with |
716 | * cdc_filter; it's not RNDIS-specific | |
1da177e4 LT |
717 | * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: |
718 | * PROMISCUOUS, DIRECTED, | |
719 | * MULTICAST, ALL_MULTICAST, BROADCAST | |
720 | */ | |
a5abdeaf | 721 | *params->filter = (u16)get_unaligned_le32(buf); |
70790f63 | 722 | DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", |
441b62c1 | 723 | __func__, *params->filter); |
1da177e4 LT |
724 | |
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 | */ | |
340600ab DB |
729 | #ifdef RNDIS_PM |
730 | update_linkstate: | |
731 | #endif | |
732 | retval = 0; | |
733 | if (*params->filter) { | |
1da177e4 LT |
734 | 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; | |
7e27f18c | 744 | |
1da177e4 | 745 | case OID_802_3_MULTICAST_LIST: |
7e27f18c | 746 | /* I think we can ignore this */ |
441b62c1 | 747 | DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); |
1da177e4 LT |
748 | 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; | |
70790f63 | 755 | DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", |
441b62c1 | 756 | __func__, |
1da177e4 LT |
757 | 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: | |
340600ab DB |
766 | /* 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 | */ | |
a5abdeaf | 771 | i = get_unaligned_le32(buf); |
441b62c1 | 772 | DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1); |
340600ab DB |
773 | 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 | } | |
1da177e4 LT |
784 | break; |
785 | ||
340600ab DB |
786 | #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 | |
1da177e4 LT |
790 | #endif |
791 | ||
340600ab DB |
792 | #endif /* RNDIS_PM */ |
793 | ||
1da177e4 | 794 | default: |
00274921 | 795 | pr_warning("%s: set unknown OID 0x%08X, size %d\n", |
441b62c1 | 796 | __func__, OID, buf_len); |
1da177e4 | 797 | } |
7e27f18c | 798 | |
1da177e4 LT |
799 | return retval; |
800 | } | |
801 | ||
7e27f18c DB |
802 | /* |
803 | * Response Functions | |
1da177e4 LT |
804 | */ |
805 | ||
806 | static int rndis_init_response (int configNr, rndis_init_msg_type *buf) | |
807 | { | |
7e27f18c | 808 | rndis_init_cmplt_type *resp; |
1da177e4 | 809 | rndis_resp_t *r; |
7e27f18c | 810 | |
1da177e4 | 811 | if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; |
7e27f18c | 812 | |
1da177e4 | 813 | r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); |
340600ab DB |
814 | if (!r) |
815 | return -ENOMEM; | |
1da177e4 | 816 | resp = (rndis_init_cmplt_type *) r->buf; |
7e27f18c | 817 | |
1da177e4 LT |
818 | 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); | |
7e27f18c | 836 | |
1da177e4 | 837 | if (rndis_per_dev_params [configNr].ack) |
7e27f18c DB |
838 | rndis_per_dev_params [configNr].ack ( |
839 | rndis_per_dev_params [configNr].dev); | |
840 | ||
1da177e4 LT |
841 | return 0; |
842 | } | |
843 | ||
844 | static int rndis_query_response (int configNr, rndis_query_msg_type *buf) | |
845 | { | |
846 | rndis_query_cmplt_type *resp; | |
847 | rndis_resp_t *r; | |
7e27f18c | 848 | |
441b62c1 | 849 | // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); |
1da177e4 | 850 | if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; |
7e27f18c | 851 | |
8763716b ST |
852 | /* |
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 | |
1da177e4 | 857 | */ |
8763716b ST |
858 | r = rndis_add_response (configNr, |
859 | sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type)); | |
340600ab DB |
860 | if (!r) |
861 | return -ENOMEM; | |
1da177e4 | 862 | resp = (rndis_query_cmplt_type *) r->buf; |
7e27f18c | 863 | |
1da177e4 | 864 | resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); |
1da177e4 | 865 | resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ |
7e27f18c | 866 | |
340600ab DB |
867 | 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)) { | |
1da177e4 LT |
872 | /* OID not supported */ |
873 | resp->Status = __constant_cpu_to_le32 ( | |
874 | RNDIS_STATUS_NOT_SUPPORTED); | |
340600ab | 875 | resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp); |
1da177e4 LT |
876 | 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); | |
7e27f18c | 880 | |
1da177e4 | 881 | if (rndis_per_dev_params [configNr].ack) |
7e27f18c DB |
882 | rndis_per_dev_params [configNr].ack ( |
883 | rndis_per_dev_params [configNr].dev); | |
1da177e4 LT |
884 | return 0; |
885 | } | |
886 | ||
887 | static 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; | |
7e27f18c | 892 | |
1da177e4 | 893 | r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); |
340600ab DB |
894 | if (!r) |
895 | return -ENOMEM; | |
1da177e4 | 896 | resp = (rndis_set_cmplt_type *) r->buf; |
1da177e4 LT |
897 | |
898 | BufLength = le32_to_cpu (buf->InformationBufferLength); | |
899 | BufOffset = le32_to_cpu (buf->InformationBufferOffset); | |
900 | ||
901 | #ifdef VERBOSE | |
441b62c1 HH |
902 | DBG("%s: Length: %d\n", __func__, BufLength); |
903 | DBG("%s: Offset: %d\n", __func__, BufOffset); | |
904 | DBG("%s: InfoBuffer: ", __func__); | |
7e27f18c | 905 | |
1da177e4 | 906 | for (i = 0; i < BufLength; i++) { |
70790f63 | 907 | DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); |
1da177e4 | 908 | } |
7e27f18c | 909 | |
70790f63 | 910 | DBG("\n"); |
1da177e4 | 911 | #endif |
7e27f18c | 912 | |
1da177e4 LT |
913 | 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 */ | |
7e27f18c DB |
916 | 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 | ||
1da177e4 | 922 | if (rndis_per_dev_params [configNr].ack) |
7e27f18c DB |
923 | rndis_per_dev_params [configNr].ack ( |
924 | rndis_per_dev_params [configNr].dev); | |
925 | ||
1da177e4 LT |
926 | return 0; |
927 | } | |
928 | ||
929 | static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) | |
930 | { | |
931 | rndis_reset_cmplt_type *resp; | |
932 | rndis_resp_t *r; | |
7e27f18c | 933 | |
1da177e4 | 934 | r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); |
340600ab DB |
935 | if (!r) |
936 | return -ENOMEM; | |
1da177e4 | 937 | resp = (rndis_reset_cmplt_type *) r->buf; |
7e27f18c | 938 | |
1da177e4 LT |
939 | 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); | |
7e27f18c | 944 | |
1da177e4 | 945 | if (rndis_per_dev_params [configNr].ack) |
7e27f18c DB |
946 | rndis_per_dev_params [configNr].ack ( |
947 | rndis_per_dev_params [configNr].dev); | |
1da177e4 LT |
948 | |
949 | return 0; | |
950 | } | |
951 | ||
952 | static int rndis_keepalive_response (int configNr, | |
7e27f18c | 953 | rndis_keepalive_msg_type *buf) |
1da177e4 LT |
954 | { |
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)); | |
340600ab DB |
961 | if (!r) |
962 | return -ENOMEM; | |
1da177e4 | 963 | resp = (rndis_keepalive_cmplt_type *) r->buf; |
7e27f18c | 964 | |
1da177e4 LT |
965 | 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); | |
7e27f18c | 970 | |
1da177e4 | 971 | if (rndis_per_dev_params [configNr].ack) |
7e27f18c DB |
972 | rndis_per_dev_params [configNr].ack ( |
973 | rndis_per_dev_params [configNr].dev); | |
974 | ||
1da177e4 LT |
975 | return 0; |
976 | } | |
977 | ||
978 | ||
7e27f18c DB |
979 | /* |
980 | * Device to Host Comunication | |
1da177e4 LT |
981 | */ |
982 | static int rndis_indicate_status_msg (int configNr, u32 status) | |
983 | { | |
7e27f18c | 984 | rndis_indicate_status_msg_type *resp; |
1da177e4 | 985 | rndis_resp_t *r; |
7e27f18c | 986 | |
1da177e4 | 987 | if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) |
7e27f18c DB |
988 | return -ENOTSUPP; |
989 | ||
990 | r = rndis_add_response (configNr, | |
1da177e4 | 991 | sizeof (rndis_indicate_status_msg_type)); |
340600ab DB |
992 | if (!r) |
993 | return -ENOMEM; | |
1da177e4 | 994 | resp = (rndis_indicate_status_msg_type *) r->buf; |
7e27f18c | 995 | |
1da177e4 LT |
996 | 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); | |
7e27f18c DB |
1002 | |
1003 | if (rndis_per_dev_params [configNr].ack) | |
1004 | rndis_per_dev_params [configNr].ack ( | |
1005 | rndis_per_dev_params [configNr].dev); | |
1da177e4 LT |
1006 | return 0; |
1007 | } | |
1008 | ||
1009 | int rndis_signal_connect (int configNr) | |
1010 | { | |
1011 | rndis_per_dev_params [configNr].media_state | |
1012 | = NDIS_MEDIA_STATE_CONNECTED; | |
7e27f18c | 1013 | return rndis_indicate_status_msg (configNr, |
1da177e4 LT |
1014 | RNDIS_STATUS_MEDIA_CONNECT); |
1015 | } | |
1016 | ||
1017 | int 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 | ||
340600ab DB |
1025 | void rndis_uninit (int configNr) |
1026 | { | |
486e2df6 DB |
1027 | u8 *buf; |
1028 | u32 length; | |
1029 | ||
340600ab DB |
1030 | if (configNr >= RNDIS_MAX_CONFIGS) |
1031 | return; | |
1032 | rndis_per_dev_params [configNr].used = 0; | |
1033 | rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; | |
486e2df6 DB |
1034 | |
1035 | /* drain the response queue */ | |
1036 | while ((buf = rndis_get_next_response(configNr, &length))) | |
1037 | rndis_free_response(configNr, buf); | |
340600ab DB |
1038 | } |
1039 | ||
1da177e4 LT |
1040 | void rndis_set_host_mac (int configNr, const u8 *addr) |
1041 | { | |
1042 | rndis_per_dev_params [configNr].host_mac = addr; | |
1043 | } | |
1044 | ||
7e27f18c DB |
1045 | /* |
1046 | * Message Parser | |
1da177e4 LT |
1047 | */ |
1048 | int rndis_msg_parser (u8 configNr, u8 *buf) | |
1049 | { | |
1050 | u32 MsgType, MsgLength; | |
1051 | __le32 *tmp; | |
1052 | struct rndis_params *params; | |
7e27f18c | 1053 | |
1da177e4 LT |
1054 | if (!buf) |
1055 | return -ENOMEM; | |
7e27f18c DB |
1056 | |
1057 | tmp = (__le32 *) buf; | |
a5abdeaf HH |
1058 | MsgType = get_unaligned_le32(tmp++); |
1059 | MsgLength = get_unaligned_le32(tmp++); | |
7e27f18c | 1060 | |
1da177e4 LT |
1061 | if (configNr >= RNDIS_MAX_CONFIGS) |
1062 | return -ENOTSUPP; | |
1063 | params = &rndis_per_dev_params [configNr]; | |
7e27f18c | 1064 | |
340600ab DB |
1065 | /* 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 | ||
1da177e4 | 1070 | /* For USB: responses may take up to 10 seconds */ |
340600ab | 1071 | switch (MsgType) { |
1da177e4 | 1072 | case REMOTE_NDIS_INITIALIZE_MSG: |
70790f63 | 1073 | DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", |
441b62c1 | 1074 | __func__ ); |
1da177e4 LT |
1075 | params->state = RNDIS_INITIALIZED; |
1076 | return rndis_init_response (configNr, | |
7e27f18c DB |
1077 | (rndis_init_msg_type *) buf); |
1078 | ||
1da177e4 | 1079 | case REMOTE_NDIS_HALT_MSG: |
70790f63 | 1080 | DBG("%s: REMOTE_NDIS_HALT_MSG\n", |
441b62c1 | 1081 | __func__ ); |
1da177e4 LT |
1082 | params->state = RNDIS_UNINITIALIZED; |
1083 | if (params->dev) { | |
1084 | netif_carrier_off (params->dev); | |
1085 | netif_stop_queue (params->dev); | |
1086 | } | |
1087 | return 0; | |
7e27f18c | 1088 | |
1da177e4 | 1089 | case REMOTE_NDIS_QUERY_MSG: |
7e27f18c DB |
1090 | return rndis_query_response (configNr, |
1091 | (rndis_query_msg_type *) buf); | |
1092 | ||
1da177e4 | 1093 | case REMOTE_NDIS_SET_MSG: |
7e27f18c DB |
1094 | return rndis_set_response (configNr, |
1095 | (rndis_set_msg_type *) buf); | |
1096 | ||
1da177e4 | 1097 | case REMOTE_NDIS_RESET_MSG: |
70790f63 | 1098 | DBG("%s: REMOTE_NDIS_RESET_MSG\n", |
441b62c1 | 1099 | __func__ ); |
1da177e4 | 1100 | return rndis_reset_response (configNr, |
7e27f18c | 1101 | (rndis_reset_msg_type *) buf); |
1da177e4 LT |
1102 | |
1103 | case REMOTE_NDIS_KEEPALIVE_MSG: | |
1104 | /* For USB: host does this every 5 seconds */ | |
340600ab | 1105 | if (rndis_debug > 1) |
70790f63 | 1106 | DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", |
441b62c1 | 1107 | __func__ ); |
1da177e4 | 1108 | return rndis_keepalive_response (configNr, |
7e27f18c | 1109 | (rndis_keepalive_msg_type *) |
1da177e4 | 1110 | buf); |
7e27f18c DB |
1111 | |
1112 | default: | |
1da177e4 LT |
1113 | /* 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 | */ | |
00274921 | 1117 | pr_warning("%s: unknown RNDIS message 0x%08X len %d\n", |
441b62c1 | 1118 | __func__ , MsgType, MsgLength); |
1da177e4 LT |
1119 | { |
1120 | unsigned i; | |
1121 | for (i = 0; i < MsgLength; i += 16) { | |
70790f63 | 1122 | DBG("%03d: " |
1da177e4 LT |
1123 | " %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 | } | |
7e27f18c | 1141 | |
1da177e4 LT |
1142 | return -ENOTSUPP; |
1143 | } | |
1144 | ||
1145 | int rndis_register (int (* rndis_control_ack) (struct net_device *)) | |
1146 | { | |
1147 | u8 i; | |
7e27f18c | 1148 | |
1da177e4 LT |
1149 | 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; | |
441b62c1 | 1153 | DBG("%s: configNr = %d\n", __func__, i); |
1da177e4 LT |
1154 | return i; |
1155 | } | |
1156 | } | |
70790f63 | 1157 | DBG("failed\n"); |
7e27f18c | 1158 | |
1da177e4 LT |
1159 | return -1; |
1160 | } | |
1161 | ||
1162 | void rndis_deregister (int configNr) | |
1163 | { | |
441b62c1 | 1164 | DBG("%s: \n", __func__ ); |
7e27f18c | 1165 | |
1da177e4 LT |
1166 | if (configNr >= RNDIS_MAX_CONFIGS) return; |
1167 | rndis_per_dev_params [configNr].used = 0; | |
7e27f18c | 1168 | |
1da177e4 LT |
1169 | return; |
1170 | } | |
1171 | ||
7e27f18c | 1172 | int rndis_set_param_dev (u8 configNr, struct net_device *dev, |
340600ab DB |
1173 | struct net_device_stats *stats, |
1174 | u16 *cdc_filter) | |
1da177e4 | 1175 | { |
441b62c1 | 1176 | DBG("%s:\n", __func__ ); |
1da177e4 LT |
1177 | if (!dev || !stats) return -1; |
1178 | if (configNr >= RNDIS_MAX_CONFIGS) return -1; | |
7e27f18c | 1179 | |
1da177e4 LT |
1180 | rndis_per_dev_params [configNr].dev = dev; |
1181 | rndis_per_dev_params [configNr].stats = stats; | |
340600ab | 1182 | rndis_per_dev_params [configNr].filter = cdc_filter; |
7e27f18c | 1183 | |
1da177e4 LT |
1184 | return 0; |
1185 | } | |
1186 | ||
1187 | int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) | |
1188 | { | |
441b62c1 | 1189 | DBG("%s:\n", __func__ ); |
1da177e4 LT |
1190 | if (!vendorDescr) return -1; |
1191 | if (configNr >= RNDIS_MAX_CONFIGS) return -1; | |
7e27f18c | 1192 | |
1da177e4 LT |
1193 | rndis_per_dev_params [configNr].vendorID = vendorID; |
1194 | rndis_per_dev_params [configNr].vendorDescr = vendorDescr; | |
7e27f18c | 1195 | |
1da177e4 LT |
1196 | return 0; |
1197 | } | |
1198 | ||
1199 | int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) | |
1200 | { | |
441b62c1 | 1201 | DBG("%s: %u %u\n", __func__, medium, speed); |
1da177e4 | 1202 | if (configNr >= RNDIS_MAX_CONFIGS) return -1; |
7e27f18c | 1203 | |
1da177e4 LT |
1204 | rndis_per_dev_params [configNr].medium = medium; |
1205 | rndis_per_dev_params [configNr].speed = speed; | |
7e27f18c | 1206 | |
1da177e4 LT |
1207 | return 0; |
1208 | } | |
1209 | ||
1210 | void 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); | |
6cdee106 | 1218 | header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG); |
1da177e4 LT |
1219 | header->MessageLength = cpu_to_le32(skb->len); |
1220 | header->DataOffset = __constant_cpu_to_le32 (36); | |
6cdee106 | 1221 | header->DataLength = cpu_to_le32(skb->len - sizeof *header); |
1da177e4 LT |
1222 | } |
1223 | ||
1224 | void rndis_free_response (int configNr, u8 *buf) | |
1225 | { | |
1226 | rndis_resp_t *r; | |
1227 | struct list_head *act, *tmp; | |
7e27f18c DB |
1228 | |
1229 | list_for_each_safe (act, tmp, | |
1230 | &(rndis_per_dev_params [configNr].resp_queue)) | |
1da177e4 LT |
1231 | { |
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 | ||
1240 | u8 *rndis_get_next_response (int configNr, u32 *length) | |
1241 | { | |
1242 | rndis_resp_t *r; | |
7e27f18c DB |
1243 | struct list_head *act, *tmp; |
1244 | ||
1da177e4 | 1245 | if (!length) return NULL; |
7e27f18c DB |
1246 | |
1247 | list_for_each_safe (act, tmp, | |
1248 | &(rndis_per_dev_params [configNr].resp_queue)) | |
1da177e4 LT |
1249 | { |
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 | } | |
7e27f18c | 1257 | |
1da177e4 LT |
1258 | return NULL; |
1259 | } | |
1260 | ||
1261 | static rndis_resp_t *rndis_add_response (int configNr, u32 length) | |
1262 | { | |
1263 | rndis_resp_t *r; | |
7e27f18c | 1264 | |
340600ab | 1265 | /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */ |
1da177e4 LT |
1266 | r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); |
1267 | if (!r) return NULL; | |
7e27f18c | 1268 | |
1da177e4 LT |
1269 | r->buf = (u8 *) (r + 1); |
1270 | r->length = length; | |
1271 | r->send = 0; | |
7e27f18c DB |
1272 | |
1273 | list_add_tail (&r->list, | |
1274 | &(rndis_per_dev_params [configNr].resp_queue)); | |
1da177e4 LT |
1275 | return r; |
1276 | } | |
1277 | ||
6cdee106 | 1278 | int rndis_rm_hdr(struct sk_buff *skb) |
1da177e4 | 1279 | { |
6cdee106 DB |
1280 | /* tmp points to a struct rndis_packet_msg_type */ |
1281 | __le32 *tmp = (void *) skb->data; | |
1da177e4 | 1282 | |
6cdee106 DB |
1283 | /* MessageType, MessageLength */ |
1284 | if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG) | |
1285 | != get_unaligned(tmp++)) | |
1286 | return -EINVAL; | |
1287 | tmp++; | |
1288 | ||
1289 | /* DataOffset, DataLength */ | |
a5abdeaf | 1290 | if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) |
6cdee106 | 1291 | return -EOVERFLOW; |
a5abdeaf | 1292 | skb_trim(skb, get_unaligned_le32(tmp++)); |
1da177e4 | 1293 | |
1da177e4 LT |
1294 | return 0; |
1295 | } | |
1296 | ||
1297 | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | |
1298 | ||
7e27f18c DB |
1299 | static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, |
1300 | void *data) | |
1da177e4 LT |
1301 | { |
1302 | char *out = page; | |
1303 | int len; | |
1304 | rndis_params *param = (rndis_params *) data; | |
7e27f18c DB |
1305 | |
1306 | out += snprintf (out, count, | |
1da177e4 LT |
1307 | "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" | |
7e27f18c DB |
1314 | "vendor : %s\n", |
1315 | param->confignr, (param->used) ? "y" : "n", | |
1da177e4 LT |
1316 | ({ 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; }), | |
7e27f18c DB |
1325 | param->medium, |
1326 | (param->media_state) ? 0 : param->speed*100, | |
1da177e4 | 1327 | (param->media_state) ? "disconnected" : "connected", |
7e27f18c DB |
1328 | param->vendorID, param->vendorDescr); |
1329 | ||
1da177e4 LT |
1330 | len = out - page; |
1331 | len -= off; | |
7e27f18c | 1332 | |
1da177e4 LT |
1333 | if (len < count) { |
1334 | *eof = 1; | |
1335 | if (len <= 0) | |
1336 | return 0; | |
1337 | } else | |
1338 | len = count; | |
7e27f18c | 1339 | |
1da177e4 LT |
1340 | *start = page + off; |
1341 | return len; | |
1342 | } | |
1343 | ||
7e27f18c DB |
1344 | static int rndis_proc_write (struct file *file, const char __user *buffer, |
1345 | unsigned long count, void *data) | |
1da177e4 LT |
1346 | { |
1347 | rndis_params *p = data; | |
1348 | u32 speed = 0; | |
1349 | int i, fl_speed = 0; | |
7e27f18c | 1350 | |
1da177e4 LT |
1351 | 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; | |
7e27f18c | 1377 | default: |
1da177e4 | 1378 | if (fl_speed) p->speed = speed; |
70790f63 | 1379 | else DBG("%c is not valid\n", c); |
1da177e4 LT |
1380 | break; |
1381 | } | |
7e27f18c | 1382 | |
1da177e4 LT |
1383 | buffer++; |
1384 | } | |
7e27f18c | 1385 | |
1da177e4 LT |
1386 | return count; |
1387 | } | |
1388 | ||
1389 | #define NAME_TEMPLATE "driver/rndis-%03d" | |
1390 | ||
1391 | static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; | |
1392 | ||
1393 | #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ | |
1394 | ||
1395 | ||
0e530b45 | 1396 | int __init rndis_init (void) |
1da177e4 LT |
1397 | { |
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] | |
7e27f18c | 1406 | = create_proc_entry (name, 0660, NULL))) |
1da177e4 | 1407 | { |
441b62c1 | 1408 | DBG("%s :remove entries", __func__); |
1da177e4 LT |
1409 | while (i) { |
1410 | sprintf (name, NAME_TEMPLATE, --i); | |
1411 | remove_proc_entry (name, NULL); | |
1412 | } | |
70790f63 | 1413 | DBG("\n"); |
1da177e4 LT |
1414 | return -EIO; |
1415 | } | |
1416 | ||
1da177e4 LT |
1417 | 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 | } | |
7e27f18c | 1429 | |
1da177e4 LT |
1430 | return 0; |
1431 | } | |
1432 | ||
1433 | void rndis_exit (void) | |
1434 | { | |
1435 | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | |
1436 | u8 i; | |
1437 | char name [20]; | |
7e27f18c | 1438 | |
1da177e4 LT |
1439 | 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 |