]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * RNDIS MSG parser | |
3 | * | |
4 | * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ | |
5 | * | |
6 | * Authors: Benedikt Spranger, Pengutronix | |
7 | * Robert Schwebel, Pengutronix | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * version 2, as published by the Free Software Foundation. | |
12 | * | |
13 | * This software was originally developed in conformance with | |
14 | * Microsoft's Remote NDIS Specification License Agreement. | |
15 | * | |
16 | * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de> | |
17 | * Fixed message length bug in init_response | |
18 | * | |
19 | * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de> | |
20 | * Fixed rndis_rm_hdr length bug. | |
21 | * | |
22 | * Copyright (C) 2004 by David Brownell | |
23 | * updates to merge with Linux 2.6, better match RNDIS spec | |
24 | */ | |
25 | ||
26 | #include <linux/config.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/moduleparam.h> | |
29 | #include <linux/kernel.h> | |
30 | #include <linux/errno.h> | |
31 | #include <linux/version.h> | |
32 | #include <linux/init.h> | |
33 | #include <linux/list.h> | |
34 | #include <linux/proc_fs.h> | |
35 | #include <linux/netdevice.h> | |
36 | ||
37 | #include <asm/io.h> | |
38 | #include <asm/byteorder.h> | |
39 | #include <asm/system.h> | |
6cdee106 | 40 | #include <asm/unaligned.h> |
1da177e4 LT |
41 | |
42 | ||
43 | #undef RNDIS_PM | |
44 | #undef VERBOSE | |
45 | ||
46 | #include "rndis.h" | |
47 | ||
48 | ||
49 | /* The driver for your USB chip needs to support ep0 OUT to work with | |
50 | * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). | |
51 | * | |
52 | * Windows hosts need an INF file like Documentation/usb/linux.inf | |
53 | * and will be happier if you provide the host_addr module parameter. | |
54 | */ | |
55 | ||
56 | #if 0 | |
57 | #define DEBUG(str,args...) do { \ | |
58 | if (rndis_debug) \ | |
59 | printk(KERN_DEBUG str , ## args ); \ | |
60 | } while (0) | |
61 | static int rndis_debug = 0; | |
62 | ||
63 | module_param (rndis_debug, bool, 0); | |
64 | MODULE_PARM_DESC (rndis_debug, "enable debugging"); | |
65 | ||
66 | #else | |
67 | ||
68 | #define rndis_debug 0 | |
69 | #define DEBUG(str,args...) do{}while(0) | |
70 | #endif | |
71 | ||
72 | #define RNDIS_MAX_CONFIGS 1 | |
73 | ||
74 | ||
75 | static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; | |
76 | ||
77 | /* Driver Version */ | |
78 | static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1); | |
79 | ||
80 | /* Function Prototypes */ | |
81 | static int rndis_init_response (int configNr, rndis_init_msg_type *buf); | |
82 | static int rndis_query_response (int configNr, rndis_query_msg_type *buf); | |
83 | static int rndis_set_response (int configNr, rndis_set_msg_type *buf); | |
84 | static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf); | |
85 | static int rndis_keepalive_response (int configNr, | |
86 | rndis_keepalive_msg_type *buf); | |
87 | ||
88 | static rndis_resp_t *rndis_add_response (int configNr, u32 length); | |
89 | ||
90 | ||
91 | /* NDIS Functions */ | |
92 | static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) | |
93 | { | |
94 | int retval = -ENOTSUPP; | |
95 | u32 length = 0; | |
96 | __le32 *tmp; | |
97 | int i, count; | |
98 | rndis_query_cmplt_type *resp; | |
99 | ||
100 | if (!r) return -ENOMEM; | |
101 | resp = (rndis_query_cmplt_type *) r->buf; | |
102 | ||
103 | if (!resp) return -ENOMEM; | |
104 | ||
105 | switch (OID) { | |
106 | ||
107 | /* general oids (table 4-1) */ | |
108 | ||
109 | /* mandatory */ | |
110 | case OID_GEN_SUPPORTED_LIST: | |
111 | DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); | |
112 | length = sizeof (oid_supported_list); | |
113 | count = length / sizeof (u32); | |
114 | tmp = (__le32 *) ((u8 *)resp + 24); | |
115 | for (i = 0; i < count; i++) | |
116 | tmp[i] = cpu_to_le32 (oid_supported_list[i]); | |
117 | retval = 0; | |
118 | break; | |
119 | ||
120 | /* mandatory */ | |
121 | case OID_GEN_HARDWARE_STATUS: | |
122 | DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); | |
123 | length = 4; | |
124 | /* Bogus question! | |
125 | * Hardware must be ready to receive high level protocols. | |
126 | * BTW: | |
127 | * reddite ergo quae sunt Caesaris Caesari | |
128 | * et quae sunt Dei Deo! | |
129 | */ | |
130 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
131 | retval = 0; | |
132 | break; | |
133 | ||
134 | /* mandatory */ | |
135 | case OID_GEN_MEDIA_SUPPORTED: | |
136 | DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); | |
137 | length = 4; | |
138 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
139 | rndis_per_dev_params [configNr].medium); | |
140 | retval = 0; | |
141 | break; | |
142 | ||
143 | /* mandatory */ | |
144 | case OID_GEN_MEDIA_IN_USE: | |
145 | DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); | |
146 | length = 4; | |
147 | /* one medium, one transport... (maybe you do it better) */ | |
148 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
149 | rndis_per_dev_params [configNr].medium); | |
150 | retval = 0; | |
151 | break; | |
152 | ||
153 | /* mandatory */ | |
154 | case OID_GEN_MAXIMUM_FRAME_SIZE: | |
155 | DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); | |
156 | if (rndis_per_dev_params [configNr].dev) { | |
157 | length = 4; | |
158 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
159 | rndis_per_dev_params [configNr].dev->mtu); | |
160 | retval = 0; | |
161 | } else { | |
162 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
163 | retval = 0; | |
164 | } | |
165 | break; | |
166 | ||
167 | /* mandatory */ | |
168 | case OID_GEN_LINK_SPEED: | |
6cdee106 | 169 | // DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); |
1da177e4 LT |
170 | length = 4; |
171 | if (rndis_per_dev_params [configNr].media_state | |
172 | == NDIS_MEDIA_STATE_DISCONNECTED) | |
173 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
174 | else | |
175 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
176 | rndis_per_dev_params [configNr].speed); | |
177 | retval = 0; | |
178 | break; | |
179 | ||
180 | /* mandatory */ | |
181 | case OID_GEN_TRANSMIT_BLOCK_SIZE: | |
182 | DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); | |
183 | if (rndis_per_dev_params [configNr].dev) { | |
184 | length = 4; | |
185 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
186 | rndis_per_dev_params [configNr].dev->mtu); | |
187 | retval = 0; | |
188 | } | |
189 | break; | |
190 | ||
191 | /* mandatory */ | |
192 | case OID_GEN_RECEIVE_BLOCK_SIZE: | |
193 | DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); | |
194 | if (rndis_per_dev_params [configNr].dev) { | |
195 | length = 4; | |
196 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
197 | rndis_per_dev_params [configNr].dev->mtu); | |
198 | retval = 0; | |
199 | } | |
200 | break; | |
201 | ||
202 | /* mandatory */ | |
203 | case OID_GEN_VENDOR_ID: | |
204 | DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); | |
205 | length = 4; | |
206 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
207 | rndis_per_dev_params [configNr].vendorID); | |
208 | retval = 0; | |
209 | break; | |
210 | ||
211 | /* mandatory */ | |
212 | case OID_GEN_VENDOR_DESCRIPTION: | |
213 | DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); | |
214 | length = strlen (rndis_per_dev_params [configNr].vendorDescr); | |
215 | memcpy ((u8 *) resp + 24, | |
216 | rndis_per_dev_params [configNr].vendorDescr, length); | |
217 | retval = 0; | |
218 | break; | |
219 | ||
220 | case OID_GEN_VENDOR_DRIVER_VERSION: | |
221 | DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); | |
222 | length = 4; | |
223 | /* Created as LE */ | |
224 | *((__le32 *) resp + 6) = rndis_driver_version; | |
225 | retval = 0; | |
226 | break; | |
227 | ||
228 | /* mandatory */ | |
229 | case OID_GEN_CURRENT_PACKET_FILTER: | |
230 | DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); | |
231 | length = 4; | |
232 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
233 | rndis_per_dev_params[configNr].filter); | |
234 | retval = 0; | |
235 | break; | |
236 | ||
237 | /* mandatory */ | |
238 | case OID_GEN_MAXIMUM_TOTAL_SIZE: | |
239 | DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); | |
240 | length = 4; | |
241 | *((__le32 *) resp + 6) = __constant_cpu_to_le32( | |
242 | RNDIS_MAX_TOTAL_SIZE); | |
243 | retval = 0; | |
244 | break; | |
245 | ||
246 | /* mandatory */ | |
247 | case OID_GEN_MEDIA_CONNECT_STATUS: | |
248 | DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); | |
249 | length = 4; | |
250 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
251 | rndis_per_dev_params [configNr] | |
252 | .media_state); | |
253 | retval = 0; | |
254 | break; | |
255 | ||
256 | case OID_GEN_PHYSICAL_MEDIUM: | |
257 | DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); | |
258 | length = 4; | |
259 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
260 | retval = 0; | |
261 | break; | |
262 | ||
263 | /* The RNDIS specification is incomplete/wrong. Some versions | |
264 | * of MS-Windows expect OIDs that aren't specified there. Other | |
265 | * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! | |
266 | */ | |
267 | case OID_GEN_MAC_OPTIONS: /* from WinME */ | |
268 | DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); | |
269 | length = 4; | |
270 | *((__le32 *) resp + 6) = __constant_cpu_to_le32( | |
271 | NDIS_MAC_OPTION_RECEIVE_SERIALIZED | |
272 | | NDIS_MAC_OPTION_FULL_DUPLEX); | |
273 | retval = 0; | |
274 | break; | |
275 | ||
276 | /* statistics OIDs (table 4-2) */ | |
277 | ||
278 | /* mandatory */ | |
279 | case OID_GEN_XMIT_OK: | |
280 | DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); | |
281 | if (rndis_per_dev_params [configNr].stats) { | |
282 | length = 4; | |
283 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
284 | rndis_per_dev_params [configNr].stats->tx_packets - | |
285 | rndis_per_dev_params [configNr].stats->tx_errors - | |
286 | rndis_per_dev_params [configNr].stats->tx_dropped); | |
287 | retval = 0; | |
288 | } else { | |
289 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
290 | retval = 0; | |
291 | } | |
292 | break; | |
293 | ||
294 | /* mandatory */ | |
295 | case OID_GEN_RCV_OK: | |
296 | DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); | |
297 | if (rndis_per_dev_params [configNr].stats) { | |
298 | length = 4; | |
299 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
300 | rndis_per_dev_params [configNr].stats->rx_packets - | |
301 | rndis_per_dev_params [configNr].stats->rx_errors - | |
302 | rndis_per_dev_params [configNr].stats->rx_dropped); | |
303 | retval = 0; | |
304 | } else { | |
305 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
306 | retval = 0; | |
307 | } | |
308 | break; | |
309 | ||
310 | /* mandatory */ | |
311 | case OID_GEN_XMIT_ERROR: | |
312 | DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); | |
313 | if (rndis_per_dev_params [configNr].stats) { | |
314 | length = 4; | |
315 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
316 | rndis_per_dev_params [configNr] | |
317 | .stats->tx_errors); | |
318 | retval = 0; | |
319 | } else { | |
320 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
321 | retval = 0; | |
322 | } | |
323 | break; | |
324 | ||
325 | /* mandatory */ | |
326 | case OID_GEN_RCV_ERROR: | |
327 | DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); | |
328 | if (rndis_per_dev_params [configNr].stats) { | |
329 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
330 | rndis_per_dev_params [configNr] | |
331 | .stats->rx_errors); | |
332 | retval = 0; | |
333 | } else { | |
334 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
335 | retval = 0; | |
336 | } | |
337 | break; | |
338 | ||
339 | /* mandatory */ | |
340 | case OID_GEN_RCV_NO_BUFFER: | |
341 | DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); | |
342 | if (rndis_per_dev_params [configNr].stats) { | |
343 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
344 | rndis_per_dev_params [configNr] | |
345 | .stats->rx_dropped); | |
346 | retval = 0; | |
347 | } else { | |
348 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
349 | retval = 0; | |
350 | } | |
351 | break; | |
352 | ||
353 | #ifdef RNDIS_OPTIONAL_STATS | |
354 | case OID_GEN_DIRECTED_BYTES_XMIT: | |
355 | DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); | |
356 | /* | |
357 | * Aunt Tilly's size of shoes | |
358 | * minus antarctica count of penguins | |
359 | * divided by weight of Alpha Centauri | |
360 | */ | |
361 | if (rndis_per_dev_params [configNr].stats) { | |
362 | length = 4; | |
363 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
364 | (rndis_per_dev_params [configNr] | |
365 | .stats->tx_packets - | |
366 | rndis_per_dev_params [configNr] | |
367 | .stats->tx_errors - | |
368 | rndis_per_dev_params [configNr] | |
369 | .stats->tx_dropped) | |
370 | * 123); | |
371 | retval = 0; | |
372 | } else { | |
373 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
374 | retval = 0; | |
375 | } | |
376 | break; | |
377 | ||
378 | case OID_GEN_DIRECTED_FRAMES_XMIT: | |
379 | DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); | |
380 | /* dito */ | |
381 | if (rndis_per_dev_params [configNr].stats) { | |
382 | length = 4; | |
383 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
384 | (rndis_per_dev_params [configNr] | |
385 | .stats->tx_packets - | |
386 | rndis_per_dev_params [configNr] | |
387 | .stats->tx_errors - | |
388 | rndis_per_dev_params [configNr] | |
389 | .stats->tx_dropped) | |
390 | / 123); | |
391 | retval = 0; | |
392 | } else { | |
393 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
394 | retval = 0; | |
395 | } | |
396 | break; | |
397 | ||
398 | case OID_GEN_MULTICAST_BYTES_XMIT: | |
399 | DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); | |
400 | if (rndis_per_dev_params [configNr].stats) { | |
401 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
402 | rndis_per_dev_params [configNr] | |
403 | .stats->multicast*1234); | |
404 | retval = 0; | |
405 | } else { | |
406 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
407 | retval = 0; | |
408 | } | |
409 | break; | |
410 | ||
411 | case OID_GEN_MULTICAST_FRAMES_XMIT: | |
412 | DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); | |
413 | if (rndis_per_dev_params [configNr].stats) { | |
414 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
415 | rndis_per_dev_params [configNr] | |
416 | .stats->multicast); | |
417 | retval = 0; | |
418 | } else { | |
419 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
420 | retval = 0; | |
421 | } | |
422 | break; | |
423 | ||
424 | case OID_GEN_BROADCAST_BYTES_XMIT: | |
425 | DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); | |
426 | if (rndis_per_dev_params [configNr].stats) { | |
427 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
428 | rndis_per_dev_params [configNr] | |
429 | .stats->tx_packets/42*255); | |
430 | retval = 0; | |
431 | } else { | |
432 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
433 | retval = 0; | |
434 | } | |
435 | break; | |
436 | ||
437 | case OID_GEN_BROADCAST_FRAMES_XMIT: | |
438 | DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); | |
439 | if (rndis_per_dev_params [configNr].stats) { | |
440 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
441 | rndis_per_dev_params [configNr] | |
442 | .stats->tx_packets/42); | |
443 | retval = 0; | |
444 | } else { | |
445 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
446 | retval = 0; | |
447 | } | |
448 | break; | |
449 | ||
450 | case OID_GEN_DIRECTED_BYTES_RCV: | |
451 | DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); | |
452 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
453 | retval = 0; | |
454 | break; | |
455 | ||
456 | case OID_GEN_DIRECTED_FRAMES_RCV: | |
457 | DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); | |
458 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
459 | retval = 0; | |
460 | break; | |
461 | ||
462 | case OID_GEN_MULTICAST_BYTES_RCV: | |
463 | DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); | |
464 | if (rndis_per_dev_params [configNr].stats) { | |
465 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
466 | rndis_per_dev_params [configNr] | |
467 | .stats->multicast * 1111); | |
468 | retval = 0; | |
469 | } else { | |
470 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
471 | retval = 0; | |
472 | } | |
473 | break; | |
474 | ||
475 | case OID_GEN_MULTICAST_FRAMES_RCV: | |
476 | DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); | |
477 | if (rndis_per_dev_params [configNr].stats) { | |
478 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
479 | rndis_per_dev_params [configNr] | |
480 | .stats->multicast); | |
481 | retval = 0; | |
482 | } else { | |
483 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
484 | retval = 0; | |
485 | } | |
486 | break; | |
487 | ||
488 | case OID_GEN_BROADCAST_BYTES_RCV: | |
489 | DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); | |
490 | if (rndis_per_dev_params [configNr].stats) { | |
491 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
492 | rndis_per_dev_params [configNr] | |
493 | .stats->rx_packets/42*255); | |
494 | retval = 0; | |
495 | } else { | |
496 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
497 | retval = 0; | |
498 | } | |
499 | break; | |
500 | ||
501 | case OID_GEN_BROADCAST_FRAMES_RCV: | |
502 | DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); | |
503 | if (rndis_per_dev_params [configNr].stats) { | |
504 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
505 | rndis_per_dev_params [configNr] | |
506 | .stats->rx_packets/42); | |
507 | retval = 0; | |
508 | } else { | |
509 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
510 | retval = 0; | |
511 | } | |
512 | break; | |
513 | ||
514 | case OID_GEN_RCV_CRC_ERROR: | |
515 | DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); | |
516 | if (rndis_per_dev_params [configNr].stats) { | |
517 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
518 | rndis_per_dev_params [configNr] | |
519 | .stats->rx_crc_errors); | |
520 | retval = 0; | |
521 | } else { | |
522 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
523 | retval = 0; | |
524 | } | |
525 | break; | |
526 | ||
527 | case OID_GEN_TRANSMIT_QUEUE_LENGTH: | |
528 | DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); | |
529 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
530 | retval = 0; | |
531 | break; | |
532 | #endif /* RNDIS_OPTIONAL_STATS */ | |
533 | ||
534 | /* ieee802.3 OIDs (table 4-3) */ | |
535 | ||
536 | /* mandatory */ | |
537 | case OID_802_3_PERMANENT_ADDRESS: | |
538 | DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); | |
539 | if (rndis_per_dev_params [configNr].dev) { | |
540 | length = ETH_ALEN; | |
541 | memcpy ((u8 *) resp + 24, | |
542 | rndis_per_dev_params [configNr].host_mac, | |
543 | length); | |
544 | retval = 0; | |
545 | } else { | |
546 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
547 | retval = 0; | |
548 | } | |
549 | break; | |
550 | ||
551 | /* mandatory */ | |
552 | case OID_802_3_CURRENT_ADDRESS: | |
553 | DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); | |
554 | if (rndis_per_dev_params [configNr].dev) { | |
555 | length = ETH_ALEN; | |
556 | memcpy ((u8 *) resp + 24, | |
557 | rndis_per_dev_params [configNr].host_mac, | |
558 | length); | |
559 | retval = 0; | |
560 | } | |
561 | break; | |
562 | ||
563 | /* mandatory */ | |
564 | case OID_802_3_MULTICAST_LIST: | |
565 | DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); | |
566 | length = 4; | |
567 | /* Multicast base address only */ | |
568 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0xE0000000); | |
569 | retval = 0; | |
570 | break; | |
571 | ||
572 | /* mandatory */ | |
573 | case OID_802_3_MAXIMUM_LIST_SIZE: | |
574 | DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); | |
575 | length = 4; | |
576 | /* Multicast base address only */ | |
577 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (1); | |
578 | retval = 0; | |
579 | break; | |
580 | ||
581 | case OID_802_3_MAC_OPTIONS: | |
582 | DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); | |
583 | break; | |
584 | ||
585 | /* ieee802.3 statistics OIDs (table 4-4) */ | |
586 | ||
587 | /* mandatory */ | |
588 | case OID_802_3_RCV_ERROR_ALIGNMENT: | |
589 | DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); | |
590 | if (rndis_per_dev_params [configNr].stats) | |
591 | { | |
592 | length = 4; | |
593 | *((__le32 *) resp + 6) = cpu_to_le32 ( | |
594 | rndis_per_dev_params [configNr] | |
595 | .stats->rx_frame_errors); | |
596 | retval = 0; | |
597 | } | |
598 | break; | |
599 | ||
600 | /* mandatory */ | |
601 | case OID_802_3_XMIT_ONE_COLLISION: | |
602 | DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); | |
603 | length = 4; | |
604 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
605 | retval = 0; | |
606 | break; | |
607 | ||
608 | /* mandatory */ | |
609 | case OID_802_3_XMIT_MORE_COLLISIONS: | |
610 | DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); | |
611 | length = 4; | |
612 | *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); | |
613 | retval = 0; | |
614 | break; | |
615 | ||
616 | #ifdef RNDIS_OPTIONAL_STATS | |
617 | case OID_802_3_XMIT_DEFERRED: | |
618 | DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); | |
619 | /* TODO */ | |
620 | break; | |
621 | ||
622 | case OID_802_3_XMIT_MAX_COLLISIONS: | |
623 | DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); | |
624 | /* TODO */ | |
625 | break; | |
626 | ||
627 | case OID_802_3_RCV_OVERRUN: | |
628 | DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); | |
629 | /* TODO */ | |
630 | break; | |
631 | ||
632 | case OID_802_3_XMIT_UNDERRUN: | |
633 | DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); | |
634 | /* TODO */ | |
635 | break; | |
636 | ||
637 | case OID_802_3_XMIT_HEARTBEAT_FAILURE: | |
638 | DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); | |
639 | /* TODO */ | |
640 | break; | |
641 | ||
642 | case OID_802_3_XMIT_TIMES_CRS_LOST: | |
643 | DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); | |
644 | /* TODO */ | |
645 | break; | |
646 | ||
647 | case OID_802_3_XMIT_LATE_COLLISIONS: | |
648 | DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); | |
649 | /* TODO */ | |
650 | break; | |
651 | #endif /* RNDIS_OPTIONAL_STATS */ | |
652 | ||
653 | #ifdef RNDIS_PM | |
654 | /* power management OIDs (table 4-5) */ | |
655 | case OID_PNP_CAPABILITIES: | |
656 | DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); | |
657 | ||
658 | /* just PM, and remote wakeup on link status change | |
659 | * (not magic packet or pattern match) | |
660 | */ | |
661 | length = sizeof (struct NDIS_PNP_CAPABILITIES); | |
662 | memset (resp, 0, length); | |
663 | { | |
664 | struct NDIS_PNP_CAPABILITIES *caps = (void *) resp; | |
665 | ||
666 | caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE; | |
667 | caps->WakeUpCapabilities.MinLinkChangeWakeUp | |
668 | = NdisDeviceStateD3; | |
669 | ||
670 | /* FIXME then use usb_gadget_wakeup(), and | |
671 | * set USB_CONFIG_ATT_WAKEUP in config desc | |
672 | */ | |
673 | } | |
674 | retval = 0; | |
675 | break; | |
676 | case OID_PNP_QUERY_POWER: | |
677 | DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__); | |
678 | /* sure, handle any power state that maps to USB suspend */ | |
679 | retval = 0; | |
680 | break; | |
681 | #endif | |
682 | ||
683 | default: | |
684 | printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", | |
685 | __FUNCTION__, OID); | |
686 | } | |
687 | ||
688 | resp->InformationBufferOffset = __constant_cpu_to_le32 (16); | |
689 | resp->InformationBufferLength = cpu_to_le32 (length); | |
690 | resp->MessageLength = cpu_to_le32 (24 + length); | |
691 | r->length = 24 + length; | |
692 | return retval; | |
693 | } | |
694 | ||
695 | static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, | |
696 | rndis_resp_t *r) | |
697 | { | |
698 | rndis_set_cmplt_type *resp; | |
699 | int i, retval = -ENOTSUPP; | |
700 | struct rndis_params *params; | |
701 | ||
702 | if (!r) | |
703 | return -ENOMEM; | |
704 | resp = (rndis_set_cmplt_type *) r->buf; | |
705 | if (!resp) | |
706 | return -ENOMEM; | |
707 | ||
708 | DEBUG("set OID %08x value, len %d:\n", OID, buf_len); | |
709 | for (i = 0; i < buf_len; i += 16) { | |
710 | DEBUG ("%03d: " | |
711 | " %02x %02x %02x %02x" | |
712 | " %02x %02x %02x %02x" | |
713 | " %02x %02x %02x %02x" | |
714 | " %02x %02x %02x %02x" | |
715 | "\n", | |
716 | i, | |
717 | buf[i], buf [i+1], | |
718 | buf[i+2], buf[i+3], | |
719 | buf[i+4], buf [i+5], | |
720 | buf[i+6], buf[i+7], | |
721 | buf[i+8], buf [i+9], | |
722 | buf[i+10], buf[i+11], | |
723 | buf[i+12], buf [i+13], | |
724 | buf[i+14], buf[i+15]); | |
725 | } | |
726 | ||
727 | switch (OID) { | |
728 | case OID_GEN_CURRENT_PACKET_FILTER: | |
729 | params = &rndis_per_dev_params [configNr]; | |
730 | retval = 0; | |
731 | ||
732 | /* FIXME use these NDIS_PACKET_TYPE_* bitflags to | |
6cdee106 | 733 | * set the cdc_filter; it's not RNDIS-specific |
1da177e4 LT |
734 | * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: |
735 | * PROMISCUOUS, DIRECTED, | |
736 | * MULTICAST, ALL_MULTICAST, BROADCAST | |
737 | */ | |
738 | params->filter = le32_to_cpup((__le32 *)buf); | |
739 | DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", | |
740 | __FUNCTION__, params->filter); | |
741 | ||
742 | /* this call has a significant side effect: it's | |
743 | * what makes the packet flow start and stop, like | |
744 | * activating the CDC Ethernet altsetting. | |
745 | */ | |
746 | if (params->filter) { | |
747 | params->state = RNDIS_DATA_INITIALIZED; | |
748 | netif_carrier_on(params->dev); | |
749 | if (netif_running(params->dev)) | |
750 | netif_wake_queue (params->dev); | |
751 | } else { | |
752 | params->state = RNDIS_INITIALIZED; | |
753 | netif_carrier_off (params->dev); | |
754 | netif_stop_queue (params->dev); | |
755 | } | |
756 | break; | |
757 | ||
758 | case OID_802_3_MULTICAST_LIST: | |
759 | /* I think we can ignore this */ | |
760 | DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); | |
761 | retval = 0; | |
762 | break; | |
763 | #if 0 | |
764 | case OID_GEN_RNDIS_CONFIG_PARAMETER: | |
765 | { | |
766 | struct rndis_config_parameter *param; | |
767 | param = (struct rndis_config_parameter *) buf; | |
768 | DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", | |
769 | __FUNCTION__, | |
770 | min(cpu_to_le32(param->ParameterNameLength),80), | |
771 | buf + param->ParameterNameOffset); | |
772 | retval = 0; | |
773 | } | |
774 | break; | |
775 | #endif | |
776 | ||
777 | #ifdef RNDIS_PM | |
778 | case OID_PNP_SET_POWER: | |
779 | DEBUG ("OID_PNP_SET_POWER\n"); | |
780 | /* sure, handle any power state that maps to USB suspend */ | |
781 | retval = 0; | |
782 | break; | |
783 | ||
784 | case OID_PNP_ENABLE_WAKE_UP: | |
785 | /* always-connected ... */ | |
786 | DEBUG ("OID_PNP_ENABLE_WAKE_UP\n"); | |
787 | retval = 0; | |
788 | break; | |
789 | ||
790 | // no PM resume patterns supported (specified where?) | |
791 | // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails | |
792 | #endif | |
793 | ||
794 | default: | |
795 | printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", | |
796 | __FUNCTION__, OID, buf_len); | |
797 | } | |
798 | ||
799 | return retval; | |
800 | } | |
801 | ||
802 | /* | |
803 | * Response Functions | |
804 | */ | |
805 | ||
806 | static int rndis_init_response (int configNr, rndis_init_msg_type *buf) | |
807 | { | |
808 | rndis_init_cmplt_type *resp; | |
809 | rndis_resp_t *r; | |
810 | ||
811 | if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; | |
812 | ||
813 | r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); | |
814 | ||
815 | if (!r) return -ENOMEM; | |
816 | ||
817 | resp = (rndis_init_cmplt_type *) r->buf; | |
818 | ||
819 | if (!resp) return -ENOMEM; | |
820 | ||
821 | resp->MessageType = __constant_cpu_to_le32 ( | |
822 | REMOTE_NDIS_INITIALIZE_CMPLT); | |
823 | resp->MessageLength = __constant_cpu_to_le32 (52); | |
824 | resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ | |
825 | resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); | |
826 | resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION); | |
827 | resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION); | |
828 | resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); | |
829 | resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); | |
830 | resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); | |
831 | resp->MaxTransferSize = cpu_to_le32 ( | |
832 | rndis_per_dev_params [configNr].dev->mtu | |
833 | + sizeof (struct ethhdr) | |
834 | + sizeof (struct rndis_packet_msg_type) | |
835 | + 22); | |
836 | resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0); | |
837 | resp->AFListOffset = __constant_cpu_to_le32 (0); | |
838 | resp->AFListSize = __constant_cpu_to_le32 (0); | |
839 | ||
840 | if (rndis_per_dev_params [configNr].ack) | |
841 | rndis_per_dev_params [configNr].ack ( | |
842 | rndis_per_dev_params [configNr].dev); | |
843 | ||
844 | return 0; | |
845 | } | |
846 | ||
847 | static int rndis_query_response (int configNr, rndis_query_msg_type *buf) | |
848 | { | |
849 | rndis_query_cmplt_type *resp; | |
850 | rndis_resp_t *r; | |
851 | ||
852 | // DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); | |
853 | if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; | |
854 | ||
855 | /* | |
856 | * we need more memory: | |
857 | * oid_supported_list is the largest answer | |
858 | */ | |
859 | r = rndis_add_response (configNr, sizeof (oid_supported_list)); | |
860 | ||
861 | if (!r) return -ENOMEM; | |
862 | resp = (rndis_query_cmplt_type *) r->buf; | |
863 | ||
864 | if (!resp) return -ENOMEM; | |
865 | ||
866 | resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); | |
867 | resp->MessageLength = __constant_cpu_to_le32 (24); | |
868 | resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ | |
869 | ||
870 | if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), r)) { | |
871 | /* OID not supported */ | |
872 | resp->Status = __constant_cpu_to_le32 ( | |
873 | RNDIS_STATUS_NOT_SUPPORTED); | |
874 | resp->InformationBufferLength = __constant_cpu_to_le32 (0); | |
875 | resp->InformationBufferOffset = __constant_cpu_to_le32 (0); | |
876 | } else | |
877 | resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); | |
878 | ||
879 | if (rndis_per_dev_params [configNr].ack) | |
880 | rndis_per_dev_params [configNr].ack ( | |
881 | rndis_per_dev_params [configNr].dev); | |
882 | return 0; | |
883 | } | |
884 | ||
885 | static int rndis_set_response (int configNr, rndis_set_msg_type *buf) | |
886 | { | |
887 | u32 BufLength, BufOffset; | |
888 | rndis_set_cmplt_type *resp; | |
889 | rndis_resp_t *r; | |
890 | ||
891 | r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); | |
892 | ||
893 | if (!r) return -ENOMEM; | |
894 | resp = (rndis_set_cmplt_type *) r->buf; | |
895 | if (!resp) return -ENOMEM; | |
896 | ||
897 | BufLength = le32_to_cpu (buf->InformationBufferLength); | |
898 | BufOffset = le32_to_cpu (buf->InformationBufferOffset); | |
899 | ||
900 | #ifdef VERBOSE | |
901 | DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength); | |
902 | DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset); | |
903 | DEBUG("%s: InfoBuffer: ", __FUNCTION__); | |
904 | ||
905 | for (i = 0; i < BufLength; i++) { | |
906 | DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); | |
907 | } | |
908 | ||
909 | DEBUG ("\n"); | |
910 | #endif | |
911 | ||
912 | resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); | |
913 | resp->MessageLength = __constant_cpu_to_le32 (16); | |
914 | resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ | |
915 | if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID), | |
916 | ((u8 *) buf) + 8 + BufOffset, BufLength, r)) | |
917 | resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); | |
918 | else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); | |
919 | ||
920 | if (rndis_per_dev_params [configNr].ack) | |
921 | rndis_per_dev_params [configNr].ack ( | |
922 | rndis_per_dev_params [configNr].dev); | |
923 | ||
924 | return 0; | |
925 | } | |
926 | ||
927 | static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) | |
928 | { | |
929 | rndis_reset_cmplt_type *resp; | |
930 | rndis_resp_t *r; | |
931 | ||
932 | r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); | |
933 | ||
934 | if (!r) return -ENOMEM; | |
935 | resp = (rndis_reset_cmplt_type *) r->buf; | |
936 | if (!resp) return -ENOMEM; | |
937 | ||
938 | resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); | |
939 | resp->MessageLength = __constant_cpu_to_le32 (16); | |
940 | resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); | |
941 | /* resent information */ | |
942 | resp->AddressingReset = __constant_cpu_to_le32 (1); | |
943 | ||
944 | if (rndis_per_dev_params [configNr].ack) | |
945 | rndis_per_dev_params [configNr].ack ( | |
946 | rndis_per_dev_params [configNr].dev); | |
947 | ||
948 | return 0; | |
949 | } | |
950 | ||
951 | static int rndis_keepalive_response (int configNr, | |
952 | rndis_keepalive_msg_type *buf) | |
953 | { | |
954 | rndis_keepalive_cmplt_type *resp; | |
955 | rndis_resp_t *r; | |
956 | ||
957 | /* host "should" check only in RNDIS_DATA_INITIALIZED state */ | |
958 | ||
959 | r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); | |
960 | resp = (rndis_keepalive_cmplt_type *) r->buf; | |
961 | if (!resp) return -ENOMEM; | |
962 | ||
963 | resp->MessageType = __constant_cpu_to_le32 ( | |
964 | REMOTE_NDIS_KEEPALIVE_CMPLT); | |
965 | resp->MessageLength = __constant_cpu_to_le32 (16); | |
966 | resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ | |
967 | resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); | |
968 | ||
969 | if (rndis_per_dev_params [configNr].ack) | |
970 | rndis_per_dev_params [configNr].ack ( | |
971 | rndis_per_dev_params [configNr].dev); | |
972 | ||
973 | return 0; | |
974 | } | |
975 | ||
976 | ||
977 | /* | |
978 | * Device to Host Comunication | |
979 | */ | |
980 | static int rndis_indicate_status_msg (int configNr, u32 status) | |
981 | { | |
982 | rndis_indicate_status_msg_type *resp; | |
983 | rndis_resp_t *r; | |
984 | ||
985 | if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) | |
986 | return -ENOTSUPP; | |
987 | ||
988 | r = rndis_add_response (configNr, | |
989 | sizeof (rndis_indicate_status_msg_type)); | |
990 | if (!r) return -ENOMEM; | |
991 | ||
992 | resp = (rndis_indicate_status_msg_type *) r->buf; | |
993 | if (!resp) return -ENOMEM; | |
994 | ||
995 | resp->MessageType = __constant_cpu_to_le32 ( | |
996 | REMOTE_NDIS_INDICATE_STATUS_MSG); | |
997 | resp->MessageLength = __constant_cpu_to_le32 (20); | |
998 | resp->Status = cpu_to_le32 (status); | |
999 | resp->StatusBufferLength = __constant_cpu_to_le32 (0); | |
1000 | resp->StatusBufferOffset = __constant_cpu_to_le32 (0); | |
1001 | ||
1002 | if (rndis_per_dev_params [configNr].ack) | |
1003 | rndis_per_dev_params [configNr].ack ( | |
1004 | rndis_per_dev_params [configNr].dev); | |
1005 | return 0; | |
1006 | } | |
1007 | ||
1008 | int rndis_signal_connect (int configNr) | |
1009 | { | |
1010 | rndis_per_dev_params [configNr].media_state | |
1011 | = NDIS_MEDIA_STATE_CONNECTED; | |
1012 | return rndis_indicate_status_msg (configNr, | |
1013 | RNDIS_STATUS_MEDIA_CONNECT); | |
1014 | } | |
1015 | ||
1016 | int rndis_signal_disconnect (int configNr) | |
1017 | { | |
1018 | rndis_per_dev_params [configNr].media_state | |
1019 | = NDIS_MEDIA_STATE_DISCONNECTED; | |
1020 | return rndis_indicate_status_msg (configNr, | |
1021 | RNDIS_STATUS_MEDIA_DISCONNECT); | |
1022 | } | |
1023 | ||
1024 | void rndis_set_host_mac (int configNr, const u8 *addr) | |
1025 | { | |
1026 | rndis_per_dev_params [configNr].host_mac = addr; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | * Message Parser | |
1031 | */ | |
1032 | int rndis_msg_parser (u8 configNr, u8 *buf) | |
1033 | { | |
1034 | u32 MsgType, MsgLength; | |
1035 | __le32 *tmp; | |
1036 | struct rndis_params *params; | |
1037 | ||
1038 | if (!buf) | |
1039 | return -ENOMEM; | |
1040 | ||
1041 | tmp = (__le32 *) buf; | |
1042 | MsgType = le32_to_cpup(tmp++); | |
1043 | MsgLength = le32_to_cpup(tmp++); | |
1044 | ||
1045 | if (configNr >= RNDIS_MAX_CONFIGS) | |
1046 | return -ENOTSUPP; | |
1047 | params = &rndis_per_dev_params [configNr]; | |
1048 | ||
1049 | /* For USB: responses may take up to 10 seconds */ | |
1050 | switch (MsgType) | |
1051 | { | |
1052 | case REMOTE_NDIS_INITIALIZE_MSG: | |
1053 | DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", | |
1054 | __FUNCTION__ ); | |
1055 | params->state = RNDIS_INITIALIZED; | |
1056 | return rndis_init_response (configNr, | |
1057 | (rndis_init_msg_type *) buf); | |
1058 | ||
1059 | case REMOTE_NDIS_HALT_MSG: | |
1060 | DEBUG("%s: REMOTE_NDIS_HALT_MSG\n", | |
1061 | __FUNCTION__ ); | |
1062 | params->state = RNDIS_UNINITIALIZED; | |
1063 | if (params->dev) { | |
1064 | netif_carrier_off (params->dev); | |
1065 | netif_stop_queue (params->dev); | |
1066 | } | |
1067 | return 0; | |
1068 | ||
1069 | case REMOTE_NDIS_QUERY_MSG: | |
1070 | return rndis_query_response (configNr, | |
1071 | (rndis_query_msg_type *) buf); | |
1072 | ||
1073 | case REMOTE_NDIS_SET_MSG: | |
1074 | return rndis_set_response (configNr, | |
1075 | (rndis_set_msg_type *) buf); | |
1076 | ||
1077 | case REMOTE_NDIS_RESET_MSG: | |
1078 | DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", | |
1079 | __FUNCTION__ ); | |
1080 | return rndis_reset_response (configNr, | |
1081 | (rndis_reset_msg_type *) buf); | |
1082 | ||
1083 | case REMOTE_NDIS_KEEPALIVE_MSG: | |
1084 | /* For USB: host does this every 5 seconds */ | |
1085 | #ifdef VERBOSE | |
1086 | DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", | |
1087 | __FUNCTION__ ); | |
1088 | #endif | |
1089 | return rndis_keepalive_response (configNr, | |
1090 | (rndis_keepalive_msg_type *) | |
1091 | buf); | |
1092 | ||
1093 | default: | |
1094 | /* At least Windows XP emits some undefined RNDIS messages. | |
1095 | * In one case those messages seemed to relate to the host | |
1096 | * suspending itself. | |
1097 | */ | |
1098 | printk (KERN_WARNING | |
1099 | "%s: unknown RNDIS message 0x%08X len %d\n", | |
1100 | __FUNCTION__ , MsgType, MsgLength); | |
1101 | { | |
1102 | unsigned i; | |
1103 | for (i = 0; i < MsgLength; i += 16) { | |
1104 | DEBUG ("%03d: " | |
1105 | " %02x %02x %02x %02x" | |
1106 | " %02x %02x %02x %02x" | |
1107 | " %02x %02x %02x %02x" | |
1108 | " %02x %02x %02x %02x" | |
1109 | "\n", | |
1110 | i, | |
1111 | buf[i], buf [i+1], | |
1112 | buf[i+2], buf[i+3], | |
1113 | buf[i+4], buf [i+5], | |
1114 | buf[i+6], buf[i+7], | |
1115 | buf[i+8], buf [i+9], | |
1116 | buf[i+10], buf[i+11], | |
1117 | buf[i+12], buf [i+13], | |
1118 | buf[i+14], buf[i+15]); | |
1119 | } | |
1120 | } | |
1121 | break; | |
1122 | } | |
1123 | ||
1124 | return -ENOTSUPP; | |
1125 | } | |
1126 | ||
1127 | int rndis_register (int (* rndis_control_ack) (struct net_device *)) | |
1128 | { | |
1129 | u8 i; | |
1130 | ||
1131 | for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { | |
1132 | if (!rndis_per_dev_params [i].used) { | |
1133 | rndis_per_dev_params [i].used = 1; | |
1134 | rndis_per_dev_params [i].ack = rndis_control_ack; | |
1135 | DEBUG("%s: configNr = %d\n", __FUNCTION__, i); | |
1136 | return i; | |
1137 | } | |
1138 | } | |
1139 | DEBUG("failed\n"); | |
1140 | ||
1141 | return -1; | |
1142 | } | |
1143 | ||
1144 | void rndis_deregister (int configNr) | |
1145 | { | |
1146 | DEBUG("%s: \n", __FUNCTION__ ); | |
1147 | ||
1148 | if (configNr >= RNDIS_MAX_CONFIGS) return; | |
1149 | rndis_per_dev_params [configNr].used = 0; | |
1150 | ||
1151 | return; | |
1152 | } | |
1153 | ||
1154 | int rndis_set_param_dev (u8 configNr, struct net_device *dev, | |
1155 | struct net_device_stats *stats) | |
1156 | { | |
1157 | DEBUG("%s:\n", __FUNCTION__ ); | |
1158 | if (!dev || !stats) return -1; | |
1159 | if (configNr >= RNDIS_MAX_CONFIGS) return -1; | |
1160 | ||
1161 | rndis_per_dev_params [configNr].dev = dev; | |
1162 | rndis_per_dev_params [configNr].stats = stats; | |
1163 | ||
1164 | return 0; | |
1165 | } | |
1166 | ||
1167 | int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) | |
1168 | { | |
1169 | DEBUG("%s:\n", __FUNCTION__ ); | |
1170 | if (!vendorDescr) return -1; | |
1171 | if (configNr >= RNDIS_MAX_CONFIGS) return -1; | |
1172 | ||
1173 | rndis_per_dev_params [configNr].vendorID = vendorID; | |
1174 | rndis_per_dev_params [configNr].vendorDescr = vendorDescr; | |
1175 | ||
1176 | return 0; | |
1177 | } | |
1178 | ||
1179 | int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) | |
1180 | { | |
1181 | DEBUG("%s:\n", __FUNCTION__ ); | |
1182 | if (configNr >= RNDIS_MAX_CONFIGS) return -1; | |
1183 | ||
1184 | rndis_per_dev_params [configNr].medium = medium; | |
1185 | rndis_per_dev_params [configNr].speed = speed; | |
1186 | ||
1187 | return 0; | |
1188 | } | |
1189 | ||
1190 | void rndis_add_hdr (struct sk_buff *skb) | |
1191 | { | |
1192 | struct rndis_packet_msg_type *header; | |
1193 | ||
1194 | if (!skb) | |
1195 | return; | |
1196 | header = (void *) skb_push (skb, sizeof *header); | |
1197 | memset (header, 0, sizeof *header); | |
6cdee106 | 1198 | header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG); |
1da177e4 LT |
1199 | header->MessageLength = cpu_to_le32(skb->len); |
1200 | header->DataOffset = __constant_cpu_to_le32 (36); | |
6cdee106 | 1201 | header->DataLength = cpu_to_le32(skb->len - sizeof *header); |
1da177e4 LT |
1202 | } |
1203 | ||
1204 | void rndis_free_response (int configNr, u8 *buf) | |
1205 | { | |
1206 | rndis_resp_t *r; | |
1207 | struct list_head *act, *tmp; | |
1208 | ||
1209 | list_for_each_safe (act, tmp, | |
1210 | &(rndis_per_dev_params [configNr].resp_queue)) | |
1211 | { | |
1212 | r = list_entry (act, rndis_resp_t, list); | |
1213 | if (r && r->buf == buf) { | |
1214 | list_del (&r->list); | |
1215 | kfree (r); | |
1216 | } | |
1217 | } | |
1218 | } | |
1219 | ||
1220 | u8 *rndis_get_next_response (int configNr, u32 *length) | |
1221 | { | |
1222 | rndis_resp_t *r; | |
1223 | struct list_head *act, *tmp; | |
1224 | ||
1225 | if (!length) return NULL; | |
1226 | ||
1227 | list_for_each_safe (act, tmp, | |
1228 | &(rndis_per_dev_params [configNr].resp_queue)) | |
1229 | { | |
1230 | r = list_entry (act, rndis_resp_t, list); | |
1231 | if (!r->send) { | |
1232 | r->send = 1; | |
1233 | *length = r->length; | |
1234 | return r->buf; | |
1235 | } | |
1236 | } | |
1237 | ||
1238 | return NULL; | |
1239 | } | |
1240 | ||
1241 | static rndis_resp_t *rndis_add_response (int configNr, u32 length) | |
1242 | { | |
1243 | rndis_resp_t *r; | |
1244 | ||
1245 | r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); | |
1246 | if (!r) return NULL; | |
1247 | ||
1248 | r->buf = (u8 *) (r + 1); | |
1249 | r->length = length; | |
1250 | r->send = 0; | |
1251 | ||
1252 | list_add_tail (&r->list, | |
1253 | &(rndis_per_dev_params [configNr].resp_queue)); | |
1254 | return r; | |
1255 | } | |
1256 | ||
6cdee106 | 1257 | int rndis_rm_hdr(struct sk_buff *skb) |
1da177e4 | 1258 | { |
6cdee106 DB |
1259 | /* tmp points to a struct rndis_packet_msg_type */ |
1260 | __le32 *tmp = (void *) skb->data; | |
1da177e4 | 1261 | |
6cdee106 DB |
1262 | /* MessageType, MessageLength */ |
1263 | if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG) | |
1264 | != get_unaligned(tmp++)) | |
1265 | return -EINVAL; | |
1266 | tmp++; | |
1267 | ||
1268 | /* DataOffset, DataLength */ | |
1269 | if (!skb_pull(skb, le32_to_cpu(get_unaligned(tmp++)) | |
1270 | + 8 /* offset of DataOffset */)) | |
1271 | return -EOVERFLOW; | |
1272 | skb_trim(skb, le32_to_cpu(get_unaligned(tmp++))); | |
1da177e4 | 1273 | |
1da177e4 LT |
1274 | return 0; |
1275 | } | |
1276 | ||
1277 | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | |
1278 | ||
1279 | static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, | |
1280 | void *data) | |
1281 | { | |
1282 | char *out = page; | |
1283 | int len; | |
1284 | rndis_params *param = (rndis_params *) data; | |
1285 | ||
1286 | out += snprintf (out, count, | |
1287 | "Config Nr. %d\n" | |
1288 | "used : %s\n" | |
1289 | "state : %s\n" | |
1290 | "medium : 0x%08X\n" | |
1291 | "speed : %d\n" | |
1292 | "cable : %s\n" | |
1293 | "vendor ID : 0x%08X\n" | |
1294 | "vendor : %s\n", | |
1295 | param->confignr, (param->used) ? "y" : "n", | |
1296 | ({ char *s = "?"; | |
1297 | switch (param->state) { | |
1298 | case RNDIS_UNINITIALIZED: | |
1299 | s = "RNDIS_UNINITIALIZED"; break; | |
1300 | case RNDIS_INITIALIZED: | |
1301 | s = "RNDIS_INITIALIZED"; break; | |
1302 | case RNDIS_DATA_INITIALIZED: | |
1303 | s = "RNDIS_DATA_INITIALIZED"; break; | |
1304 | }; s; }), | |
1305 | param->medium, | |
1306 | (param->media_state) ? 0 : param->speed*100, | |
1307 | (param->media_state) ? "disconnected" : "connected", | |
1308 | param->vendorID, param->vendorDescr); | |
1309 | ||
1310 | len = out - page; | |
1311 | len -= off; | |
1312 | ||
1313 | if (len < count) { | |
1314 | *eof = 1; | |
1315 | if (len <= 0) | |
1316 | return 0; | |
1317 | } else | |
1318 | len = count; | |
1319 | ||
1320 | *start = page + off; | |
1321 | return len; | |
1322 | } | |
1323 | ||
1324 | static int rndis_proc_write (struct file *file, const char __user *buffer, | |
1325 | unsigned long count, void *data) | |
1326 | { | |
1327 | rndis_params *p = data; | |
1328 | u32 speed = 0; | |
1329 | int i, fl_speed = 0; | |
1330 | ||
1331 | for (i = 0; i < count; i++) { | |
1332 | char c; | |
1333 | if (get_user(c, buffer)) | |
1334 | return -EFAULT; | |
1335 | switch (c) { | |
1336 | case '0': | |
1337 | case '1': | |
1338 | case '2': | |
1339 | case '3': | |
1340 | case '4': | |
1341 | case '5': | |
1342 | case '6': | |
1343 | case '7': | |
1344 | case '8': | |
1345 | case '9': | |
1346 | fl_speed = 1; | |
1347 | speed = speed*10 + c - '0'; | |
1348 | break; | |
1349 | case 'C': | |
1350 | case 'c': | |
1351 | rndis_signal_connect (p->confignr); | |
1352 | break; | |
1353 | case 'D': | |
1354 | case 'd': | |
1355 | rndis_signal_disconnect(p->confignr); | |
1356 | break; | |
1357 | default: | |
1358 | if (fl_speed) p->speed = speed; | |
1359 | else DEBUG ("%c is not valid\n", c); | |
1360 | break; | |
1361 | } | |
1362 | ||
1363 | buffer++; | |
1364 | } | |
1365 | ||
1366 | return count; | |
1367 | } | |
1368 | ||
1369 | #define NAME_TEMPLATE "driver/rndis-%03d" | |
1370 | ||
1371 | static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; | |
1372 | ||
1373 | #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ | |
1374 | ||
1375 | ||
1376 | int __init rndis_init (void) | |
1377 | { | |
1378 | u8 i; | |
1379 | ||
1380 | for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { | |
1381 | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | |
1382 | char name [20]; | |
1383 | ||
1384 | sprintf (name, NAME_TEMPLATE, i); | |
1385 | if (!(rndis_connect_state [i] | |
1386 | = create_proc_entry (name, 0660, NULL))) | |
1387 | { | |
1388 | DEBUG ("%s :remove entries", __FUNCTION__); | |
1389 | while (i) { | |
1390 | sprintf (name, NAME_TEMPLATE, --i); | |
1391 | remove_proc_entry (name, NULL); | |
1392 | } | |
1393 | DEBUG ("\n"); | |
1394 | return -EIO; | |
1395 | } | |
1396 | ||
1397 | rndis_connect_state [i]->nlink = 1; | |
1398 | rndis_connect_state [i]->write_proc = rndis_proc_write; | |
1399 | rndis_connect_state [i]->read_proc = rndis_proc_read; | |
1400 | rndis_connect_state [i]->data = (void *) | |
1401 | (rndis_per_dev_params + i); | |
1402 | #endif | |
1403 | rndis_per_dev_params [i].confignr = i; | |
1404 | rndis_per_dev_params [i].used = 0; | |
1405 | rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; | |
1406 | rndis_per_dev_params [i].media_state | |
1407 | = NDIS_MEDIA_STATE_DISCONNECTED; | |
1408 | INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue)); | |
1409 | } | |
1410 | ||
1411 | return 0; | |
1412 | } | |
1413 | ||
1414 | void rndis_exit (void) | |
1415 | { | |
1416 | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | |
1417 | u8 i; | |
1418 | char name [20]; | |
1419 | ||
1420 | for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { | |
1421 | sprintf (name, NAME_TEMPLATE, i); | |
1422 | remove_proc_entry (name, NULL); | |
1423 | } | |
1424 | #endif | |
1425 | } | |
1426 |