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