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