diff options
Diffstat (limited to 'drivers/net/wireless/libertas')
37 files changed, 22307 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c new file mode 100644 index 000000000000..e0ecc4d483bb --- /dev/null +++ b/drivers/net/wireless/libertas/11d.c | |||
@@ -0,0 +1,754 @@ | |||
1 | /** | ||
2 | * This file contains functions for 802.11D. | ||
3 | */ | ||
4 | #include <linux/ctype.h> | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/wireless.h> | ||
7 | |||
8 | #include "host.h" | ||
9 | #include "decl.h" | ||
10 | #include "11d.h" | ||
11 | #include "dev.h" | ||
12 | #include "wext.h" | ||
13 | |||
14 | #define TX_PWR_DEFAULT 10 | ||
15 | |||
16 | static struct region_code_mapping region_code_mapping[] = { | ||
17 | {"US ", 0x10}, /* US FCC */ | ||
18 | {"CA ", 0x10}, /* IC Canada */ | ||
19 | {"SG ", 0x10}, /* Singapore */ | ||
20 | {"EU ", 0x30}, /* ETSI */ | ||
21 | {"AU ", 0x30}, /* Australia */ | ||
22 | {"KR ", 0x30}, /* Republic Of Korea */ | ||
23 | {"ES ", 0x31}, /* Spain */ | ||
24 | {"FR ", 0x32}, /* France */ | ||
25 | {"JP ", 0x40}, /* Japan */ | ||
26 | }; | ||
27 | |||
28 | /* Following 2 structure defines the supported channels */ | ||
29 | static struct chan_freq_power channel_freq_power_UN_BG[] = { | ||
30 | {1, 2412, TX_PWR_DEFAULT}, | ||
31 | {2, 2417, TX_PWR_DEFAULT}, | ||
32 | {3, 2422, TX_PWR_DEFAULT}, | ||
33 | {4, 2427, TX_PWR_DEFAULT}, | ||
34 | {5, 2432, TX_PWR_DEFAULT}, | ||
35 | {6, 2437, TX_PWR_DEFAULT}, | ||
36 | {7, 2442, TX_PWR_DEFAULT}, | ||
37 | {8, 2447, TX_PWR_DEFAULT}, | ||
38 | {9, 2452, TX_PWR_DEFAULT}, | ||
39 | {10, 2457, TX_PWR_DEFAULT}, | ||
40 | {11, 2462, TX_PWR_DEFAULT}, | ||
41 | {12, 2467, TX_PWR_DEFAULT}, | ||
42 | {13, 2472, TX_PWR_DEFAULT}, | ||
43 | {14, 2484, TX_PWR_DEFAULT} | ||
44 | }; | ||
45 | |||
46 | static u8 wlan_region_2_code(u8 * region) | ||
47 | { | ||
48 | u8 i; | ||
49 | u8 size = sizeof(region_code_mapping)/ | ||
50 | sizeof(struct region_code_mapping); | ||
51 | |||
52 | for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++) | ||
53 | region[i] = toupper(region[i]); | ||
54 | |||
55 | for (i = 0; i < size; i++) { | ||
56 | if (!memcmp(region, region_code_mapping[i].region, | ||
57 | COUNTRY_CODE_LEN)) | ||
58 | return (region_code_mapping[i].code); | ||
59 | } | ||
60 | |||
61 | /* default is US */ | ||
62 | return (region_code_mapping[0].code); | ||
63 | } | ||
64 | |||
65 | static u8 *wlan_code_2_region(u8 code) | ||
66 | { | ||
67 | u8 i; | ||
68 | u8 size = sizeof(region_code_mapping) | ||
69 | / sizeof(struct region_code_mapping); | ||
70 | for (i = 0; i < size; i++) { | ||
71 | if (region_code_mapping[i].code == code) | ||
72 | return (region_code_mapping[i].region); | ||
73 | } | ||
74 | /* default is US */ | ||
75 | return (region_code_mapping[0].region); | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @brief This function finds the nrchan-th chan after the firstchan | ||
80 | * @param band band | ||
81 | * @param firstchan first channel number | ||
82 | * @param nrchan number of channels | ||
83 | * @return the nrchan-th chan number | ||
84 | */ | ||
85 | static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan) | ||
86 | /*find the nrchan-th chan after the firstchan*/ | ||
87 | { | ||
88 | u8 i; | ||
89 | struct chan_freq_power *cfp; | ||
90 | u8 cfp_no; | ||
91 | |||
92 | cfp = channel_freq_power_UN_BG; | ||
93 | cfp_no = sizeof(channel_freq_power_UN_BG) / | ||
94 | sizeof(struct chan_freq_power); | ||
95 | |||
96 | for (i = 0; i < cfp_no; i++) { | ||
97 | if ((cfp + i)->channel == firstchan) { | ||
98 | lbs_pr_debug(1, "firstchan found\n"); | ||
99 | break; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | if (i < cfp_no) { | ||
104 | /*if beyond the boundary */ | ||
105 | if (i + nrchan < cfp_no) { | ||
106 | *chan = (cfp + i + nrchan)->channel; | ||
107 | return 1; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * @brief This function Checks if chan txpwr is learned from AP/IBSS | ||
116 | * @param chan chan number | ||
117 | * @param parsed_region_chan pointer to parsed_region_chan_11d | ||
118 | * @return TRUE; FALSE | ||
119 | */ | ||
120 | static u8 wlan_channel_known_11d(u8 chan, | ||
121 | struct parsed_region_chan_11d * parsed_region_chan) | ||
122 | { | ||
123 | struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; | ||
124 | u8 nr_chan = parsed_region_chan->nr_chan; | ||
125 | u8 i = 0; | ||
126 | |||
127 | lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr, | ||
128 | sizeof(struct chan_power_11d) * nr_chan); | ||
129 | |||
130 | for (i = 0; i < nr_chan; i++) { | ||
131 | if (chan == chanpwr[i].chan) { | ||
132 | lbs_pr_debug(1, "11D: Found Chan:%d\n", chan); | ||
133 | return 1; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | u32 libertas_chan_2_freq(u8 chan, u8 band) | ||
142 | { | ||
143 | struct chan_freq_power *cf; | ||
144 | u16 cnt; | ||
145 | u16 i; | ||
146 | u32 freq = 0; | ||
147 | |||
148 | cf = channel_freq_power_UN_BG; | ||
149 | cnt = | ||
150 | sizeof(channel_freq_power_UN_BG) / | ||
151 | sizeof(struct chan_freq_power); | ||
152 | |||
153 | for (i = 0; i < cnt; i++) { | ||
154 | if (chan == cf[i].channel) | ||
155 | freq = cf[i].freq; | ||
156 | } | ||
157 | |||
158 | return freq; | ||
159 | } | ||
160 | |||
161 | static int generate_domain_info_11d(struct parsed_region_chan_11d | ||
162 | *parsed_region_chan, | ||
163 | struct wlan_802_11d_domain_reg * domaininfo) | ||
164 | { | ||
165 | u8 nr_subband = 0; | ||
166 | |||
167 | u8 nr_chan = parsed_region_chan->nr_chan; | ||
168 | u8 nr_parsedchan = 0; | ||
169 | |||
170 | u8 firstchan = 0, nextchan = 0, maxpwr = 0; | ||
171 | |||
172 | u8 i, flag = 0; | ||
173 | |||
174 | memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, | ||
175 | COUNTRY_CODE_LEN); | ||
176 | |||
177 | lbs_pr_debug(1, "11D:nrchan=%d\n", nr_chan); | ||
178 | lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan, | ||
179 | sizeof(struct parsed_region_chan_11d)); | ||
180 | |||
181 | for (i = 0; i < nr_chan; i++) { | ||
182 | if (!flag) { | ||
183 | flag = 1; | ||
184 | nextchan = firstchan = | ||
185 | parsed_region_chan->chanpwr[i].chan; | ||
186 | maxpwr = parsed_region_chan->chanpwr[i].pwr; | ||
187 | nr_parsedchan = 1; | ||
188 | continue; | ||
189 | } | ||
190 | |||
191 | if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && | ||
192 | parsed_region_chan->chanpwr[i].pwr == maxpwr) { | ||
193 | nextchan++; | ||
194 | nr_parsedchan++; | ||
195 | } else { | ||
196 | domaininfo->subband[nr_subband].firstchan = firstchan; | ||
197 | domaininfo->subband[nr_subband].nrchan = | ||
198 | nr_parsedchan; | ||
199 | domaininfo->subband[nr_subband].maxtxpwr = maxpwr; | ||
200 | nr_subband++; | ||
201 | nextchan = firstchan = | ||
202 | parsed_region_chan->chanpwr[i].chan; | ||
203 | maxpwr = parsed_region_chan->chanpwr[i].pwr; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if (flag) { | ||
208 | domaininfo->subband[nr_subband].firstchan = firstchan; | ||
209 | domaininfo->subband[nr_subband].nrchan = nr_parsedchan; | ||
210 | domaininfo->subband[nr_subband].maxtxpwr = maxpwr; | ||
211 | nr_subband++; | ||
212 | } | ||
213 | domaininfo->nr_subband = nr_subband; | ||
214 | |||
215 | lbs_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband); | ||
216 | lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo, | ||
217 | COUNTRY_CODE_LEN + 1 + | ||
218 | sizeof(struct ieeetypes_subbandset) * nr_subband); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS | ||
224 | * @param region_chan pointer to struct region_channel | ||
225 | * @param *parsed_region_chan pointer to parsed_region_chan_11d | ||
226 | * @return N/A | ||
227 | */ | ||
228 | static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan, | ||
229 | struct parsed_region_chan_11d * | ||
230 | parsed_region_chan) | ||
231 | { | ||
232 | u8 i; | ||
233 | struct chan_freq_power *cfp; | ||
234 | |||
235 | if (region_chan == NULL) { | ||
236 | lbs_pr_debug(1, "11D: region_chan is NULL\n"); | ||
237 | return; | ||
238 | } | ||
239 | |||
240 | cfp = region_chan->CFP; | ||
241 | if (cfp == NULL) { | ||
242 | lbs_pr_debug(1, "11D: cfp equal NULL \n"); | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | parsed_region_chan->band = region_chan->band; | ||
247 | parsed_region_chan->region = region_chan->region; | ||
248 | memcpy(parsed_region_chan->countrycode, | ||
249 | wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN); | ||
250 | |||
251 | lbs_pr_debug(1, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region, | ||
252 | parsed_region_chan->band); | ||
253 | |||
254 | for (i = 0; i < region_chan->nrcfp; i++, cfp++) { | ||
255 | parsed_region_chan->chanpwr[i].chan = cfp->channel; | ||
256 | parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; | ||
257 | lbs_pr_debug(1, "11D: Chan[%d] Pwr[%d]\n", | ||
258 | parsed_region_chan->chanpwr[i].chan, | ||
259 | parsed_region_chan->chanpwr[i].pwr); | ||
260 | } | ||
261 | parsed_region_chan->nr_chan = region_chan->nrcfp; | ||
262 | |||
263 | lbs_pr_debug(1, "11D: nrchan[%d]\n", parsed_region_chan->nr_chan); | ||
264 | |||
265 | return; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS | ||
270 | * @param region region ID | ||
271 | * @param band band | ||
272 | * @param chan chan | ||
273 | * @return TRUE;FALSE | ||
274 | */ | ||
275 | static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan) | ||
276 | { | ||
277 | struct chan_freq_power *cfp; | ||
278 | int cfp_no; | ||
279 | u8 idx; | ||
280 | |||
281 | ENTER(); | ||
282 | |||
283 | cfp = libertas_get_region_cfp_table(region, band, &cfp_no); | ||
284 | if (cfp == NULL) | ||
285 | return 0; | ||
286 | |||
287 | for (idx = 0; idx < cfp_no; idx++) { | ||
288 | if (chan == (cfp + idx)->channel) { | ||
289 | /* If Mrvl Chip Supported? */ | ||
290 | if ((cfp + idx)->unsupported) { | ||
291 | return 0; | ||
292 | } else { | ||
293 | return 1; | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /*chan is not in the region table */ | ||
299 | LEAVE(); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | /** | ||
304 | * @brief This function checks if chan txpwr is learned from AP/IBSS | ||
305 | * @param chan chan number | ||
306 | * @param parsed_region_chan pointer to parsed_region_chan_11d | ||
307 | * @return 0 | ||
308 | */ | ||
309 | static int parse_domain_info_11d(struct ieeetypes_countryinfofullset* | ||
310 | countryinfo, | ||
311 | u8 band, | ||
312 | struct parsed_region_chan_11d * | ||
313 | parsed_region_chan) | ||
314 | { | ||
315 | u8 nr_subband, nrchan; | ||
316 | u8 lastchan, firstchan; | ||
317 | u8 region; | ||
318 | u8 curchan = 0; | ||
319 | |||
320 | u8 idx = 0; /*chan index in parsed_region_chan */ | ||
321 | |||
322 | u8 j, i; | ||
323 | |||
324 | ENTER(); | ||
325 | |||
326 | /*validation Rules: | ||
327 | 1. valid region Code | ||
328 | 2. First Chan increment | ||
329 | 3. channel range no overlap | ||
330 | 4. channel is valid? | ||
331 | 5. channel is supported by region? | ||
332 | 6. Others | ||
333 | */ | ||
334 | |||
335 | lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30); | ||
336 | |||
337 | if ((*(countryinfo->countrycode)) == 0 | ||
338 | || (countryinfo->len <= COUNTRY_CODE_LEN)) { | ||
339 | /* No region Info or Wrong region info: treat as No 11D info */ | ||
340 | LEAVE(); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | /*Step1: check region_code */ | ||
345 | parsed_region_chan->region = region = | ||
346 | wlan_region_2_code(countryinfo->countrycode); | ||
347 | |||
348 | lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region); | ||
349 | lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode, | ||
350 | COUNTRY_CODE_LEN); | ||
351 | |||
352 | parsed_region_chan->band = band; | ||
353 | |||
354 | memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, | ||
355 | COUNTRY_CODE_LEN); | ||
356 | |||
357 | nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) / | ||
358 | sizeof(struct ieeetypes_subbandset); | ||
359 | |||
360 | for (j = 0, lastchan = 0; j < nr_subband; j++) { | ||
361 | |||
362 | if (countryinfo->subband[j].firstchan <= lastchan) { | ||
363 | /*Step2&3. Check First Chan Num increment and no overlap */ | ||
364 | lbs_pr_debug(1, "11D: Chan[%d>%d] Overlap\n", | ||
365 | countryinfo->subband[j].firstchan, lastchan); | ||
366 | continue; | ||
367 | } | ||
368 | |||
369 | firstchan = countryinfo->subband[j].firstchan; | ||
370 | nrchan = countryinfo->subband[j].nrchan; | ||
371 | |||
372 | for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { | ||
373 | /*step4: channel is supported? */ | ||
374 | |||
375 | if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) { | ||
376 | /* Chan is not found in UN table */ | ||
377 | lbs_pr_debug(1, "chan is not supported: %d \n", i); | ||
378 | break; | ||
379 | } | ||
380 | |||
381 | lastchan = curchan; | ||
382 | |||
383 | if (wlan_region_chan_supported_11d | ||
384 | (region, band, curchan)) { | ||
385 | /*step5: Check if curchan is supported by mrvl in region */ | ||
386 | parsed_region_chan->chanpwr[idx].chan = curchan; | ||
387 | parsed_region_chan->chanpwr[idx].pwr = | ||
388 | countryinfo->subband[j].maxtxpwr; | ||
389 | idx++; | ||
390 | } else { | ||
391 | /*not supported and ignore the chan */ | ||
392 | lbs_pr_debug(1, | ||
393 | "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n", | ||
394 | i, curchan, region, band); | ||
395 | } | ||
396 | } | ||
397 | |||
398 | /*Step6: Add other checking if any */ | ||
399 | |||
400 | } | ||
401 | |||
402 | parsed_region_chan->nr_chan = idx; | ||
403 | |||
404 | lbs_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan); | ||
405 | lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan, | ||
406 | 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); | ||
407 | |||
408 | LEAVE(); | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | /** | ||
413 | * @brief This function calculates the scan type for channels | ||
414 | * @param chan chan number | ||
415 | * @param parsed_region_chan pointer to parsed_region_chan_11d | ||
416 | * @return PASSIVE if chan is unknown; ACTIVE if chan is known | ||
417 | */ | ||
418 | u8 libertas_get_scan_type_11d(u8 chan, | ||
419 | struct parsed_region_chan_11d * parsed_region_chan) | ||
420 | { | ||
421 | u8 scan_type = cmd_scan_type_passive; | ||
422 | |||
423 | ENTER(); | ||
424 | |||
425 | if (wlan_channel_known_11d(chan, parsed_region_chan)) { | ||
426 | lbs_pr_debug(1, "11D: Found and do Active Scan\n"); | ||
427 | scan_type = cmd_scan_type_active; | ||
428 | } else { | ||
429 | lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n"); | ||
430 | } | ||
431 | |||
432 | LEAVE(); | ||
433 | return scan_type; | ||
434 | |||
435 | } | ||
436 | |||
437 | void libertas_init_11d(wlan_private * priv) | ||
438 | { | ||
439 | priv->adapter->enable11d = 0; | ||
440 | memset(&(priv->adapter->parsed_region_chan), 0, | ||
441 | sizeof(struct parsed_region_chan_11d)); | ||
442 | return; | ||
443 | } | ||
444 | |||
445 | static int wlan_enable_11d(wlan_private * priv, u8 flag) | ||
446 | { | ||
447 | int ret; | ||
448 | |||
449 | priv->adapter->enable11d = flag; | ||
450 | |||
451 | /* send cmd to FW to enable/disable 11D function in FW */ | ||
452 | ret = libertas_prepare_and_send_command(priv, | ||
453 | cmd_802_11_snmp_mib, | ||
454 | cmd_act_set, | ||
455 | cmd_option_waitforrsp, | ||
456 | OID_802_11D_ENABLE, | ||
457 | &priv->adapter->enable11d); | ||
458 | if (ret) | ||
459 | lbs_pr_debug(1, "11D: Fail to enable 11D \n"); | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | /** | ||
465 | * @brief This function sets DOMAIN INFO to FW | ||
466 | * @param priv pointer to wlan_private | ||
467 | * @return 0; -1 | ||
468 | */ | ||
469 | static int set_domain_info_11d(wlan_private * priv) | ||
470 | { | ||
471 | int ret; | ||
472 | |||
473 | if (!priv->adapter->enable11d) { | ||
474 | lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n"); | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info, | ||
479 | cmd_act_set, | ||
480 | cmd_option_waitforrsp, 0, NULL); | ||
481 | if (ret) | ||
482 | lbs_pr_debug(1, "11D: Fail to dnld domain Info\n"); | ||
483 | |||
484 | return ret; | ||
485 | } | ||
486 | |||
487 | /** | ||
488 | * @brief This function setups scan channels | ||
489 | * @param priv pointer to wlan_private | ||
490 | * @param band band | ||
491 | * @return 0 | ||
492 | */ | ||
493 | int libertas_set_universaltable(wlan_private * priv, u8 band) | ||
494 | { | ||
495 | wlan_adapter *adapter = priv->adapter; | ||
496 | u16 size = sizeof(struct chan_freq_power); | ||
497 | u16 i = 0; | ||
498 | |||
499 | memset(adapter->universal_channel, 0, | ||
500 | sizeof(adapter->universal_channel)); | ||
501 | |||
502 | adapter->universal_channel[i].nrcfp = | ||
503 | sizeof(channel_freq_power_UN_BG) / size; | ||
504 | lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n", | ||
505 | adapter->universal_channel[i].nrcfp); | ||
506 | |||
507 | adapter->universal_channel[i].CFP = channel_freq_power_UN_BG; | ||
508 | adapter->universal_channel[i].valid = 1; | ||
509 | adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; | ||
510 | adapter->universal_channel[i].band = band; | ||
511 | i++; | ||
512 | |||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * @brief This function implements command CMD_802_11D_DOMAIN_INFO | ||
518 | * @param priv pointer to wlan_private | ||
519 | * @param cmd pointer to cmd buffer | ||
520 | * @param cmdno cmd ID | ||
521 | * @param cmdOption cmd action | ||
522 | * @return 0 | ||
523 | */ | ||
524 | int libertas_cmd_802_11d_domain_info(wlan_private * priv, | ||
525 | struct cmd_ds_command *cmd, u16 cmdno, | ||
526 | u16 cmdoption) | ||
527 | { | ||
528 | struct cmd_ds_802_11d_domain_info *pdomaininfo = | ||
529 | &cmd->params.domaininfo; | ||
530 | struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain; | ||
531 | wlan_adapter *adapter = priv->adapter; | ||
532 | u8 nr_subband = adapter->domainreg.nr_subband; | ||
533 | |||
534 | ENTER(); | ||
535 | |||
536 | lbs_pr_debug(1, "nr_subband=%x\n", nr_subband); | ||
537 | |||
538 | cmd->command = cpu_to_le16(cmdno); | ||
539 | pdomaininfo->action = cpu_to_le16(cmdoption); | ||
540 | if (cmdoption == cmd_act_get) { | ||
541 | cmd->size = | ||
542 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | ||
543 | lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd, | ||
544 | (int)(cmd->size)); | ||
545 | LEAVE(); | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); | ||
550 | memcpy(domain->countrycode, adapter->domainreg.countrycode, | ||
551 | sizeof(domain->countrycode)); | ||
552 | |||
553 | domain->header.len = | ||
554 | cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) + | ||
555 | sizeof(domain->countrycode)); | ||
556 | |||
557 | if (nr_subband) { | ||
558 | memcpy(domain->subband, adapter->domainreg.subband, | ||
559 | nr_subband * sizeof(struct ieeetypes_subbandset)); | ||
560 | |||
561 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | ||
562 | domain->header.len + | ||
563 | sizeof(struct mrvlietypesheader) + | ||
564 | S_DS_GEN); | ||
565 | } else { | ||
566 | cmd->size = | ||
567 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | ||
568 | } | ||
569 | |||
570 | lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size)); | ||
571 | |||
572 | LEAVE(); | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | /** | ||
578 | * @brief This function implements private cmd: enable/disable 11D | ||
579 | * @param priv pointer to wlan_private | ||
580 | * @param wrq pointer to user data | ||
581 | * @return 0 or -1 | ||
582 | */ | ||
583 | int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq) | ||
584 | { | ||
585 | int data = 0; | ||
586 | int *val; | ||
587 | |||
588 | ENTER(); | ||
589 | data = SUBCMD_DATA(wrq); | ||
590 | |||
591 | lbs_pr_debug(1, "enable 11D: %s\n", | ||
592 | (data == 1) ? "enable" : "Disable"); | ||
593 | |||
594 | wlan_enable_11d(priv, data); | ||
595 | val = (int *)wrq->u.name; | ||
596 | *val = priv->adapter->enable11d; | ||
597 | |||
598 | LEAVE(); | ||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | /** | ||
603 | * @brief This function parses countryinfo from AP and download country info to FW | ||
604 | * @param priv pointer to wlan_private | ||
605 | * @param resp pointer to command response buffer | ||
606 | * @return 0; -1 | ||
607 | */ | ||
608 | int libertas_ret_802_11d_domain_info(wlan_private * priv, | ||
609 | struct cmd_ds_command *resp) | ||
610 | { | ||
611 | struct cmd_ds_802_11d_domain_info | ||
612 | *domaininfo = &resp->params.domaininforesp; | ||
613 | struct mrvlietypes_domainparamset *domain = &domaininfo->domain; | ||
614 | u16 action = le16_to_cpu(domaininfo->action); | ||
615 | s16 ret = 0; | ||
616 | u8 nr_subband = 0; | ||
617 | |||
618 | ENTER(); | ||
619 | |||
620 | lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp, | ||
621 | (int)le16_to_cpu(resp->size)); | ||
622 | |||
623 | nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset); | ||
624 | /* countrycode 3 bytes */ | ||
625 | |||
626 | lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband); | ||
627 | |||
628 | if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { | ||
629 | lbs_pr_debug(1, "Invalid Numrer of Subband returned!!\n"); | ||
630 | return -1; | ||
631 | } | ||
632 | |||
633 | switch (action) { | ||
634 | case cmd_act_set: /*Proc Set action */ | ||
635 | break; | ||
636 | |||
637 | case cmd_act_get: | ||
638 | break; | ||
639 | default: | ||
640 | lbs_pr_debug(1, "Invalid action:%d\n", domaininfo->action); | ||
641 | ret = -1; | ||
642 | break; | ||
643 | } | ||
644 | |||
645 | LEAVE(); | ||
646 | return ret; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * @brief This function parses countryinfo from AP and download country info to FW | ||
651 | * @param priv pointer to wlan_private | ||
652 | * @return 0; -1 | ||
653 | */ | ||
654 | int libertas_parse_dnld_countryinfo_11d(wlan_private * priv) | ||
655 | { | ||
656 | int ret; | ||
657 | wlan_adapter *adapter = priv->adapter; | ||
658 | |||
659 | ENTER(); | ||
660 | if (priv->adapter->enable11d) { | ||
661 | memset(&adapter->parsed_region_chan, 0, | ||
662 | sizeof(struct parsed_region_chan_11d)); | ||
663 | ret = parse_domain_info_11d(&adapter->pattemptedbssdesc-> | ||
664 | countryinfo, 0, | ||
665 | &adapter->parsed_region_chan); | ||
666 | |||
667 | if (ret == -1) { | ||
668 | lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n"); | ||
669 | LEAVE(); | ||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | memset(&adapter->domainreg, 0, | ||
674 | sizeof(struct wlan_802_11d_domain_reg)); | ||
675 | generate_domain_info_11d(&adapter->parsed_region_chan, | ||
676 | &adapter->domainreg); | ||
677 | |||
678 | ret = set_domain_info_11d(priv); | ||
679 | |||
680 | if (ret) { | ||
681 | lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); | ||
682 | LEAVE(); | ||
683 | return ret; | ||
684 | } | ||
685 | } | ||
686 | LEAVE(); | ||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * @brief This function generates 11D info from user specified regioncode and download to FW | ||
692 | * @param priv pointer to wlan_private | ||
693 | * @return 0; -1 | ||
694 | */ | ||
695 | int libertas_create_dnld_countryinfo_11d(wlan_private * priv) | ||
696 | { | ||
697 | int ret; | ||
698 | wlan_adapter *adapter = priv->adapter; | ||
699 | struct region_channel *region_chan; | ||
700 | u8 j; | ||
701 | |||
702 | ENTER(); | ||
703 | lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band); | ||
704 | |||
705 | if (priv->adapter->enable11d) { | ||
706 | /* update parsed_region_chan_11; dnld domaininf to FW */ | ||
707 | |||
708 | for (j = 0; j < sizeof(adapter->region_channel) / | ||
709 | sizeof(adapter->region_channel[0]); j++) { | ||
710 | region_chan = &adapter->region_channel[j]; | ||
711 | |||
712 | lbs_pr_debug(1, "11D:[%d] region_chan->band[%d]\n", j, | ||
713 | region_chan->band); | ||
714 | |||
715 | if (!region_chan || !region_chan->valid | ||
716 | || !region_chan->CFP) | ||
717 | continue; | ||
718 | if (region_chan->band != adapter->curbssparams.band) | ||
719 | continue; | ||
720 | break; | ||
721 | } | ||
722 | |||
723 | if (j >= sizeof(adapter->region_channel) / | ||
724 | sizeof(adapter->region_channel[0])) { | ||
725 | lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n", | ||
726 | adapter->curbssparams.band); | ||
727 | LEAVE(); | ||
728 | return -1; | ||
729 | } | ||
730 | |||
731 | memset(&adapter->parsed_region_chan, 0, | ||
732 | sizeof(struct parsed_region_chan_11d)); | ||
733 | wlan_generate_parsed_region_chan_11d(region_chan, | ||
734 | &adapter-> | ||
735 | parsed_region_chan); | ||
736 | |||
737 | memset(&adapter->domainreg, 0, | ||
738 | sizeof(struct wlan_802_11d_domain_reg)); | ||
739 | generate_domain_info_11d(&adapter->parsed_region_chan, | ||
740 | &adapter->domainreg); | ||
741 | |||
742 | ret = set_domain_info_11d(priv); | ||
743 | |||
744 | if (ret) { | ||
745 | lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); | ||
746 | LEAVE(); | ||
747 | return ret; | ||
748 | } | ||
749 | |||
750 | } | ||
751 | |||
752 | LEAVE(); | ||
753 | return 0; | ||
754 | } | ||
diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h new file mode 100644 index 000000000000..db2ebea9f231 --- /dev/null +++ b/drivers/net/wireless/libertas/11d.h | |||
@@ -0,0 +1,105 @@ | |||
1 | /** | ||
2 | * This header file contains data structures and | ||
3 | * function declarations of 802.11d | ||
4 | */ | ||
5 | #ifndef _WLAN_11D_ | ||
6 | #define _WLAN_11D_ | ||
7 | |||
8 | #include "types.h" | ||
9 | #include "defs.h" | ||
10 | |||
11 | #define UNIVERSAL_REGION_CODE 0xff | ||
12 | |||
13 | /** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) | ||
14 | */ | ||
15 | #define MRVDRV_MAX_SUBBAND_802_11D 83 | ||
16 | |||
17 | #define COUNTRY_CODE_LEN 3 | ||
18 | #define MAX_NO_OF_CHAN 40 | ||
19 | |||
20 | struct cmd_ds_command; | ||
21 | |||
22 | /** Data structure for Country IE*/ | ||
23 | struct ieeetypes_subbandset { | ||
24 | u8 firstchan; | ||
25 | u8 nrchan; | ||
26 | u8 maxtxpwr; | ||
27 | } __attribute__ ((packed)); | ||
28 | |||
29 | struct ieeetypes_countryinfoset { | ||
30 | u8 element_id; | ||
31 | u8 len; | ||
32 | u8 countrycode[COUNTRY_CODE_LEN]; | ||
33 | struct ieeetypes_subbandset subband[1]; | ||
34 | }; | ||
35 | |||
36 | struct ieeetypes_countryinfofullset { | ||
37 | u8 element_id; | ||
38 | u8 len; | ||
39 | u8 countrycode[COUNTRY_CODE_LEN]; | ||
40 | struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; | ||
41 | } __attribute__ ((packed)); | ||
42 | |||
43 | struct mrvlietypes_domainparamset { | ||
44 | struct mrvlietypesheader header; | ||
45 | u8 countrycode[COUNTRY_CODE_LEN]; | ||
46 | struct ieeetypes_subbandset subband[1]; | ||
47 | } __attribute__ ((packed)); | ||
48 | |||
49 | struct cmd_ds_802_11d_domain_info { | ||
50 | u16 action; | ||
51 | struct mrvlietypes_domainparamset domain; | ||
52 | } __attribute__ ((packed)); | ||
53 | |||
54 | /** domain regulatory information */ | ||
55 | struct wlan_802_11d_domain_reg { | ||
56 | /** country Code*/ | ||
57 | u8 countrycode[COUNTRY_CODE_LEN]; | ||
58 | /** No. of subband*/ | ||
59 | u8 nr_subband; | ||
60 | struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; | ||
61 | }; | ||
62 | |||
63 | struct chan_power_11d { | ||
64 | u8 chan; | ||
65 | u8 pwr; | ||
66 | } __attribute__ ((packed)); | ||
67 | |||
68 | struct parsed_region_chan_11d { | ||
69 | u8 band; | ||
70 | u8 region; | ||
71 | s8 countrycode[COUNTRY_CODE_LEN]; | ||
72 | struct chan_power_11d chanpwr[MAX_NO_OF_CHAN]; | ||
73 | u8 nr_chan; | ||
74 | } __attribute__ ((packed)); | ||
75 | |||
76 | struct region_code_mapping { | ||
77 | u8 region[COUNTRY_CODE_LEN]; | ||
78 | u8 code; | ||
79 | }; | ||
80 | |||
81 | u8 libertas_get_scan_type_11d(u8 chan, | ||
82 | struct parsed_region_chan_11d *parsed_region_chan); | ||
83 | |||
84 | u32 libertas_chan_2_freq(u8 chan, u8 band); | ||
85 | |||
86 | enum state_11d libertas_get_state_11d(wlan_private * priv); | ||
87 | |||
88 | void libertas_init_11d(wlan_private * priv); | ||
89 | |||
90 | int libertas_set_universaltable(wlan_private * priv, u8 band); | ||
91 | |||
92 | int libertas_cmd_802_11d_domain_info(wlan_private * priv, | ||
93 | struct cmd_ds_command *cmd, u16 cmdno, | ||
94 | u16 cmdOption); | ||
95 | |||
96 | int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq); | ||
97 | |||
98 | int libertas_ret_802_11d_domain_info(wlan_private * priv, | ||
99 | struct cmd_ds_command *resp); | ||
100 | |||
101 | int libertas_parse_dnld_countryinfo_11d(wlan_private * priv); | ||
102 | |||
103 | int libertas_create_dnld_countryinfo_11d(wlan_private * priv); | ||
104 | |||
105 | #endif /* _WLAN_11D_ */ | ||
diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE new file mode 100644 index 000000000000..8862742213b9 --- /dev/null +++ b/drivers/net/wireless/libertas/LICENSE | |||
@@ -0,0 +1,16 @@ | |||
1 | Copyright (c) 2003-2006, Marvell International Ltd. | ||
2 | All Rights Reserved | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify it | ||
5 | under the terms of version 2 of the GNU General Public License as | ||
6 | published by the Free Software Foundation. | ||
7 | |||
8 | This program is distributed in the hope that it will be useful, but WITHOUT | ||
9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | more details. | ||
12 | |||
13 | You should have received a copy of the GNU General Public License along with | ||
14 | this program; if not, write to the Free Software Foundation, Inc., 59 | ||
15 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
16 | |||
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile new file mode 100644 index 000000000000..19c935071d8e --- /dev/null +++ b/drivers/net/wireless/libertas/Makefile | |||
@@ -0,0 +1,21 @@ | |||
1 | # EXTRA_CFLAGS += -Wpacked | ||
2 | |||
3 | usb8xxx-objs := main.o fw.o wext.o \ | ||
4 | rx.o tx.o cmd.o \ | ||
5 | cmdresp.o scan.o \ | ||
6 | join.o 11d.o \ | ||
7 | ioctl.o debugfs.o \ | ||
8 | ethtool.o assoc.o | ||
9 | |||
10 | ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y) | ||
11 | EXTRA_CFLAGS += -DDEBUG -DPROC_DEBUG | ||
12 | endif | ||
13 | |||
14 | |||
15 | # This is needed to support the newer boot2 bootloader (v >= 3104) | ||
16 | EXTRA_CFLAGS += -DSUPPORT_BOOT_COMMAND | ||
17 | usb8xxx-objs += if_bootcmd.o | ||
18 | usb8xxx-objs += if_usb.o | ||
19 | |||
20 | obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o | ||
21 | |||
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README new file mode 100644 index 000000000000..688da4c784b1 --- /dev/null +++ b/drivers/net/wireless/libertas/README | |||
@@ -0,0 +1,1044 @@ | |||
1 | ================================================================================ | ||
2 | README for USB8388 | ||
3 | |||
4 | (c) Copyright © 2003-2006, Marvell International Ltd. | ||
5 | All Rights Reserved | ||
6 | |||
7 | This software file (the "File") is distributed by Marvell International | ||
8 | Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
9 | (the "License"). You may use, redistribute and/or modify this File in | ||
10 | accordance with the terms and conditions of the License, a copy of which | ||
11 | is available along with the File in the license.txt file or by writing to | ||
12 | the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
13 | 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. | ||
14 | |||
15 | THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
16 | IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
17 | ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
18 | this warranty disclaimer. | ||
19 | ================================================================================ | ||
20 | |||
21 | ===================== | ||
22 | DRIVER LOADING | ||
23 | ===================== | ||
24 | |||
25 | o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ | ||
26 | |||
27 | o. Load driver by using the following command: | ||
28 | |||
29 | insmod usb8388.ko [fw_name=usb8388.bin] | ||
30 | |||
31 | ===================== | ||
32 | IWPRIV COMMAND | ||
33 | ===================== | ||
34 | |||
35 | NAME | ||
36 | This manual describes the usage of private commands used in Marvell WLAN | ||
37 | Linux Driver. All the commands available in Wlanconfig will not be available | ||
38 | in the iwpriv. | ||
39 | |||
40 | SYNOPSIS | ||
41 | iwpriv <ethX> <command> [sub-command] ... | ||
42 | |||
43 | iwpriv ethX version | ||
44 | iwpriv ethX scantype [sub-command] | ||
45 | iwpriv ethX getSNR <n> | ||
46 | iwpriv ethX getNF <n> | ||
47 | iwpriv ethX getRSSI <n> | ||
48 | iwpriv ethX setrxant <n> | ||
49 | iwpriv ethX getrxant | ||
50 | iwpriv ethX settxant <n> | ||
51 | iwpriv ethX gettxant | ||
52 | iwpriv ethX authalgs <n> | ||
53 | iwpriv ethX pre-TBTT <n> | ||
54 | iwpriv ethX 8021xauthalgs <n> | ||
55 | iwpriv ethX encryptionmode <n> | ||
56 | iwpriv ethX setregioncode <n> | ||
57 | iwpriv ethX getregioncode | ||
58 | iwpriv ethX setbcnavg <n> | ||
59 | iwpriv ethX getbcnavg | ||
60 | iwpriv ethX setdataavg <n> | ||
61 | iwpriv ethX setlisteninter <n> | ||
62 | iwpriv ethX getlisteninter | ||
63 | iwpriv ethX setmultipledtim <n> | ||
64 | iwpriv ethX getmultipledtim | ||
65 | iwpriv ethX atimwindow <n> | ||
66 | iwpriv ethX deauth | ||
67 | iwpriv ethX adhocstop | ||
68 | iwpriv ethX radioon | ||
69 | iwpriv ethX radiooff | ||
70 | iwpriv ethX reasso-on | ||
71 | iwpriv ethX reasso-off | ||
72 | iwpriv ethX scanmode [sub-command] | ||
73 | iwpriv ethX setwpaie <n> | ||
74 | iwpriv ethX wlanidle-off | ||
75 | iwpriv ethX wlanidle-on | ||
76 | iwpriv ethX getcis | ||
77 | iwpriv ethX getlog | ||
78 | iwpriv ethX getadhocstatus | ||
79 | iwpriv ethX adhocgrate <n> | ||
80 | |||
81 | Version 4 Command: | ||
82 | iwpriv ethX inactvityto <n> | ||
83 | iwpriv ethX sleeppd <n> | ||
84 | iwpriv ethX enable11d <n> | ||
85 | iwpriv ethX tpccfg <n> | ||
86 | iwpriv ethX powercfg <n> | ||
87 | iwpriv ethX setafc <n> | ||
88 | iwpriv ethX getafc | ||
89 | |||
90 | Version 5 Command: | ||
91 | iwpriv ethX ledgpio <n> | ||
92 | iwpriv ethX scanprobes <n> | ||
93 | iwpriv ethX lolisteninter <n> | ||
94 | iwpriv ethX rateadapt <n> <m> | ||
95 | iwpriv ethX txcontrol <n> | ||
96 | iwpriv ethX psnullinterval <n> | ||
97 | iwpriv ethX prescan <n> | ||
98 | iwpriv ethX getrxinfo | ||
99 | iwpriv ethX gettxrate | ||
100 | iwpriv ethX beaconinterval | ||
101 | |||
102 | BT Commands: | ||
103 | The blinding table (BT) contains a list of mac addresses that should be | ||
104 | ignored by the firmware. It is primarily used for debugging and | ||
105 | testing networks. It can be edited and inspected with the following | ||
106 | commands: | ||
107 | |||
108 | iwpriv ethX bt_reset | ||
109 | iwpriv ethX bt_add <mac_address> | ||
110 | iwpriv ethX bt_del <mac_address> | ||
111 | iwpriv ethX bt_list <id> | ||
112 | |||
113 | FWT Commands: | ||
114 | The forwarding table (FWT) is a feature used to manage mesh network | ||
115 | routing in the firmware. The FWT is essentially a routing table that | ||
116 | associates a destination mac address (da) with a next hop receiver | ||
117 | address (ra). The FWT can be inspected and edited with the following | ||
118 | iwpriv commands, which are described in greater detail below. | ||
119 | Eventually, the table will be automatically maintained by a custom | ||
120 | routing protocol. | ||
121 | |||
122 | NOTE: FWT commands replace the previous DFT commands. What were the DFT | ||
123 | commands?, you might ask. They were an earlier API to the firmware that | ||
124 | implemented a simple MAC-layer forwarding mechanism. In the unlikely | ||
125 | event that you were using these commands, you must migrate to the new | ||
126 | FWT commands which can be used to achieve the same functionality. | ||
127 | |||
128 | iwpriv ethX fwt_add [parameters] | ||
129 | iwpriv ethX fwt_del [parameters] | ||
130 | iwpriv ethX fwt_lookup [parameters] | ||
131 | iwpriv ethX fwt_list [parameters] | ||
132 | iwpriv ethX fwt_list_route [parameters] | ||
133 | iwpriv ethX fwt_list_neigh [parameters] | ||
134 | iwpriv ethX fwt_reset [parameters] | ||
135 | iwpriv ethX fwt_cleanup | ||
136 | iwpriv ethX fwt_time | ||
137 | |||
138 | MESH Commands: | ||
139 | |||
140 | The MESH commands are used to configure various features of the mesh | ||
141 | routing protocol. The following commands are supported: | ||
142 | |||
143 | iwpriv ethX mesh_get_ttl | ||
144 | iwpriv ethX mesh_set_ttl ttl | ||
145 | |||
146 | DESCRIPTION | ||
147 | Those commands are used to send additional commands to the Marvell WLAN | ||
148 | card via the Linux device driver. | ||
149 | |||
150 | The ethX parameter specifies the network device that is to be used to | ||
151 | perform this command on. it could be eth0, eth1 etc. | ||
152 | |||
153 | version | ||
154 | This is used to get the current version of the driver and the firmware. | ||
155 | |||
156 | scantype | ||
157 | This command is used to set the scan type to be used by the driver in | ||
158 | the scan command. This setting will not be used while performing a scan | ||
159 | for a specific SSID, as it is always done with scan type being active. | ||
160 | |||
161 | where the sub-commands are: - | ||
162 | active -- to set the scan type to active | ||
163 | passive -- to set the scan type to passive | ||
164 | get -- to get the scan type set in the driver | ||
165 | |||
166 | getSNR | ||
167 | This command gets the average and non average value of Signal to Noise | ||
168 | Ratio of Beacon and Data. | ||
169 | |||
170 | where value is:- | ||
171 | 0 -- Beacon non-average. | ||
172 | 1 -- Beacon average. | ||
173 | 2 -- Data non-average. | ||
174 | 3 -- Data average. | ||
175 | |||
176 | If no value is given, all four values are returned in the order mentioned | ||
177 | above. | ||
178 | |||
179 | Note: This command is available only when STA is connected. | ||
180 | |||
181 | getRSSI | ||
182 | This command gets the average and non average value os Receive Signal | ||
183 | Strength of Beacon and Data. | ||
184 | |||
185 | where value is:- | ||
186 | 0 -- Beacon non-average. | ||
187 | 1 -- Beacon average. | ||
188 | 2 -- Data non-average. | ||
189 | 3 -- Data average. | ||
190 | |||
191 | Note: This command is available only when STA is connected. | ||
192 | |||
193 | getNF | ||
194 | This command gets the average and non average value of Noise Floor of | ||
195 | Beacon and Data. | ||
196 | |||
197 | where value is:- | ||
198 | 0 -- Beacon non-average. | ||
199 | 1 -- Beacon average. | ||
200 | 2 -- Data non-average. | ||
201 | 3 -- Data average. | ||
202 | |||
203 | Note: This command is available only when STA is connected. | ||
204 | |||
205 | setrxant | ||
206 | This command is used to set the mode for Rx antenna. | ||
207 | |||
208 | The options that can be sent are:- | ||
209 | 1 -- Antenna 1. | ||
210 | 2 -- Antenna 2. | ||
211 | 0xFFFF -- Diversity. | ||
212 | |||
213 | Usage: | ||
214 | iwpriv ethX setrxant 0x01: select Antenna 1. | ||
215 | |||
216 | getrxant | ||
217 | This command is used to get the mode for Rx antenna. | ||
218 | |||
219 | |||
220 | settxant | ||
221 | This command is used to set the mode for Tx antenna. | ||
222 | The options that can be sent are:- | ||
223 | 1 -- Antenna 1. | ||
224 | 2 -- Antenna 2. | ||
225 | 0xFFFF -- Diversity. | ||
226 | Usage: | ||
227 | iwpriv ethX settxant 0x01: select Antenna 1. | ||
228 | |||
229 | gettxant | ||
230 | This command is used to get the mode for Tx antenna. | ||
231 | |||
232 | authalgs | ||
233 | This command is used by the WPA supplicant to set the authentication | ||
234 | algorithms in the station. | ||
235 | |||
236 | 8021xauthalgs | ||
237 | This command is used by the WPA supplicant to set the 8021.x authentication algorithm type | ||
238 | station. | ||
239 | |||
240 | where values can be:- | ||
241 | 1 -- None | ||
242 | 2 -- LEAP | ||
243 | 4 -- TLS | ||
244 | 8 -- TTLs | ||
245 | 16 -- MD5 | ||
246 | |||
247 | |||
248 | encryptionmode | ||
249 | This command is used by the WPA supplicant to set the encryption algorithm. | ||
250 | |||
251 | where values can be:- | ||
252 | 0 -- NONE | ||
253 | 1 -- WEP40 | ||
254 | 2 -- TKIP | ||
255 | 3 -- CCMP | ||
256 | 4 -- WEP104 | ||
257 | |||
258 | pre-TBTT | ||
259 | This command is used to set pre-TBTT time period where value is in microseconds. | ||
260 | |||
261 | setregioncode | ||
262 | This command is used to set the region code in the station. | ||
263 | where value is 'region code' for various regions like | ||
264 | USA FCC, Canada IC, Spain, France, Europe ETSI, Japan ... | ||
265 | |||
266 | Usage: | ||
267 | iwpriv ethX setregioncode 0x10: set region code to USA (0x10). | ||
268 | |||
269 | getregioncode | ||
270 | This command is used to get the region code information set in the | ||
271 | station. | ||
272 | |||
273 | setbcnavg | ||
274 | Set the weighting factor for calculating RSSI. | ||
275 | |||
276 | getbcnavg | ||
277 | Get weighting factor for calculating RSSI. | ||
278 | |||
279 | setdataavg | ||
280 | Set the weighting factor for calculating SNR. | ||
281 | |||
282 | setlisteninter | ||
283 | This command is used to set the listen interval in the | ||
284 | station. | ||
285 | |||
286 | where the value ranges between 1 - 255 | ||
287 | |||
288 | getlisteninter | ||
289 | This command is used to get the listen interval value set in the | ||
290 | station. | ||
291 | |||
292 | setmultipledtim | ||
293 | This command is used to set the multiple dtim value in the | ||
294 | station. | ||
295 | where the value is 1,2,3,4,5,0xfffe | ||
296 | 0xfffe means the firmware will use listen interval in association | ||
297 | command for waking up | ||
298 | |||
299 | getmultipledtim | ||
300 | This command is used to get the multiple dtim value set in the station. | ||
301 | |||
302 | atimwindow | ||
303 | This command is used to set the atim value in the | ||
304 | station. | ||
305 | |||
306 | where the value ranges between 0 - 50 | ||
307 | |||
308 | deauth | ||
309 | This command is used to send the de-authentication to the AP with which | ||
310 | the station is associated. This command is valid only when | ||
311 | station is in Infrastructure mode. | ||
312 | |||
313 | Note: This command is available only when STA is connected. | ||
314 | |||
315 | adhocstop | ||
316 | This command is used to stop beacon transmission from the station and | ||
317 | go into idle state in ad-hoc mode. | ||
318 | |||
319 | Note: This command is available only when STA is connected. | ||
320 | |||
321 | radioon | ||
322 | This command is used to turn on the RF antenna. | ||
323 | |||
324 | radiooff | ||
325 | This command is sued to turn off the RF antenna. | ||
326 | |||
327 | scanmode | ||
328 | This command is used to set the station to scan for either IBSS | ||
329 | networks or BSS networks or both BSS and IBSS networks. This | ||
330 | command can be used with sub commands, | ||
331 | |||
332 | where the value for | ||
333 | bss -- Scan All the BSS networks. | ||
334 | ibss -- Scan All the IBSS networks. | ||
335 | any -- Scan both BSS and IBSS networks. | ||
336 | |||
337 | |||
338 | |||
339 | setwpaie | ||
340 | This command is used by WPA supplicant to send the WPA-IE to the driver. | ||
341 | |||
342 | wlanidle-off | ||
343 | This command is used to get into idle state. | ||
344 | |||
345 | Note: This command is available only when STA is connected. | ||
346 | |||
347 | wlanidle-on | ||
348 | This command is used to get off the idle state. | ||
349 | |||
350 | Note: This command is available only when STA is connected. | ||
351 | |||
352 | |||
353 | getlog | ||
354 | This command is used to get the 802.11 statistics available in the | ||
355 | station. | ||
356 | |||
357 | Note: This command is available only when STA is connected. | ||
358 | |||
359 | getadhocstatus | ||
360 | This command is used to get the ad-hoc Network Status. | ||
361 | |||
362 | The various status codes are: | ||
363 | AdhocStarted | ||
364 | AdhocJoined | ||
365 | AdhocIdle | ||
366 | InfraMode | ||
367 | AutoUnknownMode | ||
368 | |||
369 | Note: This command is available only when STA is connected. | ||
370 | |||
371 | adhocgrate | ||
372 | This command is used to enable(1) g_rate, Disable(0) g_rate | ||
373 | and request(2) the status which g_rate is disabled/enabled, | ||
374 | for Ad-hoc creator. | ||
375 | |||
376 | where value is:- | ||
377 | 0 -- Disabled | ||
378 | 1 -- Enabled | ||
379 | 2 -- Get | ||
380 | |||
381 | ledgpio | ||
382 | This command is used to set/get LEDs. | ||
383 | |||
384 | iwpriv ethX ledgpio <LEDs> | ||
385 | will set the corresponding LED for the GPIO Line. | ||
386 | |||
387 | iwpriv ethX ledgpio | ||
388 | will give u which LEDs are Enabled. | ||
389 | |||
390 | Usage: | ||
391 | iwpriv eth1 ledgpio 1 0 2 1 3 4 | ||
392 | will enable | ||
393 | LED 1 -> GPIO 0 | ||
394 | LED 2 -> GPIO 1 | ||
395 | LED 3 -> GPIO 4 | ||
396 | |||
397 | iwpriv eth1 ledgpio | ||
398 | shows LED information in the format as mentioned above. | ||
399 | |||
400 | Note: LED0 is invalid | ||
401 | Note: Maximum Number of LEDs are 16. | ||
402 | |||
403 | inactivityto | ||
404 | This command is used by the host to set/get the inactivity timeout value, | ||
405 | which specifies when WLAN device is put to sleep. | ||
406 | |||
407 | Usage: | ||
408 | iwpriv ethX inactivityto [<timeout>] | ||
409 | |||
410 | where the parameter are: | ||
411 | timeout: timeout value in milliseconds. | ||
412 | |||
413 | Example: | ||
414 | iwpriv eth1 inactivityto | ||
415 | "get the timeout value" | ||
416 | |||
417 | iwpriv eth1 inactivityto X | ||
418 | "set timeout value to X ms" | ||
419 | |||
420 | |||
421 | sleeppd | ||
422 | This command is used to configure the sleep period of the WLAN device. | ||
423 | |||
424 | Usage: | ||
425 | iwpriv ethX sleeppd [<sleep period>] | ||
426 | |||
427 | where the parameter are: | ||
428 | Period: sleep period in milliseconds. Range 10~60. | ||
429 | |||
430 | Example: | ||
431 | iwpriv eth1 sleeppd 10 | ||
432 | "set period as 10 ms" | ||
433 | iwpriv eth1 sleeppd | ||
434 | "get the sleep period configuration" | ||
435 | |||
436 | enable11d | ||
437 | This command is used to control 11d | ||
438 | where value is:- | ||
439 | 1 -- Enabled | ||
440 | 0 -- Disabled | ||
441 | 2 -- Get | ||
442 | |||
443 | |||
444 | |||
445 | |||
446 | tpccfg | ||
447 | Enables or disables automatic transmit power control. | ||
448 | |||
449 | The first parameter turns this feature on (1) or off (0). When turning | ||
450 | on, the user must also supply four more parameters in the following | ||
451 | order: | ||
452 | -UseSNR (Use SNR (in addition to PER) for TPC algorithm), | ||
453 | -P0 (P0 power level for TPC), | ||
454 | -P1 (P1 power level for TPC), | ||
455 | -P2 (P2 power level for TPC). | ||
456 | |||
457 | Usage: | ||
458 | iwpriv ethX tpccfg: Get current configuration | ||
459 | iwpriv ethX tpccfg 0: disable auto TPC | ||
460 | iwpriv ethX tpccfg 0x01 0x00 0x05 0x0a 0x0d: enable auto TPC; do not use SNR; | ||
461 | P0=0x05; P1=0x0a; P2=0x0d; | ||
462 | iwpriv ethX tpccfg 0x01 0x01 0x05 0x0a 0x0d: enable auto TPC; use SNR; | ||
463 | P0=0x05; P1=0x0a; P2=0x0d. | ||
464 | |||
465 | powercfg | ||
466 | Enables or disables power adaptation. | ||
467 | |||
468 | The first parameter turns this feature on (1) or off (0). When turning | ||
469 | on, the user must also supply three more parameters in the following | ||
470 | order: | ||
471 | -P0 (P0 power level for Power Adaptation), | ||
472 | -P1 (P1 power level for Power Adaptation), | ||
473 | -P2 (P2 power level for Power Adaptation). | ||
474 | |||
475 | Usage: | ||
476 | iwpriv ethX powercfg: Get current configuration | ||
477 | iwpriv ethX powercfg 0: disable power adaptation | ||
478 | iwpriv ethX powercfg 1 0x0d 0x0f 0x12: enable power adaptation; | ||
479 | P0=0x0d; P1=0x0f; P2=0x12. | ||
480 | |||
481 | getafc | ||
482 | This command returns automatic frequency control parameters. It returns | ||
483 | three integers: | ||
484 | -P0: automatic is on (1), or off (0), | ||
485 | -P1: current timing offset in PPM (part per million), and | ||
486 | -P2: current frequency offset in PPM. | ||
487 | |||
488 | setafc | ||
489 | Set automatic frequency control options. | ||
490 | |||
491 | The first parameter turns automatic on (1) or off (0). | ||
492 | The user must supply two more parameters in either case, in the following | ||
493 | order: | ||
494 | |||
495 | When auto is on: | ||
496 | |||
497 | -P0 (automatic adjustment frequency threshold in PPM), | ||
498 | -P1 (automatic adjustment period in beacon period), | ||
499 | |||
500 | When auto is off: | ||
501 | |||
502 | -P0 (manual adjustment timing offset in PPM), and | ||
503 | -P1 (manual adjustment frequency offset in PPM). | ||
504 | |||
505 | Usage: | ||
506 | iwpriv ethX setafc 0 10 10: manual adjustment, both timing and frequcncy | ||
507 | offset are 10 PPM. | ||
508 | |||
509 | iwpriv ethX setafc 1 10 10 enable afc, automatic adjustment, | ||
510 | frequency threshold 10 PPM, for every 10 beacon periods. | ||
511 | |||
512 | |||
513 | |||
514 | scanprobes | ||
515 | This command sets number of probe requests per channel. | ||
516 | |||
517 | Usage: | ||
518 | iwpriv ethX scanprobes 3 (set scan probes to 3) | ||
519 | iwpriv ethX scanprobes (get scan probes) | ||
520 | |||
521 | lolisteninter | ||
522 | This command sets the value of listen interval. | ||
523 | |||
524 | Usage: | ||
525 | iwpriv ethX lolisteninter 234 (set the lolisteninter to 234) | ||
526 | iwpriv ethX lolisteninter (get the lolisteninter value) | ||
527 | |||
528 | rateadapt | ||
529 | This command sets the data rates bitmap. | ||
530 | Where <n> | ||
531 | 0: Disable auto rate adapt | ||
532 | 1: Enable auto rate adapt | ||
533 | |||
534 | <m> | ||
535 | data rate bitmap | ||
536 | Bit Data rate | ||
537 | 0 1 Mbps | ||
538 | 1 2 Mbps | ||
539 | 2 5.5 Mbps | ||
540 | 3 11 Mbps | ||
541 | 4 Reserved | ||
542 | 5 6 Mbps | ||
543 | 6 9 Mbps | ||
544 | 7 12 Mbps | ||
545 | 8 18 Mbps | ||
546 | 9 24 Mbps | ||
547 | 10 36 Mbps | ||
548 | 11 48 Mbps | ||
549 | 12 54 Mbps | ||
550 | 12-15 Reserved | ||
551 | |||
552 | Usage: | ||
553 | iwpriv ethX rateadapt | ||
554 | read the currect data rate setting | ||
555 | iwpriv ethX rateadapt 1 0x07 | ||
556 | enable auto data rate adapt and | ||
557 | data rates are 1Mbps, 2Mbsp and 5.5Mbps | ||
558 | |||
559 | |||
560 | txcontrol | ||
561 | This command is used to set the Tx rate, ack policy, and retry limit on a per packet basis. | ||
562 | |||
563 | Where value <n> is: | ||
564 | if bit[4] == 1: | ||
565 | bit[3:0] -- 0 1 2 3 4 5 6 7 8 9 10 11 12 13-16 | ||
566 | Data Rate(Mbps) -- 1 2 5.5 11 Rsv 6 9 12 18 24 36 48 54 Rsv | ||
567 | |||
568 | bit[12:8] | ||
569 | if bit[12] == 1, bit[11:8] specifies the Tx retry limit. | ||
570 | |||
571 | bit[14:13] specifies per packet ack policy: | ||
572 | bit[14:13] | ||
573 | 1 0 use immediate ack policy for this packet | ||
574 | 1 1 use no ack policy for this packet | ||
575 | 0 x use the per-packet ack policy setting | ||
576 | |||
577 | Usage: | ||
578 | iwpriv ethX txcontrol 0x7513 | ||
579 | Use no-ack policy, 5 retires for Tx, 11Mbps rate | ||
580 | |||
581 | |||
582 | |||
583 | psnullinterval | ||
584 | This command is used to set/request NULL package interval for Power Save | ||
585 | under infrastructure mode. | ||
586 | |||
587 | where value is:- | ||
588 | -1 -- Disabled | ||
589 | n>0 -- Set interval as n (seconds) | ||
590 | |||
591 | prescan | ||
592 | This command is used to enable (1)/disable(0) auto prescan before assoicate to the ap | ||
593 | |||
594 | where value is:- | ||
595 | 0 -- Disabled | ||
596 | 1 -- Enabled | ||
597 | 2 -- Get | ||
598 | |||
599 | getrxinfo | ||
600 | This command gets non average value of Signal to Noise Ratio of Data and rate index. | ||
601 | |||
602 | The following table shows RateIndex and Rate | ||
603 | |||
604 | RateIndex Data rate | ||
605 | 0 1 Mbps | ||
606 | 1 2 Mbps | ||
607 | 2 5.5 Mbps | ||
608 | 3 11 Mbps | ||
609 | 4 Reserved | ||
610 | 5 6 Mbps | ||
611 | 6 9 Mbps | ||
612 | 7 12 Mbps | ||
613 | 8 18 Mbps | ||
614 | 9 24 Mbps | ||
615 | 10 36 Mbps | ||
616 | 11 48 Mbps | ||
617 | 12 54 Mbps | ||
618 | 13-15 Reserved | ||
619 | |||
620 | gettxrate | ||
621 | This command gets current Tx rate index of the first packet associated with Rate Adaptation. | ||
622 | |||
623 | The following table shows RateIndex and Rate | ||
624 | |||
625 | RateIndex Data rate | ||
626 | 0 1 Mbps | ||
627 | 1 2 Mbps | ||
628 | 2 5.5 Mbps | ||
629 | 3 11 Mbps | ||
630 | 4 Reserved | ||
631 | 5 6 Mbps | ||
632 | 6 9 Mbps | ||
633 | 7 12 Mbps | ||
634 | 8 18 Mbps | ||
635 | 9 24 Mbps | ||
636 | 10 36 Mbps | ||
637 | 11 48 Mbps | ||
638 | 12 54 Mbps | ||
639 | 13-15 Reserved | ||
640 | |||
641 | bcninterval | ||
642 | This command is used to sets beacon interval in adhoc mode when an argument is given, and gets current adhoc | ||
643 | beacon interval when no argument is given. The valid beacon interval is between 20 - 1000, | ||
644 | default beacon interval is 100. | ||
645 | |||
646 | Usage: | ||
647 | iwpriv ethX bcninterval 100 (set adhoc beacon interval to 100) | ||
648 | iwpriv ethX bcninterval (get adhoc beacon interval) | ||
649 | |||
650 | fwt_add | ||
651 | This command is used to insert an entry into the FWT table. The list of | ||
652 | parameters must follow the following structure: | ||
653 | |||
654 | iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr] | ||
655 | |||
656 | The parameters between brackets are optional, but they must appear in | ||
657 | the order specified. For example, if you want to specify the metric, | ||
658 | you must also specify the dir, ssn, and dsn but you need not specify the | ||
659 | hopcount, expiration, sleepmode, or snr. Any unspecified parameters | ||
660 | will be assigned the defaults specified below. | ||
661 | |||
662 | The different parameters are:- | ||
663 | da -- DA MAC address in the form 00:11:22:33:44:55 | ||
664 | ra -- RA MAC address in the form 00:11:22:33:44:55 | ||
665 | metric -- route metric (cost: smaller-metric routes are | ||
666 | preferred, default is 0) | ||
667 | dir -- direction (1 for direct, 0 for reverse, | ||
668 | default is 1) | ||
669 | ssn -- Source Sequence Number (time at the RA for | ||
670 | reverse routes. Default is 0) | ||
671 | dsn -- Destination Sequence Number (time at the DA | ||
672 | for direct routes. Default is 0) | ||
673 | hopcount -- hop count (currently unused, default is 0) | ||
674 | ttl -- TTL (Only used in reverse entries) | ||
675 | expiration -- entry expiration (in ticks, where a tick is | ||
676 | 1024us, or ~ 1ms. Use 0 for an indefinite | ||
677 | entry, default is 0) | ||
678 | sleepmode -- RA's sleep mode (currently unused, default is | ||
679 | 0) | ||
680 | snr -- SNR in the link to RA (currently unused, | ||
681 | default is 0) | ||
682 | |||
683 | The command does not return anything. | ||
684 | |||
685 | fwt_del | ||
686 | This command is used to remove an entry to the FWT table. The list of | ||
687 | parameters must follow the following structure: | ||
688 | |||
689 | iwpriv ethX fwt_del da ra [dir] | ||
690 | |||
691 | where the different parameters are:- | ||
692 | da -- DA MAC address (in the form "00:11:22:33:44:55") | ||
693 | ra -- RA MAC address (in the form "00:11:22:33:44:55") | ||
694 | dir -- direction (1 for direct, 0 for reverse, | ||
695 | default is 1) | ||
696 | |||
697 | The command does not return anything. | ||
698 | |||
699 | fwt_lookup | ||
700 | This command is used to get the best route in the FWT table to a given | ||
701 | host. The only parameter is the MAC address of the host that is being | ||
702 | looked for. | ||
703 | |||
704 | iwpriv ethX fwt_lookup da | ||
705 | |||
706 | where:- | ||
707 | da -- DA MAC address (in the form "00:11:22:33:44:55") | ||
708 | |||
709 | The command returns an output string identical to the one returned by | ||
710 | fwt_list described below. | ||
711 | |||
712 | |||
713 | fwt_list | ||
714 | This command is used to list a route from the FWT table. The only | ||
715 | parameter is the index into the table. If you want to list all the | ||
716 | routes in a table, start with index=0, and keep listing until you get a | ||
717 | "(null)" string. Note that the indicies may change as the fwt is | ||
718 | updated. It is expected that most users will not use fwt_list directly, | ||
719 | but that a utility similar to the traditional route command will be used | ||
720 | to invoke fwt_list over and over. | ||
721 | |||
722 | iwpriv ethX fwt_list index | ||
723 | |||
724 | The output is a string of the following form: | ||
725 | |||
726 | da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr | ||
727 | |||
728 | where the different fields are:- | ||
729 | da -- DA MAC address (in the form "00:11:22:33:44:55") | ||
730 | ra -- RA MAC address (in the form "00:11:22:33:44:55") | ||
731 | metric -- route metric (cost: smaller-metric routes are preferred) | ||
732 | dir -- direction (1 for direct, 0 for reverse) | ||
733 | ssn -- Source Sequence Number (time at the RA for reverse routes) | ||
734 | dsn -- Destination Sequence Number (time at the DA for direct routes) | ||
735 | hopcount -- hop count (currently unused) | ||
736 | ttl -- TTL (only used in reverse entries) | ||
737 | expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) | ||
738 | sleepmode -- RA's sleep mode (currently unused) | ||
739 | snr -- SNR in the link to RA (currently unused) | ||
740 | |||
741 | fwt_list_route | ||
742 | This command is used to list a route from the FWT table. The only | ||
743 | parameter is the route ID. If you want to list all the routes in a | ||
744 | table, start with rid=0, and keep incrementing rid until you get a | ||
745 | "(null)" string. This function is similar to fwt_list. The only | ||
746 | difference is the output format. Also note that this command is meant | ||
747 | for debugging. It is expected that users will use fwt_lookup and | ||
748 | fwt_list. One important reason for this is that the route id may change | ||
749 | as the route table is altered. | ||
750 | |||
751 | iwpriv ethX fwt_list_route rid | ||
752 | |||
753 | The output is a string of the following form: | ||
754 | |||
755 | da metric dir nid ssn dsn hopcount ttl expiration | ||
756 | |||
757 | where the different fields are:- | ||
758 | da -- DA MAC address (in the form "00:11:22:33:44:55") | ||
759 | metric -- route metric (cost: smaller-metric routes are preferred) | ||
760 | dir -- direction (1 for direct, 0 for reverse) | ||
761 | nid -- Next-hop (neighbor) host ID (nid) | ||
762 | ssn -- Source Sequence Number (time at the RA for reverse routes) | ||
763 | dsn -- Destination Sequence Number (time at the DA for direct routes) | ||
764 | hopcount -- hop count (currently unused) | ||
765 | ttl -- TTL count (only used in reverse entries) | ||
766 | expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) | ||
767 | |||
768 | fwt_list_neigh | ||
769 | This command is used to list a neighbor from the FWT table. The only | ||
770 | parameter is the neighbor ID. If you want to list all the neighbors in a | ||
771 | table, start with nid=0, and keep incrementing nid until you get a | ||
772 | "(null)" string. Note that the nid from a fwt_list_route command can be | ||
773 | used as an input to this command. Also note that this command is meant | ||
774 | mostly for debugging. It is expected that users will use fwt_lookup. | ||
775 | One important reason for this is that the neighbor id may change as the | ||
776 | neighbor table is altered. | ||
777 | |||
778 | iwpriv ethX fwt_list_neigh nid | ||
779 | |||
780 | The output is a string of the following form: | ||
781 | |||
782 | ra sleepmode snr references | ||
783 | |||
784 | where the different fields are:- | ||
785 | ra -- RA MAC address (in the form "00:11:22:33:44:55") | ||
786 | sleepmode -- RA's sleep mode (currently unused) | ||
787 | snr -- SNR in the link to RA (currently unused) | ||
788 | references -- RA's reference counter | ||
789 | |||
790 | fwt_reset | ||
791 | This command is used to reset the FWT table, getting rid of all the | ||
792 | entries. There are no input parameters. | ||
793 | |||
794 | iwpriv ethX fwt_reset | ||
795 | |||
796 | The command does not return anything. | ||
797 | |||
798 | fwt_cleanup | ||
799 | This command is used to perform user-based garbage recollection. The | ||
800 | FWT table is checked, and all the entries that are expired or invalid | ||
801 | are cleaned. Note that this is exported to the driver for debugging | ||
802 | purposes, as garbage collection is also fired by the firmware when in | ||
803 | space problems. There are no input parameters. | ||
804 | |||
805 | iwpriv ethX fwt_cleanup | ||
806 | |||
807 | The command does returns the number of invalid/expired routes deleted. | ||
808 | |||
809 | fwt_time | ||
810 | This command returns a card's internal time representation. It is this | ||
811 | time that is used to represent the expiration times of FWT entries. The | ||
812 | number is not consistent from card to card; it is simply a timer count. | ||
813 | The fwt_time command is used to inspect the timer so that expiration | ||
814 | times reported by fwt_list can be properly interpreted. | ||
815 | |||
816 | iwpriv ethX fwt_time | ||
817 | |||
818 | mesh_get_ttl | ||
819 | |||
820 | The mesh ttl is the number of hops a mesh packet can traverse before it | ||
821 | is dropped. This parameter is used to prevent infinite loops in the | ||
822 | mesh network. The value returned by this function is the ttl assigned | ||
823 | to all mesh packets. Currently there is no way to control the ttl on a | ||
824 | per packet or per socket basis. | ||
825 | |||
826 | iwpriv ethX mesh_get_ttl | ||
827 | |||
828 | mesh_set_ttl ttl | ||
829 | |||
830 | Set the ttl. The argument must be between 0 and 255. | ||
831 | |||
832 | iwpriv ethX mesh_set_ttl <ttl> | ||
833 | |||
834 | ========================= | ||
835 | ETHTOOL | ||
836 | ========================= | ||
837 | |||
838 | |||
839 | Use the -i option to retrieve version information from the driver. | ||
840 | |||
841 | # ethtool -i eth0 | ||
842 | driver: libertas | ||
843 | version: COMM-USB8388-318.p4 | ||
844 | firmware-version: 5.110.7 | ||
845 | bus-info: | ||
846 | |||
847 | Use the -e option to read the EEPROM contents of the card. | ||
848 | |||
849 | Usage: | ||
850 | ethtool -e ethX [raw on|off] [offset N] [length N] | ||
851 | |||
852 | -e retrieves and prints an EEPROM dump for the specified ethernet | ||
853 | device. When raw is enabled, then it dumps the raw EEPROM data | ||
854 | to stdout. The length and offset parameters allow dumping cer- | ||
855 | tain portions of the EEPROM. Default is to dump the entire EEP- | ||
856 | ROM. | ||
857 | |||
858 | # ethtool -e eth0 offset 0 length 16 | ||
859 | Offset Values | ||
860 | ------ ------ | ||
861 | 0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 | ||
862 | |||
863 | ======================== | ||
864 | DEBUGFS COMMANDS | ||
865 | ======================== | ||
866 | |||
867 | those commands are used via debugfs interface | ||
868 | |||
869 | =========== | ||
870 | rdmac | ||
871 | rdbbp | ||
872 | rdrf | ||
873 | These commands are used to read the MAC, BBP and RF registers from the | ||
874 | card. These commands take one parameter that specifies the offset | ||
875 | location that is to be read. This parameter must be specified in | ||
876 | hexadecimal (its possible to preceed preceding the number with a "0x"). | ||
877 | |||
878 | Path: /debugfs/libertas_wireless/ethX/registers/ | ||
879 | |||
880 | Usage: | ||
881 | echo "0xa123" > rdmac ; cat rdmac | ||
882 | echo "0xa123" > rdbbp ; cat rdbbp | ||
883 | echo "0xa123" > rdrf ; cat rdrf | ||
884 | wrmac | ||
885 | wrbbp | ||
886 | wrrf | ||
887 | These commands are used to write the MAC, BBP and RF registers in the | ||
888 | card. These commands take two parameters that specify the offset | ||
889 | location and the value that is to be written. This parameters must | ||
890 | be specified in hexadecimal (its possible to preceed the number | ||
891 | with a "0x"). | ||
892 | |||
893 | Usage: | ||
894 | echo "0xa123 0xaa" > wrmac | ||
895 | echo "0xa123 0xaa" > wrbbp | ||
896 | echo "0xa123 0xaa" > wrrf | ||
897 | |||
898 | sleepparams | ||
899 | This command is used to set the sleepclock configurations | ||
900 | |||
901 | Path: /debugfs/libertas_wireless/ethX/ | ||
902 | |||
903 | Usage: | ||
904 | cat sleepparams: reads the current sleepclock configuration | ||
905 | |||
906 | echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. | ||
907 | |||
908 | where: | ||
909 | p1 is Sleep clock error in ppm (0-65535) | ||
910 | p2 is Wakeup offset in usec (0-65535) | ||
911 | p3 is Clock stabilization time in usec (0-65535) | ||
912 | p4 is Control periodic calibration (0-2) | ||
913 | p5 is Control the use of external sleep clock (0-2) | ||
914 | p6 is reserved for debug (0-65535) | ||
915 | |||
916 | subscribed_events | ||
917 | |||
918 | The subscribed_events directory contains the interface for the | ||
919 | subscribed events API. | ||
920 | |||
921 | Path: /debugfs/libertas_wireless/ethX/subscribed_events/ | ||
922 | |||
923 | Each event is represented by a filename. Each filename consists of the | ||
924 | following three fields: | ||
925 | Value Frequency Subscribed | ||
926 | |||
927 | To read the current values for a given event, do: | ||
928 | cat event | ||
929 | To set the current values, do: | ||
930 | echo "60 2 1" > event | ||
931 | |||
932 | Frequency field specifies the reporting frequency for this event. | ||
933 | If it is set to 0, then the event is reported only once, and then | ||
934 | automatically unsubscribed. If it is set to 1, then the event is | ||
935 | reported every time it occurs. If it is set to N, then the event is | ||
936 | reported every Nth time it occurs. | ||
937 | |||
938 | beacon_missed | ||
939 | Value field specifies the number of consecutive missing beacons which | ||
940 | triggers the LINK_LOSS event. This event is generated only once after | ||
941 | which the firmware resets its state. At initialization, the LINK_LOSS | ||
942 | event is subscribed by default. The default value of MissedBeacons is | ||
943 | 60. | ||
944 | |||
945 | failure_count | ||
946 | Value field specifies the consecutive failure count threshold which | ||
947 | triggers the generation of the MAX_FAIL event. Once this event is | ||
948 | generated, the consecutive failure count is reset to 0. | ||
949 | At initialization, the MAX_FAIL event is NOT subscribed by | ||
950 | default. | ||
951 | |||
952 | high_rssi | ||
953 | This event is generated when the average received RSSI in beacons goes | ||
954 | above a threshold, specified by Value. | ||
955 | |||
956 | low_rssi | ||
957 | This event is generated when the average received RSSI in beacons goes | ||
958 | below a threshold, specified by Value. | ||
959 | |||
960 | high_snr | ||
961 | This event is generated when the average received SNR in beacons goes | ||
962 | above a threshold, specified by Value. | ||
963 | |||
964 | low_snr | ||
965 | This event is generated when the average received SNR in beacons goes | ||
966 | below a threshold, specified by Value. | ||
967 | |||
968 | extscan | ||
969 | This command is used to do a specific scan. | ||
970 | |||
971 | Path: /debugfs/libertas_wireless/ethX/ | ||
972 | |||
973 | Usage: echo "SSID" > extscan | ||
974 | |||
975 | Example: | ||
976 | echo "LINKSYS-AP" > extscan | ||
977 | |||
978 | To see the results of use getscantable command. | ||
979 | |||
980 | getscantable | ||
981 | |||
982 | Display the current contents of the driver scan table (ie. get the | ||
983 | scan results). | ||
984 | |||
985 | Path: /debugfs/libertas_wireless/ethX/ | ||
986 | |||
987 | Usage: | ||
988 | cat getscantable | ||
989 | |||
990 | setuserscan | ||
991 | Initiate a customized scan and retrieve the results | ||
992 | |||
993 | |||
994 | Path: /debugfs/libertas_wireless/ethX/ | ||
995 | |||
996 | Usage: | ||
997 | echo "[ARGS]" > setuserscan | ||
998 | |||
999 | where [ARGS]: | ||
1000 | |||
1001 | chan=[chan#][band][mode] where band is [a,b,g] and mode is | ||
1002 | blank for active or 'p' for passive | ||
1003 | bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan | ||
1004 | ssid="[SSID]" specify a SSID filter for the scan | ||
1005 | keep=[0 or 1] keep the previous scan results (1), discard (0) | ||
1006 | dur=[scan time] time to scan for each channel in milliseconds | ||
1007 | probes=[#] number of probe requests to send on each chan | ||
1008 | type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) | ||
1009 | |||
1010 | Any combination of the above arguments can be supplied on the command line. | ||
1011 | If the chan token is absent, a full channel scan will be completed by | ||
1012 | the driver. If the dur or probes tokens are absent, the driver default | ||
1013 | setting will be used. The bssid and ssid fields, if blank, | ||
1014 | will produce an unfiltered scan. The type field will default to 3 (Any) | ||
1015 | and the keep field will default to 0 (Discard). | ||
1016 | |||
1017 | Examples: | ||
1018 | 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: | ||
1019 | echo "chan=1g,6g,11g" > setuserscan | ||
1020 | |||
1021 | 2) Perform a passive scan on channel 11 for 20 ms: | ||
1022 | echo "chan=11gp dur=20" > setuserscan | ||
1023 | |||
1024 | 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on | ||
1025 | channel 36 in the 'a' band: | ||
1026 | |||
1027 | echo "chan=1g,6g,11g,36ap" > setuserscan | ||
1028 | |||
1029 | 4) Perform an active scan on channel 6 and 36 for a specific SSID: | ||
1030 | echo "chan=6g,36a ssid="TestAP"" > setuserscan | ||
1031 | |||
1032 | 5) Scan all available channels (B/G, A bands) for a specific BSSID, keep | ||
1033 | the current scan table intact, update existing or append new scan data: | ||
1034 | echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan | ||
1035 | |||
1036 | 6) Scan channel 6, for all infrastructure networks, sending two probe | ||
1037 | requests. Keep the previous scan table intact. Update any duplicate | ||
1038 | BSSID/SSID matches with the new scan data: | ||
1039 | echo "chan=6g type=1 probes=2 keep=1" > setuserscan | ||
1040 | |||
1041 | All entries in the scan table (not just the new scan data when keep=1) | ||
1042 | will be displayed upon completion by use of the getscantable ioctl. | ||
1043 | |||
1044 | ============================================================================== | ||
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c new file mode 100644 index 000000000000..b55c7f57aca8 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.c | |||
@@ -0,0 +1,588 @@ | |||
1 | /* Copyright (C) 2006, Red Hat, Inc. */ | ||
2 | |||
3 | #include <linux/bitops.h> | ||
4 | #include <net/ieee80211.h> | ||
5 | |||
6 | #include "assoc.h" | ||
7 | #include "join.h" | ||
8 | #include "decl.h" | ||
9 | #include "hostcmd.h" | ||
10 | #include "host.h" | ||
11 | |||
12 | |||
13 | static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
14 | static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
15 | |||
16 | static int assoc_helper_essid(wlan_private *priv, | ||
17 | struct assoc_request * assoc_req) | ||
18 | { | ||
19 | wlan_adapter *adapter = priv->adapter; | ||
20 | int ret = 0; | ||
21 | int i; | ||
22 | |||
23 | ENTER(); | ||
24 | |||
25 | lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid); | ||
26 | if (assoc_req->mode == wlan802_11infrastructure) { | ||
27 | if (adapter->prescan) { | ||
28 | libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1); | ||
29 | } | ||
30 | |||
31 | i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, | ||
32 | NULL, wlan802_11infrastructure); | ||
33 | if (i >= 0) { | ||
34 | lbs_pr_debug(1, | ||
35 | "SSID found in scan list ... associating...\n"); | ||
36 | |||
37 | ret = wlan_associate(priv, &adapter->scantable[i]); | ||
38 | if (ret == 0) { | ||
39 | memcpy(&assoc_req->bssid, | ||
40 | &adapter->scantable[i].macaddress, | ||
41 | ETH_ALEN); | ||
42 | } | ||
43 | } else { | ||
44 | lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n", | ||
45 | assoc_req->ssid.ssid); | ||
46 | } | ||
47 | } else if (assoc_req->mode == wlan802_11ibss) { | ||
48 | /* Scan for the network, do not save previous results. Stale | ||
49 | * scan data will cause us to join a non-existant adhoc network | ||
50 | */ | ||
51 | libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0); | ||
52 | |||
53 | /* Search for the requested SSID in the scan table */ | ||
54 | i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL, | ||
55 | wlan802_11ibss); | ||
56 | if (i >= 0) { | ||
57 | lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret); | ||
58 | libertas_join_adhoc_network(priv, &adapter->scantable[i]); | ||
59 | } else { | ||
60 | /* else send START command */ | ||
61 | lbs_pr_debug(1, "SSID not found in list, so creating adhoc" | ||
62 | " with SSID '%s'\n", assoc_req->ssid.ssid); | ||
63 | libertas_start_adhoc_network(priv, &assoc_req->ssid); | ||
64 | } | ||
65 | memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN); | ||
66 | } | ||
67 | |||
68 | LEAVE(); | ||
69 | return ret; | ||
70 | } | ||
71 | |||
72 | |||
73 | static int assoc_helper_bssid(wlan_private *priv, | ||
74 | struct assoc_request * assoc_req) | ||
75 | { | ||
76 | wlan_adapter *adapter = priv->adapter; | ||
77 | int i, ret = 0; | ||
78 | |||
79 | ENTER(); | ||
80 | |||
81 | lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n", | ||
82 | MAC_ARG(assoc_req->bssid)); | ||
83 | |||
84 | /* Search for index position in list for requested MAC */ | ||
85 | i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid, | ||
86 | assoc_req->mode); | ||
87 | if (i < 0) { | ||
88 | lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, " | ||
89 | "cannot associate.\n", MAC_ARG(assoc_req->bssid)); | ||
90 | goto out; | ||
91 | } | ||
92 | |||
93 | if (assoc_req->mode == wlan802_11infrastructure) { | ||
94 | ret = wlan_associate(priv, &adapter->scantable[i]); | ||
95 | lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret); | ||
96 | } else if (assoc_req->mode == wlan802_11ibss) { | ||
97 | libertas_join_adhoc_network(priv, &adapter->scantable[i]); | ||
98 | } | ||
99 | memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid, | ||
100 | sizeof(struct WLAN_802_11_SSID)); | ||
101 | |||
102 | out: | ||
103 | LEAVE(); | ||
104 | return ret; | ||
105 | } | ||
106 | |||
107 | |||
108 | static int assoc_helper_associate(wlan_private *priv, | ||
109 | struct assoc_request * assoc_req) | ||
110 | { | ||
111 | int ret = 0, done = 0; | ||
112 | |||
113 | /* If we're given and 'any' BSSID, try associating based on SSID */ | ||
114 | |||
115 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
116 | if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN) | ||
117 | && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) { | ||
118 | ret = assoc_helper_bssid(priv, assoc_req); | ||
119 | done = 1; | ||
120 | if (ret) { | ||
121 | lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
127 | ret = assoc_helper_essid(priv, assoc_req); | ||
128 | if (ret) { | ||
129 | lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | |||
137 | static int assoc_helper_mode(wlan_private *priv, | ||
138 | struct assoc_request * assoc_req) | ||
139 | { | ||
140 | wlan_adapter *adapter = priv->adapter; | ||
141 | int ret = 0; | ||
142 | |||
143 | ENTER(); | ||
144 | |||
145 | if (assoc_req->mode == adapter->inframode) { | ||
146 | LEAVE(); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | if (assoc_req->mode == wlan802_11infrastructure) { | ||
151 | if (adapter->psstate != PS_STATE_FULL_POWER) | ||
152 | libertas_ps_wakeup(priv, cmd_option_waitforrsp); | ||
153 | adapter->psmode = wlan802_11powermodecam; | ||
154 | } | ||
155 | |||
156 | adapter->inframode = assoc_req->mode; | ||
157 | ret = libertas_prepare_and_send_command(priv, | ||
158 | cmd_802_11_snmp_mib, | ||
159 | 0, cmd_option_waitforrsp, | ||
160 | OID_802_11_INFRASTRUCTURE_MODE, | ||
161 | (void *) assoc_req->mode); | ||
162 | |||
163 | LEAVE(); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | |||
168 | static int assoc_helper_wep_keys(wlan_private *priv, | ||
169 | struct assoc_request * assoc_req) | ||
170 | { | ||
171 | wlan_adapter *adapter = priv->adapter; | ||
172 | int i; | ||
173 | int ret = 0; | ||
174 | |||
175 | ENTER(); | ||
176 | |||
177 | /* Set or remove WEP keys */ | ||
178 | if ( assoc_req->wep_keys[0].len | ||
179 | || assoc_req->wep_keys[1].len | ||
180 | || assoc_req->wep_keys[2].len | ||
181 | || assoc_req->wep_keys[3].len) { | ||
182 | ret = libertas_prepare_and_send_command(priv, | ||
183 | cmd_802_11_set_wep, | ||
184 | cmd_act_add, | ||
185 | cmd_option_waitforrsp, | ||
186 | 0, assoc_req); | ||
187 | } else { | ||
188 | ret = libertas_prepare_and_send_command(priv, | ||
189 | cmd_802_11_set_wep, | ||
190 | cmd_act_remove, | ||
191 | cmd_option_waitforrsp, | ||
192 | 0, NULL); | ||
193 | } | ||
194 | |||
195 | if (ret) | ||
196 | goto out; | ||
197 | |||
198 | /* enable/disable the MAC's WEP packet filter */ | ||
199 | if (assoc_req->secinfo.WEPstatus == wlan802_11WEPenabled) | ||
200 | adapter->currentpacketfilter |= cmd_act_mac_wep_enable; | ||
201 | else | ||
202 | adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable; | ||
203 | ret = libertas_set_mac_packet_filter(priv); | ||
204 | if (ret) | ||
205 | goto out; | ||
206 | |||
207 | mutex_lock(&adapter->lock); | ||
208 | |||
209 | /* Copy WEP keys into adapter wep key fields */ | ||
210 | for (i = 0; i < 4; i++) { | ||
211 | memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i], | ||
212 | sizeof(struct WLAN_802_11_KEY)); | ||
213 | } | ||
214 | adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx; | ||
215 | |||
216 | mutex_unlock(&adapter->lock); | ||
217 | |||
218 | out: | ||
219 | LEAVE(); | ||
220 | return ret; | ||
221 | } | ||
222 | |||
223 | static int assoc_helper_secinfo(wlan_private *priv, | ||
224 | struct assoc_request * assoc_req) | ||
225 | { | ||
226 | wlan_adapter *adapter = priv->adapter; | ||
227 | int ret = 0; | ||
228 | |||
229 | ENTER(); | ||
230 | |||
231 | memcpy(&adapter->secinfo, &assoc_req->secinfo, | ||
232 | sizeof(struct wlan_802_11_security)); | ||
233 | |||
234 | ret = libertas_set_mac_packet_filter(priv); | ||
235 | |||
236 | LEAVE(); | ||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | |||
241 | static int assoc_helper_wpa_keys(wlan_private *priv, | ||
242 | struct assoc_request * assoc_req) | ||
243 | { | ||
244 | int ret = 0; | ||
245 | |||
246 | ENTER(); | ||
247 | |||
248 | /* enable/Disable RSN */ | ||
249 | ret = libertas_prepare_and_send_command(priv, | ||
250 | cmd_802_11_enable_rsn, | ||
251 | cmd_act_set, | ||
252 | cmd_option_waitforrsp, | ||
253 | 0, assoc_req); | ||
254 | if (ret) | ||
255 | goto out; | ||
256 | |||
257 | ret = libertas_prepare_and_send_command(priv, | ||
258 | cmd_802_11_key_material, | ||
259 | cmd_act_set, | ||
260 | cmd_option_waitforrsp, | ||
261 | 0, assoc_req); | ||
262 | |||
263 | out: | ||
264 | LEAVE(); | ||
265 | return ret; | ||
266 | } | ||
267 | |||
268 | |||
269 | static int assoc_helper_wpa_ie(wlan_private *priv, | ||
270 | struct assoc_request * assoc_req) | ||
271 | { | ||
272 | wlan_adapter *adapter = priv->adapter; | ||
273 | int ret = 0; | ||
274 | |||
275 | ENTER(); | ||
276 | |||
277 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | ||
278 | memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); | ||
279 | adapter->wpa_ie_len = assoc_req->wpa_ie_len; | ||
280 | } else { | ||
281 | memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN); | ||
282 | adapter->wpa_ie_len = 0; | ||
283 | } | ||
284 | |||
285 | LEAVE(); | ||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | |||
290 | static int should_deauth_infrastructure(wlan_adapter *adapter, | ||
291 | struct assoc_request * assoc_req) | ||
292 | { | ||
293 | if (adapter->connect_status != libertas_connected) | ||
294 | return 0; | ||
295 | |||
296 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
297 | lbs_pr_debug(1, "Deauthenticating due to new SSID in " | ||
298 | " configuration request.\n"); | ||
299 | return 1; | ||
300 | } | ||
301 | |||
302 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | ||
303 | if (adapter->secinfo.authmode != | ||
304 | assoc_req->secinfo.authmode) { | ||
305 | lbs_pr_debug(1, "Deauthenticating due to updated security " | ||
306 | "info in configuration request.\n"); | ||
307 | return 1; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
312 | lbs_pr_debug(1, "Deauthenticating due to new BSSID in " | ||
313 | " configuration request.\n"); | ||
314 | return 1; | ||
315 | } | ||
316 | |||
317 | /* FIXME: deal with 'auto' mode somehow */ | ||
318 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | ||
319 | if (assoc_req->mode != wlan802_11infrastructure) | ||
320 | return 1; | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | |||
327 | static int should_stop_adhoc(wlan_adapter *adapter, | ||
328 | struct assoc_request * assoc_req) | ||
329 | { | ||
330 | if (adapter->connect_status != libertas_connected) | ||
331 | return 0; | ||
332 | |||
333 | if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength) | ||
334 | return 1; | ||
335 | if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid, | ||
336 | sizeof(struct WLAN_802_11_SSID))) | ||
337 | return 1; | ||
338 | |||
339 | /* FIXME: deal with 'auto' mode somehow */ | ||
340 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | ||
341 | if (assoc_req->mode != wlan802_11ibss) | ||
342 | return 1; | ||
343 | } | ||
344 | |||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | |||
349 | void wlan_association_worker(struct work_struct *work) | ||
350 | { | ||
351 | wlan_private *priv = container_of(work, wlan_private, assoc_work.work); | ||
352 | wlan_adapter *adapter = priv->adapter; | ||
353 | struct assoc_request * assoc_req = NULL; | ||
354 | int ret = 0; | ||
355 | int find_any_ssid = 0; | ||
356 | |||
357 | ENTER(); | ||
358 | |||
359 | mutex_lock(&adapter->lock); | ||
360 | assoc_req = adapter->assoc_req; | ||
361 | adapter->assoc_req = NULL; | ||
362 | mutex_unlock(&adapter->lock); | ||
363 | |||
364 | if (!assoc_req) { | ||
365 | LEAVE(); | ||
366 | return; | ||
367 | } | ||
368 | |||
369 | lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n", | ||
370 | assoc_req->flags); | ||
371 | |||
372 | /* If 'any' SSID was specified, find an SSID to associate with */ | ||
373 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) | ||
374 | && !assoc_req->ssid.ssidlength) | ||
375 | find_any_ssid = 1; | ||
376 | |||
377 | /* But don't use 'any' SSID if there's a valid locked BSSID to use */ | ||
378 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
379 | if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN) | ||
380 | && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN)) | ||
381 | find_any_ssid = 0; | ||
382 | } | ||
383 | |||
384 | if (find_any_ssid) { | ||
385 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; | ||
386 | |||
387 | ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid, | ||
388 | assoc_req->mode, &new_mode); | ||
389 | if (ret) { | ||
390 | lbs_pr_debug(1, "Could not find best network\n"); | ||
391 | ret = -ENETUNREACH; | ||
392 | goto out; | ||
393 | } | ||
394 | |||
395 | /* Ensure we switch to the mode of the AP */ | ||
396 | if (assoc_req->mode == wlan802_11autounknown) { | ||
397 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); | ||
398 | assoc_req->mode = new_mode; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * Check if the attributes being changing require deauthentication | ||
404 | * from the currently associated infrastructure access point. | ||
405 | */ | ||
406 | if (adapter->inframode == wlan802_11infrastructure) { | ||
407 | if (should_deauth_infrastructure(adapter, assoc_req)) { | ||
408 | ret = libertas_send_deauthentication(priv); | ||
409 | if (ret) { | ||
410 | lbs_pr_debug(1, "Deauthentication due to new " | ||
411 | "configuration request failed: %d\n", | ||
412 | ret); | ||
413 | } | ||
414 | } | ||
415 | } else if (adapter->inframode == wlan802_11ibss) { | ||
416 | if (should_stop_adhoc(adapter, assoc_req)) { | ||
417 | ret = libertas_stop_adhoc_network(priv); | ||
418 | if (ret) { | ||
419 | lbs_pr_debug(1, "Teardown of AdHoc network due to " | ||
420 | "new configuration request failed: %d\n", | ||
421 | ret); | ||
422 | } | ||
423 | |||
424 | } | ||
425 | } | ||
426 | |||
427 | /* Send the various configuration bits to the firmware */ | ||
428 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | ||
429 | ret = assoc_helper_mode(priv, assoc_req); | ||
430 | if (ret) { | ||
431 | lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret); | ||
432 | goto out; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) | ||
437 | || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { | ||
438 | ret = assoc_helper_wep_keys(priv, assoc_req); | ||
439 | if (ret) { | ||
440 | lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret); | ||
441 | goto out; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | ||
446 | ret = assoc_helper_secinfo(priv, assoc_req); | ||
447 | if (ret) { | ||
448 | lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret); | ||
449 | goto out; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | ||
454 | ret = assoc_helper_wpa_ie(priv, assoc_req); | ||
455 | if (ret) { | ||
456 | lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret); | ||
457 | goto out; | ||
458 | } | ||
459 | } | ||
460 | |||
461 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) | ||
462 | || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | ||
463 | ret = assoc_helper_wpa_keys(priv, assoc_req); | ||
464 | if (ret) { | ||
465 | lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret); | ||
466 | goto out; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | /* SSID/BSSID should be the _last_ config option set, because they | ||
471 | * trigger the association attempt. | ||
472 | */ | ||
473 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) | ||
474 | || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
475 | int success = 1; | ||
476 | |||
477 | ret = assoc_helper_associate(priv, assoc_req); | ||
478 | if (ret) { | ||
479 | lbs_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n", | ||
480 | ret); | ||
481 | success = 0; | ||
482 | } | ||
483 | |||
484 | if (adapter->connect_status != libertas_connected) { | ||
485 | lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, " | ||
486 | "not connected.\n"); | ||
487 | success = 0; | ||
488 | } | ||
489 | |||
490 | if (success) { | ||
491 | lbs_pr_debug(1, "ASSOC: association attempt successful. " | ||
492 | "Associated to '%s' (" MAC_FMT ")\n", | ||
493 | assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid)); | ||
494 | libertas_prepare_and_send_command(priv, | ||
495 | cmd_802_11_rssi, | ||
496 | 0, cmd_option_waitforrsp, 0, NULL); | ||
497 | |||
498 | libertas_prepare_and_send_command(priv, | ||
499 | cmd_802_11_get_log, | ||
500 | 0, cmd_option_waitforrsp, 0, NULL); | ||
501 | } else { | ||
502 | |||
503 | ret = -1; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | out: | ||
508 | if (ret) { | ||
509 | lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n", | ||
510 | ret); | ||
511 | } | ||
512 | kfree(assoc_req); | ||
513 | LEAVE(); | ||
514 | } | ||
515 | |||
516 | |||
517 | /* | ||
518 | * Caller MUST hold any necessary locks | ||
519 | */ | ||
520 | struct assoc_request * wlan_get_association_request(wlan_adapter *adapter) | ||
521 | { | ||
522 | struct assoc_request * assoc_req; | ||
523 | |||
524 | if (!adapter->assoc_req) { | ||
525 | adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL); | ||
526 | if (!adapter->assoc_req) { | ||
527 | lbs_pr_info("Not enough memory to allocate association" | ||
528 | " request!\n"); | ||
529 | return NULL; | ||
530 | } | ||
531 | } | ||
532 | |||
533 | /* Copy current configuration attributes to the association request, | ||
534 | * but don't overwrite any that are already set. | ||
535 | */ | ||
536 | assoc_req = adapter->assoc_req; | ||
537 | if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
538 | memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid, | ||
539 | adapter->curbssparams.ssid.ssidlength); | ||
540 | } | ||
541 | |||
542 | if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) | ||
543 | assoc_req->channel = adapter->curbssparams.channel; | ||
544 | |||
545 | if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) | ||
546 | assoc_req->mode = adapter->inframode; | ||
547 | |||
548 | if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
549 | memcpy(&assoc_req->bssid, adapter->curbssparams.bssid, | ||
550 | ETH_ALEN); | ||
551 | } | ||
552 | |||
553 | if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { | ||
554 | int i; | ||
555 | for (i = 0; i < 4; i++) { | ||
556 | memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i], | ||
557 | sizeof(struct WLAN_802_11_KEY)); | ||
558 | } | ||
559 | } | ||
560 | |||
561 | if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) | ||
562 | assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx; | ||
563 | |||
564 | if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | ||
565 | memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key, | ||
566 | sizeof(struct WLAN_802_11_KEY)); | ||
567 | } | ||
568 | |||
569 | if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | ||
570 | memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key, | ||
571 | sizeof(struct WLAN_802_11_KEY)); | ||
572 | } | ||
573 | |||
574 | if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | ||
575 | memcpy(&assoc_req->secinfo, &adapter->secinfo, | ||
576 | sizeof(struct wlan_802_11_security)); | ||
577 | } | ||
578 | |||
579 | if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | ||
580 | memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie, | ||
581 | MAX_WPA_IE_LEN); | ||
582 | assoc_req->wpa_ie_len = adapter->wpa_ie_len; | ||
583 | } | ||
584 | |||
585 | return assoc_req; | ||
586 | } | ||
587 | |||
588 | |||
diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h new file mode 100644 index 000000000000..2ffd82d99b34 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* Copyright (C) 2006, Red Hat, Inc. */ | ||
2 | |||
3 | #ifndef _WLAN_ASSOC_H_ | ||
4 | #define _WLAN_ASSOC_H_ | ||
5 | |||
6 | #include "dev.h" | ||
7 | |||
8 | void wlan_association_worker(struct work_struct *work); | ||
9 | |||
10 | struct assoc_request * wlan_get_association_request(wlan_adapter *adapter); | ||
11 | |||
12 | #define ASSOC_DELAY (HZ / 2) | ||
13 | static inline void wlan_postpone_association_work(wlan_private *priv) | ||
14 | { | ||
15 | if (priv->adapter->surpriseremoved) | ||
16 | return; | ||
17 | cancel_delayed_work(&priv->assoc_work); | ||
18 | queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY); | ||
19 | } | ||
20 | |||
21 | static inline void wlan_cancel_association_work(wlan_private *priv) | ||
22 | { | ||
23 | cancel_delayed_work(&priv->assoc_work); | ||
24 | if (priv->adapter->assoc_req) { | ||
25 | kfree(priv->adapter->assoc_req); | ||
26 | priv->adapter->assoc_req = NULL; | ||
27 | } | ||
28 | } | ||
29 | |||
30 | #endif /* _WLAN_ASSOC_H */ | ||
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c new file mode 100644 index 000000000000..bfdac58b5c06 --- /dev/null +++ b/drivers/net/wireless/libertas/cmd.c | |||
@@ -0,0 +1,1958 @@ | |||
1 | /** | ||
2 | * This file contains the handling of command. | ||
3 | * It prepares command and sends it to firmware when it is ready. | ||
4 | */ | ||
5 | |||
6 | #include <net/iw_handler.h> | ||
7 | #include "host.h" | ||
8 | #include "hostcmd.h" | ||
9 | #include "sbi.h" | ||
10 | #include "decl.h" | ||
11 | #include "defs.h" | ||
12 | #include "dev.h" | ||
13 | #include "join.h" | ||
14 | #include "wext.h" | ||
15 | |||
16 | static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode); | ||
17 | |||
18 | static u16 commands_allowed_in_ps[] = { | ||
19 | cmd_802_11_rssi, | ||
20 | }; | ||
21 | |||
22 | /** | ||
23 | * @brief This function checks if the commans is allowed | ||
24 | * in PS mode not. | ||
25 | * | ||
26 | * @param command the command ID | ||
27 | * @return TRUE or FALSE | ||
28 | */ | ||
29 | static u8 is_command_allowed_in_ps(u16 command) | ||
30 | { | ||
31 | int count = sizeof(commands_allowed_in_ps) | ||
32 | / sizeof(commands_allowed_in_ps[0]); | ||
33 | int i; | ||
34 | |||
35 | for (i = 0; i < count; i++) { | ||
36 | if (command == cpu_to_le16(commands_allowed_in_ps[i])) | ||
37 | return 1; | ||
38 | } | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd) | ||
44 | { | ||
45 | struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec; | ||
46 | |||
47 | ENTER(); | ||
48 | |||
49 | cmd->command = cpu_to_le16(cmd_get_hw_spec); | ||
50 | cmd->size = | ||
51 | cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN); | ||
52 | memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN); | ||
53 | |||
54 | LEAVE(); | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int wlan_cmd_802_11_ps_mode(wlan_private * priv, | ||
59 | struct cmd_ds_command *cmd, | ||
60 | u16 cmd_action) | ||
61 | { | ||
62 | struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; | ||
63 | u16 action = cmd_action; | ||
64 | wlan_adapter *adapter = priv->adapter; | ||
65 | |||
66 | ENTER(); | ||
67 | |||
68 | cmd->command = cpu_to_le16(cmd_802_11_ps_mode); | ||
69 | cmd->size = | ||
70 | cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + | ||
71 | S_DS_GEN); | ||
72 | psm->action = cpu_to_le16(cmd_action); | ||
73 | psm->multipledtim = 0; | ||
74 | switch (action) { | ||
75 | case cmd_subcmd_enter_ps: | ||
76 | lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n"); | ||
77 | lbs_pr_debug(1, "locallisteninterval = %d\n", | ||
78 | adapter->locallisteninterval); | ||
79 | |||
80 | psm->locallisteninterval = | ||
81 | cpu_to_le16(adapter->locallisteninterval); | ||
82 | psm->nullpktinterval = | ||
83 | cpu_to_le16(adapter->nullpktinterval); | ||
84 | psm->multipledtim = | ||
85 | cpu_to_le16(priv->adapter->multipledtim); | ||
86 | break; | ||
87 | |||
88 | case cmd_subcmd_exit_ps: | ||
89 | lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n"); | ||
90 | break; | ||
91 | |||
92 | case cmd_subcmd_sleep_confirmed: | ||
93 | lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n"); | ||
94 | break; | ||
95 | |||
96 | default: | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | LEAVE(); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv, | ||
105 | struct cmd_ds_command *cmd, | ||
106 | u16 cmd_action, void *pdata_buf) | ||
107 | { | ||
108 | u16 *timeout = pdata_buf; | ||
109 | |||
110 | cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout); | ||
111 | cmd->size = | ||
112 | cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout) | ||
113 | + S_DS_GEN); | ||
114 | |||
115 | cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action); | ||
116 | |||
117 | if (cmd_action) | ||
118 | cmd->params.inactivity_timeout.timeout = | ||
119 | cpu_to_le16(*timeout); | ||
120 | else | ||
121 | cmd->params.inactivity_timeout.timeout = 0; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int wlan_cmd_802_11_sleep_params(wlan_private * priv, | ||
127 | struct cmd_ds_command *cmd, | ||
128 | u16 cmd_action) | ||
129 | { | ||
130 | wlan_adapter *adapter = priv->adapter; | ||
131 | struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params; | ||
132 | |||
133 | ENTER(); | ||
134 | |||
135 | cmd->size = | ||
136 | cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) + | ||
137 | S_DS_GEN); | ||
138 | cmd->command = cpu_to_le16(cmd_802_11_sleep_params); | ||
139 | |||
140 | if (cmd_action == cmd_act_get) { | ||
141 | memset(&adapter->sp, 0, sizeof(struct sleep_params)); | ||
142 | memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params)); | ||
143 | sp->action = cpu_to_le16(cmd_action); | ||
144 | } else if (cmd_action == cmd_act_set) { | ||
145 | sp->action = cpu_to_le16(cmd_action); | ||
146 | sp->error = cpu_to_le16(adapter->sp.sp_error); | ||
147 | sp->offset = cpu_to_le16(adapter->sp.sp_offset); | ||
148 | sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime); | ||
149 | sp->calcontrol = (u8) adapter->sp.sp_calcontrol; | ||
150 | sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk; | ||
151 | sp->reserved = cpu_to_le16(adapter->sp.sp_reserved); | ||
152 | } | ||
153 | |||
154 | LEAVE(); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int wlan_cmd_802_11_set_wep(wlan_private * priv, | ||
159 | struct cmd_ds_command *cmd, | ||
160 | u32 cmd_act, | ||
161 | void * pdata_buf) | ||
162 | { | ||
163 | struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep; | ||
164 | wlan_adapter *adapter = priv->adapter; | ||
165 | int ret = 0; | ||
166 | struct assoc_request * assoc_req = pdata_buf; | ||
167 | |||
168 | ENTER(); | ||
169 | |||
170 | cmd->command = cpu_to_le16(cmd_802_11_set_wep); | ||
171 | cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep)) | ||
172 | + S_DS_GEN); | ||
173 | |||
174 | if (cmd_act == cmd_act_add) { | ||
175 | int i; | ||
176 | |||
177 | if (!assoc_req) { | ||
178 | lbs_pr_debug(1, "Invalid association request!"); | ||
179 | ret = -1; | ||
180 | goto done; | ||
181 | } | ||
182 | |||
183 | wep->action = cpu_to_le16(cmd_act_add); | ||
184 | |||
185 | /* default tx key index */ | ||
186 | wep->keyindex = cpu_to_le16((u16) | ||
187 | (assoc_req->wep_tx_keyidx & | ||
188 | (u32)cmd_WEP_KEY_INDEX_MASK)); | ||
189 | |||
190 | lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex); | ||
191 | |||
192 | /* Copy key types and material to host command structure */ | ||
193 | for (i = 0; i < 4; i++) { | ||
194 | struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i]; | ||
195 | |||
196 | switch (pkey->len) { | ||
197 | case KEY_LEN_WEP_40: | ||
198 | wep->keytype[i] = cmd_type_wep_40_bit; | ||
199 | memmove(&wep->keymaterial[i], pkey->key, | ||
200 | pkey->len); | ||
201 | break; | ||
202 | case KEY_LEN_WEP_104: | ||
203 | wep->keytype[i] = cmd_type_wep_104_bit; | ||
204 | memmove(&wep->keymaterial[i], pkey->key, | ||
205 | pkey->len); | ||
206 | break; | ||
207 | case 0: | ||
208 | break; | ||
209 | default: | ||
210 | lbs_pr_debug(1, "Invalid WEP key %d length of %d\n", | ||
211 | i, pkey->len); | ||
212 | ret = -1; | ||
213 | goto done; | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | } else if (cmd_act == cmd_act_remove) { | ||
218 | /* ACT_REMOVE clears _all_ WEP keys */ | ||
219 | wep->action = cpu_to_le16(cmd_act_remove); | ||
220 | |||
221 | /* default tx key index */ | ||
222 | wep->keyindex = cpu_to_le16((u16) | ||
223 | (adapter->wep_tx_keyidx & | ||
224 | (u32)cmd_WEP_KEY_INDEX_MASK)); | ||
225 | } | ||
226 | |||
227 | ret = 0; | ||
228 | |||
229 | done: | ||
230 | LEAVE(); | ||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static int wlan_cmd_802_11_enable_rsn(wlan_private * priv, | ||
235 | struct cmd_ds_command *cmd, | ||
236 | u16 cmd_action) | ||
237 | { | ||
238 | struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn; | ||
239 | wlan_adapter *adapter = priv->adapter; | ||
240 | |||
241 | cmd->command = cpu_to_le16(cmd_802_11_enable_rsn); | ||
242 | cmd->size = | ||
243 | cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) + | ||
244 | S_DS_GEN); | ||
245 | penableRSN->action = cpu_to_le16(cmd_action); | ||
246 | if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { | ||
247 | penableRSN->enable = cpu_to_le16(cmd_enable_rsn); | ||
248 | } else { | ||
249 | penableRSN->enable = cpu_to_le16(cmd_disable_rsn); | ||
250 | } | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | |||
256 | static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset, | ||
257 | struct WLAN_802_11_KEY * pkey) | ||
258 | { | ||
259 | pkeyparamset->keytypeid = cpu_to_le16(pkey->type); | ||
260 | |||
261 | if (pkey->flags & KEY_INFO_WPA_ENABLED) { | ||
262 | pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED); | ||
263 | } else { | ||
264 | pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED); | ||
265 | } | ||
266 | |||
267 | if (pkey->flags & KEY_INFO_WPA_UNICAST) { | ||
268 | pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); | ||
269 | } else if (pkey->flags & KEY_INFO_WPA_MCAST) { | ||
270 | pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); | ||
271 | } | ||
272 | |||
273 | pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
274 | pkeyparamset->keylen = cpu_to_le16(pkey->len); | ||
275 | memcpy(pkeyparamset->key, pkey->key, pkey->len); | ||
276 | pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid) | ||
277 | + sizeof(pkeyparamset->keyinfo) | ||
278 | + sizeof(pkeyparamset->keylen) | ||
279 | + sizeof(pkeyparamset->key)); | ||
280 | } | ||
281 | |||
282 | static int wlan_cmd_802_11_key_material(wlan_private * priv, | ||
283 | struct cmd_ds_command *cmd, | ||
284 | u16 cmd_action, | ||
285 | u32 cmd_oid, void *pdata_buf) | ||
286 | { | ||
287 | wlan_adapter *adapter = priv->adapter; | ||
288 | struct cmd_ds_802_11_key_material *pkeymaterial = | ||
289 | &cmd->params.keymaterial; | ||
290 | int ret = 0; | ||
291 | int index = 0; | ||
292 | |||
293 | ENTER(); | ||
294 | |||
295 | cmd->command = cpu_to_le16(cmd_802_11_key_material); | ||
296 | pkeymaterial->action = cpu_to_le16(cmd_action); | ||
297 | |||
298 | if (cmd_action == cmd_act_get) { | ||
299 | cmd->size = cpu_to_le16( S_DS_GEN | ||
300 | + sizeof (pkeymaterial->action)); | ||
301 | ret = 0; | ||
302 | goto done; | ||
303 | } | ||
304 | |||
305 | memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet)); | ||
306 | |||
307 | if (adapter->wpa_unicast_key.len) { | ||
308 | set_one_wpa_key(&pkeymaterial->keyParamSet[index], | ||
309 | &adapter->wpa_unicast_key); | ||
310 | index++; | ||
311 | } | ||
312 | |||
313 | if (adapter->wpa_mcast_key.len) { | ||
314 | set_one_wpa_key(&pkeymaterial->keyParamSet[index], | ||
315 | &adapter->wpa_mcast_key); | ||
316 | index++; | ||
317 | } | ||
318 | |||
319 | cmd->size = cpu_to_le16( S_DS_GEN | ||
320 | + sizeof (pkeymaterial->action) | ||
321 | + index * sizeof(struct MrvlIEtype_keyParamSet)); | ||
322 | |||
323 | ret = 0; | ||
324 | |||
325 | done: | ||
326 | LEAVE(); | ||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static int wlan_cmd_802_11_reset(wlan_private * priv, | ||
331 | struct cmd_ds_command *cmd, int cmd_action) | ||
332 | { | ||
333 | struct cmd_ds_802_11_reset *reset = &cmd->params.reset; | ||
334 | |||
335 | cmd->command = cpu_to_le16(cmd_802_11_reset); | ||
336 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN); | ||
337 | reset->action = cpu_to_le16(cmd_action); | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int wlan_cmd_802_11_get_log(wlan_private * priv, | ||
343 | struct cmd_ds_command *cmd) | ||
344 | { | ||
345 | cmd->command = cpu_to_le16(cmd_802_11_get_log); | ||
346 | cmd->size = | ||
347 | cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN); | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static int wlan_cmd_802_11_get_stat(wlan_private * priv, | ||
353 | struct cmd_ds_command *cmd) | ||
354 | { | ||
355 | cmd->command = cpu_to_le16(cmd_802_11_get_stat); | ||
356 | cmd->size = | ||
357 | cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + | ||
358 | S_DS_GEN); | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int wlan_cmd_802_11_snmp_mib(wlan_private * priv, | ||
364 | struct cmd_ds_command *cmd, | ||
365 | int cmd_action, | ||
366 | int cmd_oid, void *pdata_buf) | ||
367 | { | ||
368 | struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib; | ||
369 | wlan_adapter *adapter = priv->adapter; | ||
370 | u8 ucTemp; | ||
371 | |||
372 | ENTER(); | ||
373 | |||
374 | lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); | ||
375 | |||
376 | cmd->command = cpu_to_le16(cmd_802_11_snmp_mib); | ||
377 | cmd->size = | ||
378 | cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) + | ||
379 | S_DS_GEN); | ||
380 | |||
381 | switch (cmd_oid) { | ||
382 | case OID_802_11_INFRASTRUCTURE_MODE: | ||
383 | { | ||
384 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode = | ||
385 | (enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf; | ||
386 | pSNMPMIB->querytype = cpu_to_le16(cmd_act_set); | ||
387 | pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i); | ||
388 | pSNMPMIB->bufsize = sizeof(u8); | ||
389 | if (mode == wlan802_11infrastructure) | ||
390 | ucTemp = SNMP_MIB_VALUE_INFRA; | ||
391 | else | ||
392 | ucTemp = SNMP_MIB_VALUE_ADHOC; | ||
393 | |||
394 | memmove(pSNMPMIB->value, &ucTemp, sizeof(u8)); | ||
395 | |||
396 | break; | ||
397 | } | ||
398 | |||
399 | case OID_802_11D_ENABLE: | ||
400 | { | ||
401 | u32 ulTemp; | ||
402 | |||
403 | pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i); | ||
404 | |||
405 | if (cmd_action == cmd_act_set) { | ||
406 | pSNMPMIB->querytype = cmd_act_set; | ||
407 | pSNMPMIB->bufsize = sizeof(u16); | ||
408 | ulTemp = *(u32 *)pdata_buf; | ||
409 | *((unsigned short *)(pSNMPMIB->value)) = | ||
410 | cpu_to_le16((u16) ulTemp); | ||
411 | } | ||
412 | break; | ||
413 | } | ||
414 | |||
415 | case OID_802_11_FRAGMENTATION_THRESHOLD: | ||
416 | { | ||
417 | u32 ulTemp; | ||
418 | |||
419 | pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i); | ||
420 | |||
421 | if (cmd_action == cmd_act_get) { | ||
422 | pSNMPMIB->querytype = | ||
423 | cpu_to_le16(cmd_act_get); | ||
424 | } else if (cmd_action == cmd_act_set) { | ||
425 | pSNMPMIB->querytype = | ||
426 | cpu_to_le16(cmd_act_set); | ||
427 | pSNMPMIB->bufsize = | ||
428 | cpu_to_le16(sizeof(u16)); | ||
429 | ulTemp = *((u32 *) pdata_buf); | ||
430 | *((unsigned short *)(pSNMPMIB->value)) = | ||
431 | cpu_to_le16((u16) ulTemp); | ||
432 | |||
433 | } | ||
434 | |||
435 | break; | ||
436 | } | ||
437 | |||
438 | case OID_802_11_RTS_THRESHOLD: | ||
439 | { | ||
440 | |||
441 | u32 ulTemp; | ||
442 | pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i); | ||
443 | |||
444 | if (cmd_action == cmd_act_get) { | ||
445 | pSNMPMIB->querytype = | ||
446 | cpu_to_le16(cmd_act_get); | ||
447 | } else if (cmd_action == cmd_act_set) { | ||
448 | pSNMPMIB->querytype = | ||
449 | cpu_to_le16(cmd_act_set); | ||
450 | pSNMPMIB->bufsize = | ||
451 | cpu_to_le16(sizeof(u16)); | ||
452 | ulTemp = *((u32 *) | ||
453 | pdata_buf); | ||
454 | *(unsigned short *)(pSNMPMIB->value) = | ||
455 | cpu_to_le16((u16) ulTemp); | ||
456 | |||
457 | } | ||
458 | break; | ||
459 | } | ||
460 | case OID_802_11_TX_RETRYCOUNT: | ||
461 | pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i); | ||
462 | |||
463 | if (cmd_action == cmd_act_get) { | ||
464 | pSNMPMIB->querytype = | ||
465 | cpu_to_le16(cmd_act_get); | ||
466 | } else if (cmd_action == cmd_act_set) { | ||
467 | pSNMPMIB->querytype = | ||
468 | cpu_to_le16(cmd_act_set); | ||
469 | pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16)); | ||
470 | *((unsigned short *)(pSNMPMIB->value)) = | ||
471 | cpu_to_le16((u16) adapter->txretrycount); | ||
472 | } | ||
473 | |||
474 | break; | ||
475 | default: | ||
476 | break; | ||
477 | } | ||
478 | |||
479 | lbs_pr_debug(1, | ||
480 | "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n", | ||
481 | cmd->command, cmd->size, cmd->seqnum, cmd->result); | ||
482 | |||
483 | lbs_pr_debug(1, | ||
484 | "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n", | ||
485 | pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize, | ||
486 | *(u16 *) pSNMPMIB->value); | ||
487 | |||
488 | LEAVE(); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int wlan_cmd_802_11_radio_control(wlan_private * priv, | ||
493 | struct cmd_ds_command *cmd, | ||
494 | int cmd_action) | ||
495 | { | ||
496 | wlan_adapter *adapter = priv->adapter; | ||
497 | struct cmd_ds_802_11_radio_control *pradiocontrol = | ||
498 | &cmd->params.radio; | ||
499 | |||
500 | ENTER(); | ||
501 | |||
502 | cmd->size = | ||
503 | cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) + | ||
504 | S_DS_GEN); | ||
505 | cmd->command = cpu_to_le16(cmd_802_11_radio_control); | ||
506 | |||
507 | pradiocontrol->action = cpu_to_le16(cmd_action); | ||
508 | |||
509 | switch (adapter->preamble) { | ||
510 | case cmd_type_short_preamble: | ||
511 | pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE); | ||
512 | break; | ||
513 | |||
514 | case cmd_type_long_preamble: | ||
515 | pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE); | ||
516 | break; | ||
517 | |||
518 | case cmd_type_auto_preamble: | ||
519 | default: | ||
520 | pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE); | ||
521 | break; | ||
522 | } | ||
523 | |||
524 | if (adapter->radioon) | ||
525 | pradiocontrol->control |= cpu_to_le16(TURN_ON_RF); | ||
526 | else | ||
527 | pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF); | ||
528 | |||
529 | LEAVE(); | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv, | ||
534 | struct cmd_ds_command *cmd, | ||
535 | u16 cmd_action, void *pdata_buf) | ||
536 | { | ||
537 | |||
538 | struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp; | ||
539 | |||
540 | ENTER(); | ||
541 | |||
542 | cmd->size = | ||
543 | cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + | ||
544 | S_DS_GEN); | ||
545 | cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power); | ||
546 | prtp->action = cmd_action; | ||
547 | |||
548 | lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size, | ||
549 | cmd->command, prtp->action); | ||
550 | |||
551 | switch (cmd_action) { | ||
552 | case cmd_act_tx_power_opt_get: | ||
553 | prtp->action = cpu_to_le16(cmd_act_get); | ||
554 | prtp->currentlevel = 0; | ||
555 | break; | ||
556 | |||
557 | case cmd_act_tx_power_opt_set_high: | ||
558 | prtp->action = cpu_to_le16(cmd_act_set); | ||
559 | prtp->currentlevel = | ||
560 | cpu_to_le16(cmd_act_tx_power_index_high); | ||
561 | break; | ||
562 | |||
563 | case cmd_act_tx_power_opt_set_mid: | ||
564 | prtp->action = cpu_to_le16(cmd_act_set); | ||
565 | prtp->currentlevel = | ||
566 | cpu_to_le16(cmd_act_tx_power_index_mid); | ||
567 | break; | ||
568 | |||
569 | case cmd_act_tx_power_opt_set_low: | ||
570 | prtp->action = cpu_to_le16(cmd_act_set); | ||
571 | prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf)); | ||
572 | break; | ||
573 | } | ||
574 | LEAVE(); | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static int wlan_cmd_802_11_rf_antenna(wlan_private * priv, | ||
579 | struct cmd_ds_command *cmd, | ||
580 | u16 cmd_action, void *pdata_buf) | ||
581 | { | ||
582 | struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant; | ||
583 | |||
584 | cmd->command = cpu_to_le16(cmd_802_11_rf_antenna); | ||
585 | cmd->size = | ||
586 | cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) + | ||
587 | S_DS_GEN); | ||
588 | |||
589 | rant->action = cpu_to_le16(cmd_action); | ||
590 | if ((cmd_action == cmd_act_set_rx) || | ||
591 | (cmd_action == cmd_act_set_tx)) { | ||
592 | rant->antennamode = | ||
593 | cpu_to_le16((u16) (*(u32 *) pdata_buf)); | ||
594 | } | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv, | ||
600 | struct cmd_ds_command *cmd, | ||
601 | u16 cmd_action) | ||
602 | { | ||
603 | struct cmd_ds_802_11_rate_adapt_rateset | ||
604 | *rateadapt = &cmd->params.rateset; | ||
605 | wlan_adapter *adapter = priv->adapter; | ||
606 | |||
607 | cmd->size = | ||
608 | cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset) | ||
609 | + S_DS_GEN); | ||
610 | cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset); | ||
611 | |||
612 | ENTER(); | ||
613 | |||
614 | rateadapt->action = cmd_action; | ||
615 | rateadapt->enablehwauto = adapter->enablehwauto; | ||
616 | rateadapt->bitmap = adapter->ratebitmap; | ||
617 | |||
618 | LEAVE(); | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int wlan_cmd_802_11_data_rate(wlan_private * priv, | ||
623 | struct cmd_ds_command *cmd, | ||
624 | u16 cmd_action) | ||
625 | { | ||
626 | struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate; | ||
627 | wlan_adapter *adapter = priv->adapter; | ||
628 | u16 action = cmd_action; | ||
629 | |||
630 | ENTER(); | ||
631 | |||
632 | cmd->size = | ||
633 | cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) + | ||
634 | S_DS_GEN); | ||
635 | |||
636 | cmd->command = cpu_to_le16(cmd_802_11_data_rate); | ||
637 | |||
638 | memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate)); | ||
639 | |||
640 | pdatarate->action = cpu_to_le16(cmd_action); | ||
641 | |||
642 | if (action == cmd_act_set_tx_fix_rate) { | ||
643 | pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate); | ||
644 | lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n", | ||
645 | adapter->datarate); | ||
646 | } else if (action == cmd_act_set_tx_auto) { | ||
647 | lbs_pr_debug(1, "Setting FW for AUTO rate\n"); | ||
648 | } | ||
649 | |||
650 | LEAVE(); | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static int wlan_cmd_mac_multicast_adr(wlan_private * priv, | ||
655 | struct cmd_ds_command *cmd, | ||
656 | u16 cmd_action) | ||
657 | { | ||
658 | struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr; | ||
659 | wlan_adapter *adapter = priv->adapter; | ||
660 | |||
661 | cmd->size = | ||
662 | cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) + | ||
663 | S_DS_GEN); | ||
664 | cmd->command = cpu_to_le16(cmd_mac_multicast_adr); | ||
665 | |||
666 | pMCastAdr->action = cpu_to_le16(cmd_action); | ||
667 | pMCastAdr->nr_of_adrs = | ||
668 | cpu_to_le16((u16) adapter->nr_of_multicastmacaddr); | ||
669 | memcpy(pMCastAdr->maclist, adapter->multicastlist, | ||
670 | adapter->nr_of_multicastmacaddr * ETH_ALEN); | ||
671 | |||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | static int wlan_cmd_802_11_rf_channel(wlan_private * priv, | ||
676 | struct cmd_ds_command *cmd, | ||
677 | int option, void *pdata_buf) | ||
678 | { | ||
679 | struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel; | ||
680 | |||
681 | cmd->command = cpu_to_le16(cmd_802_11_rf_channel); | ||
682 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) | ||
683 | + S_DS_GEN); | ||
684 | |||
685 | if (option == cmd_opt_802_11_rf_channel_set) { | ||
686 | rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf)); | ||
687 | } | ||
688 | |||
689 | rfchan->action = cpu_to_le16(option); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static int wlan_cmd_802_11_rssi(wlan_private * priv, | ||
695 | struct cmd_ds_command *cmd) | ||
696 | { | ||
697 | wlan_adapter *adapter = priv->adapter; | ||
698 | |||
699 | cmd->command = cpu_to_le16(cmd_802_11_rssi); | ||
700 | cmd->size = | ||
701 | cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); | ||
702 | cmd->params.rssi.N = priv->adapter->bcn_avg_factor; | ||
703 | |||
704 | /* reset Beacon SNR/NF/RSSI values */ | ||
705 | adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
706 | adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0; | ||
707 | adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
708 | adapter->NF[TYPE_BEACON][TYPE_AVG] = 0; | ||
709 | adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
710 | adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0; | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static int wlan_cmd_reg_access(wlan_private * priv, | ||
716 | struct cmd_ds_command *cmdptr, | ||
717 | u8 cmd_action, void *pdata_buf) | ||
718 | { | ||
719 | struct wlan_offset_value *offval; | ||
720 | |||
721 | ENTER(); | ||
722 | |||
723 | offval = (struct wlan_offset_value *)pdata_buf; | ||
724 | |||
725 | switch (cmdptr->command) { | ||
726 | case cmd_mac_reg_access: | ||
727 | { | ||
728 | struct cmd_ds_mac_reg_access *macreg; | ||
729 | |||
730 | cmdptr->size = | ||
731 | cpu_to_le16(sizeof | ||
732 | (struct cmd_ds_mac_reg_access) | ||
733 | + S_DS_GEN); | ||
734 | macreg = | ||
735 | (struct cmd_ds_mac_reg_access *)&cmdptr->params. | ||
736 | macreg; | ||
737 | |||
738 | macreg->action = cpu_to_le16(cmd_action); | ||
739 | macreg->offset = cpu_to_le16((u16) offval->offset); | ||
740 | macreg->value = cpu_to_le32(offval->value); | ||
741 | |||
742 | break; | ||
743 | } | ||
744 | |||
745 | case cmd_bbp_reg_access: | ||
746 | { | ||
747 | struct cmd_ds_bbp_reg_access *bbpreg; | ||
748 | |||
749 | cmdptr->size = | ||
750 | cpu_to_le16(sizeof | ||
751 | (struct cmd_ds_bbp_reg_access) | ||
752 | + S_DS_GEN); | ||
753 | bbpreg = | ||
754 | (struct cmd_ds_bbp_reg_access *)&cmdptr->params. | ||
755 | bbpreg; | ||
756 | |||
757 | bbpreg->action = cpu_to_le16(cmd_action); | ||
758 | bbpreg->offset = cpu_to_le16((u16) offval->offset); | ||
759 | bbpreg->value = (u8) offval->value; | ||
760 | |||
761 | break; | ||
762 | } | ||
763 | |||
764 | case cmd_rf_reg_access: | ||
765 | { | ||
766 | struct cmd_ds_rf_reg_access *rfreg; | ||
767 | |||
768 | cmdptr->size = | ||
769 | cpu_to_le16(sizeof | ||
770 | (struct cmd_ds_rf_reg_access) + | ||
771 | S_DS_GEN); | ||
772 | rfreg = | ||
773 | (struct cmd_ds_rf_reg_access *)&cmdptr->params. | ||
774 | rfreg; | ||
775 | |||
776 | rfreg->action = cpu_to_le16(cmd_action); | ||
777 | rfreg->offset = cpu_to_le16((u16) offval->offset); | ||
778 | rfreg->value = (u8) offval->value; | ||
779 | |||
780 | break; | ||
781 | } | ||
782 | |||
783 | default: | ||
784 | break; | ||
785 | } | ||
786 | |||
787 | LEAVE(); | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | static int wlan_cmd_802_11_mac_address(wlan_private * priv, | ||
792 | struct cmd_ds_command *cmd, | ||
793 | u16 cmd_action) | ||
794 | { | ||
795 | wlan_adapter *adapter = priv->adapter; | ||
796 | |||
797 | cmd->command = cpu_to_le16(cmd_802_11_mac_address); | ||
798 | cmd->size = | ||
799 | cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) + | ||
800 | S_DS_GEN); | ||
801 | cmd->result = 0; | ||
802 | |||
803 | cmd->params.macadd.action = cpu_to_le16(cmd_action); | ||
804 | |||
805 | if (cmd_action == cmd_act_set) { | ||
806 | memcpy(cmd->params.macadd.macadd, | ||
807 | adapter->current_addr, ETH_ALEN); | ||
808 | lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6); | ||
809 | } | ||
810 | |||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int wlan_cmd_802_11_eeprom_access(wlan_private * priv, | ||
815 | struct cmd_ds_command *cmd, | ||
816 | int cmd_action, void *pdata_buf) | ||
817 | { | ||
818 | struct wlan_ioctl_regrdwr *ea = pdata_buf; | ||
819 | |||
820 | ENTER(); | ||
821 | |||
822 | cmd->command = cpu_to_le16(cmd_802_11_eeprom_access); | ||
823 | cmd->size = | ||
824 | cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) + | ||
825 | S_DS_GEN); | ||
826 | cmd->result = 0; | ||
827 | |||
828 | cmd->params.rdeeprom.action = cpu_to_le16(ea->action); | ||
829 | cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset); | ||
830 | cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB); | ||
831 | cmd->params.rdeeprom.value = 0; | ||
832 | |||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | static int wlan_cmd_bt_access(wlan_private * priv, | ||
837 | struct cmd_ds_command *cmd, | ||
838 | u16 cmd_action, void *pdata_buf) | ||
839 | { | ||
840 | struct cmd_ds_bt_access *bt_access = &cmd->params.bt; | ||
841 | lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action); | ||
842 | |||
843 | cmd->command = cpu_to_le16(cmd_bt_access); | ||
844 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) | ||
845 | + S_DS_GEN); | ||
846 | cmd->result = 0; | ||
847 | bt_access->action = cpu_to_le16(cmd_action); | ||
848 | |||
849 | switch (cmd_action) { | ||
850 | case cmd_act_bt_access_add: | ||
851 | memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); | ||
852 | lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6); | ||
853 | break; | ||
854 | case cmd_act_bt_access_del: | ||
855 | memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); | ||
856 | lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6); | ||
857 | break; | ||
858 | case cmd_act_bt_access_list: | ||
859 | bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); | ||
860 | break; | ||
861 | case cmd_act_bt_access_reset: | ||
862 | break; | ||
863 | default: | ||
864 | break; | ||
865 | } | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static int wlan_cmd_fwt_access(wlan_private * priv, | ||
870 | struct cmd_ds_command *cmd, | ||
871 | u16 cmd_action, void *pdata_buf) | ||
872 | { | ||
873 | struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; | ||
874 | lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); | ||
875 | |||
876 | cmd->command = cpu_to_le16(cmd_fwt_access); | ||
877 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) | ||
878 | + S_DS_GEN); | ||
879 | cmd->result = 0; | ||
880 | |||
881 | if (pdata_buf) | ||
882 | memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); | ||
883 | else | ||
884 | memset(fwt_access, 0, sizeof(*fwt_access)); | ||
885 | |||
886 | fwt_access->action = cpu_to_le16(cmd_action); | ||
887 | |||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | static int wlan_cmd_mesh_access(wlan_private * priv, | ||
892 | struct cmd_ds_command *cmd, | ||
893 | u16 cmd_action, void *pdata_buf) | ||
894 | { | ||
895 | struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh; | ||
896 | lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); | ||
897 | |||
898 | cmd->command = cpu_to_le16(cmd_mesh_access); | ||
899 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) | ||
900 | + S_DS_GEN); | ||
901 | cmd->result = 0; | ||
902 | |||
903 | if (pdata_buf) | ||
904 | memcpy(mesh_access, pdata_buf, sizeof(*mesh_access)); | ||
905 | else | ||
906 | memset(mesh_access, 0, sizeof(*mesh_access)); | ||
907 | |||
908 | mesh_access->action = cpu_to_le16(cmd_action); | ||
909 | |||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail) | ||
914 | { | ||
915 | unsigned long flags; | ||
916 | struct cmd_ds_command *cmdptr; | ||
917 | |||
918 | ENTER(); | ||
919 | |||
920 | if (!cmdnode) { | ||
921 | lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n"); | ||
922 | goto done; | ||
923 | } | ||
924 | |||
925 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
926 | if (!cmdptr) { | ||
927 | lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n"); | ||
928 | goto done; | ||
929 | } | ||
930 | |||
931 | /* Exit_PS command needs to be queued in the header always. */ | ||
932 | if (cmdptr->command == cmd_802_11_ps_mode) { | ||
933 | struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode; | ||
934 | if (psm->action == cmd_subcmd_exit_ps) { | ||
935 | if (adapter->psstate != PS_STATE_FULL_POWER) | ||
936 | addtail = 0; | ||
937 | } | ||
938 | } | ||
939 | |||
940 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
941 | |||
942 | if (addtail) | ||
943 | list_add_tail((struct list_head *)cmdnode, | ||
944 | &adapter->cmdpendingq); | ||
945 | else | ||
946 | list_add((struct list_head *)cmdnode, &adapter->cmdpendingq); | ||
947 | |||
948 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
949 | |||
950 | lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n", | ||
951 | (u32) cmdnode, | ||
952 | ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command); | ||
953 | |||
954 | done: | ||
955 | LEAVE(); | ||
956 | return; | ||
957 | } | ||
958 | |||
959 | /* | ||
960 | * TODO: Fix the issue when DownloadcommandToStation is being called the | ||
961 | * second time when the command timesout. All the cmdptr->xxx are in little | ||
962 | * endian and therefore all the comparissions will fail. | ||
963 | * For now - we are not performing the endian conversion the second time - but | ||
964 | * for PS and DEEP_SLEEP we need to worry | ||
965 | */ | ||
966 | static int DownloadcommandToStation(wlan_private * priv, | ||
967 | struct cmd_ctrl_node *cmdnode) | ||
968 | { | ||
969 | unsigned long flags; | ||
970 | struct cmd_ds_command *cmdptr; | ||
971 | wlan_adapter *adapter = priv->adapter; | ||
972 | int ret = 0; | ||
973 | u16 cmdsize; | ||
974 | u16 command; | ||
975 | |||
976 | ENTER(); | ||
977 | |||
978 | if (!adapter || !cmdnode) { | ||
979 | lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n", | ||
980 | (int)adapter, (int)cmdnode); | ||
981 | if (cmdnode) { | ||
982 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
983 | __libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
984 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
985 | } | ||
986 | ret = -1; | ||
987 | goto done; | ||
988 | } | ||
989 | |||
990 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
991 | |||
992 | |||
993 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
994 | if (!cmdptr || !cmdptr->size) { | ||
995 | lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, " | ||
996 | "Not sending\n"); | ||
997 | __libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
998 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
999 | ret = -1; | ||
1000 | goto done; | ||
1001 | } | ||
1002 | |||
1003 | adapter->cur_cmd = cmdnode; | ||
1004 | adapter->cur_cmd_retcode = 0; | ||
1005 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1006 | lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n", | ||
1007 | cmdptr->size); | ||
1008 | |||
1009 | cmdsize = cmdptr->size; | ||
1010 | |||
1011 | command = cpu_to_le16(cmdptr->command); | ||
1012 | |||
1013 | cmdnode->cmdwaitqwoken = 0; | ||
1014 | cmdsize = cpu_to_le16(cmdsize); | ||
1015 | |||
1016 | ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize); | ||
1017 | |||
1018 | if (ret != 0) { | ||
1019 | lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n"); | ||
1020 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1021 | __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); | ||
1022 | adapter->cur_cmd = NULL; | ||
1023 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1024 | ret = -1; | ||
1025 | goto done; | ||
1026 | } | ||
1027 | |||
1028 | lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies); | ||
1029 | lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize); | ||
1030 | |||
1031 | /* Setup the timer after transmit command */ | ||
1032 | if (command == cmd_802_11_scan | ||
1033 | || command == cmd_802_11_authenticate | ||
1034 | || command == cmd_802_11_associate) | ||
1035 | mod_timer(&adapter->command_timer, jiffies + (10*HZ)); | ||
1036 | else | ||
1037 | mod_timer(&adapter->command_timer, jiffies + (5*HZ)); | ||
1038 | |||
1039 | ret = 0; | ||
1040 | |||
1041 | done: | ||
1042 | LEAVE(); | ||
1043 | return ret; | ||
1044 | } | ||
1045 | |||
1046 | static int wlan_cmd_mac_control(wlan_private * priv, | ||
1047 | struct cmd_ds_command *cmd) | ||
1048 | { | ||
1049 | struct cmd_ds_mac_control *mac = &cmd->params.macctrl; | ||
1050 | |||
1051 | ENTER(); | ||
1052 | |||
1053 | cmd->command = cpu_to_le16(cmd_mac_control); | ||
1054 | cmd->size = | ||
1055 | cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN); | ||
1056 | mac->action = cpu_to_le16(priv->adapter->currentpacketfilter); | ||
1057 | |||
1058 | lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n", | ||
1059 | mac->action, cmd->size); | ||
1060 | |||
1061 | LEAVE(); | ||
1062 | return 0; | ||
1063 | } | ||
1064 | |||
1065 | /** | ||
1066 | * This function inserts command node to cmdfreeq | ||
1067 | * after cleans it. Requires adapter->driver_lock held. | ||
1068 | */ | ||
1069 | void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) | ||
1070 | { | ||
1071 | wlan_adapter *adapter = priv->adapter; | ||
1072 | |||
1073 | if (!ptempcmd) | ||
1074 | goto done; | ||
1075 | |||
1076 | cleanup_cmdnode(ptempcmd); | ||
1077 | list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq); | ||
1078 | done: | ||
1079 | return; | ||
1080 | } | ||
1081 | |||
1082 | void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) | ||
1083 | { | ||
1084 | unsigned long flags; | ||
1085 | |||
1086 | spin_lock_irqsave(&priv->adapter->driver_lock, flags); | ||
1087 | __libertas_cleanup_and_insert_cmd(priv, ptempcmd); | ||
1088 | spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); | ||
1089 | } | ||
1090 | |||
1091 | int libertas_set_radio_control(wlan_private * priv) | ||
1092 | { | ||
1093 | int ret = 0; | ||
1094 | |||
1095 | ENTER(); | ||
1096 | |||
1097 | ret = libertas_prepare_and_send_command(priv, | ||
1098 | cmd_802_11_radio_control, | ||
1099 | cmd_act_set, | ||
1100 | cmd_option_waitforrsp, 0, NULL); | ||
1101 | |||
1102 | lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n", | ||
1103 | priv->adapter->radioon, priv->adapter->preamble); | ||
1104 | |||
1105 | LEAVE(); | ||
1106 | return ret; | ||
1107 | } | ||
1108 | |||
1109 | int libertas_set_mac_packet_filter(wlan_private * priv) | ||
1110 | { | ||
1111 | int ret = 0; | ||
1112 | |||
1113 | ENTER(); | ||
1114 | |||
1115 | lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n", | ||
1116 | priv->adapter->currentpacketfilter); | ||
1117 | |||
1118 | /* Send MAC control command to station */ | ||
1119 | ret = libertas_prepare_and_send_command(priv, | ||
1120 | cmd_mac_control, 0, 0, 0, NULL); | ||
1121 | |||
1122 | LEAVE(); | ||
1123 | return ret; | ||
1124 | } | ||
1125 | |||
1126 | /** | ||
1127 | * @brief This function prepare the command before send to firmware. | ||
1128 | * | ||
1129 | * @param priv A pointer to wlan_private structure | ||
1130 | * @param cmd_no command number | ||
1131 | * @param cmd_action command action: GET or SET | ||
1132 | * @param wait_option wait option: wait response or not | ||
1133 | * @param cmd_oid cmd oid: treated as sub command | ||
1134 | * @param pdata_buf A pointer to informaion buffer | ||
1135 | * @return 0 or -1 | ||
1136 | */ | ||
1137 | int libertas_prepare_and_send_command(wlan_private * priv, | ||
1138 | u16 cmd_no, | ||
1139 | u16 cmd_action, | ||
1140 | u16 wait_option, u32 cmd_oid, void *pdata_buf) | ||
1141 | { | ||
1142 | int ret = 0; | ||
1143 | wlan_adapter *adapter = priv->adapter; | ||
1144 | struct cmd_ctrl_node *cmdnode; | ||
1145 | struct cmd_ds_command *cmdptr; | ||
1146 | unsigned long flags; | ||
1147 | |||
1148 | ENTER(); | ||
1149 | |||
1150 | if (!adapter) { | ||
1151 | lbs_pr_debug(1, "PREP_CMD: adapter is Null\n"); | ||
1152 | ret = -1; | ||
1153 | goto done; | ||
1154 | } | ||
1155 | |||
1156 | if (adapter->surpriseremoved) { | ||
1157 | lbs_pr_debug(1, "PREP_CMD: Card is Removed\n"); | ||
1158 | ret = -1; | ||
1159 | goto done; | ||
1160 | } | ||
1161 | |||
1162 | cmdnode = libertas_get_free_cmd_ctrl_node(priv); | ||
1163 | |||
1164 | if (cmdnode == NULL) { | ||
1165 | lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n"); | ||
1166 | |||
1167 | /* Wake up main thread to execute next command */ | ||
1168 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1169 | ret = -1; | ||
1170 | goto done; | ||
1171 | } | ||
1172 | |||
1173 | libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf); | ||
1174 | |||
1175 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
1176 | |||
1177 | lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n", | ||
1178 | (u32) cmdptr, cmd_no); | ||
1179 | |||
1180 | if (!cmdptr) { | ||
1181 | lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n"); | ||
1182 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1183 | ret = -1; | ||
1184 | goto done; | ||
1185 | } | ||
1186 | |||
1187 | /* Set sequence number, command and INT option */ | ||
1188 | adapter->seqnum++; | ||
1189 | cmdptr->seqnum = cpu_to_le16(adapter->seqnum); | ||
1190 | |||
1191 | cmdptr->command = cmd_no; | ||
1192 | cmdptr->result = 0; | ||
1193 | |||
1194 | switch (cmd_no) { | ||
1195 | case cmd_get_hw_spec: | ||
1196 | ret = wlan_cmd_hw_spec(priv, cmdptr); | ||
1197 | break; | ||
1198 | case cmd_802_11_ps_mode: | ||
1199 | ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action); | ||
1200 | break; | ||
1201 | |||
1202 | case cmd_802_11_scan: | ||
1203 | ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf); | ||
1204 | break; | ||
1205 | |||
1206 | case cmd_mac_control: | ||
1207 | ret = wlan_cmd_mac_control(priv, cmdptr); | ||
1208 | break; | ||
1209 | |||
1210 | case cmd_802_11_associate: | ||
1211 | case cmd_802_11_reassociate: | ||
1212 | ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf); | ||
1213 | break; | ||
1214 | |||
1215 | case cmd_802_11_deauthenticate: | ||
1216 | ret = libertas_cmd_80211_deauthenticate(priv, cmdptr); | ||
1217 | break; | ||
1218 | |||
1219 | case cmd_802_11_set_wep: | ||
1220 | ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf); | ||
1221 | break; | ||
1222 | |||
1223 | case cmd_802_11_ad_hoc_start: | ||
1224 | ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf); | ||
1225 | break; | ||
1226 | case cmd_code_dnld: | ||
1227 | break; | ||
1228 | |||
1229 | case cmd_802_11_reset: | ||
1230 | ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action); | ||
1231 | break; | ||
1232 | |||
1233 | case cmd_802_11_get_log: | ||
1234 | ret = wlan_cmd_802_11_get_log(priv, cmdptr); | ||
1235 | break; | ||
1236 | |||
1237 | case cmd_802_11_authenticate: | ||
1238 | ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf); | ||
1239 | break; | ||
1240 | |||
1241 | case cmd_802_11_get_stat: | ||
1242 | ret = wlan_cmd_802_11_get_stat(priv, cmdptr); | ||
1243 | break; | ||
1244 | |||
1245 | case cmd_802_11_snmp_mib: | ||
1246 | ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr, | ||
1247 | cmd_action, cmd_oid, pdata_buf); | ||
1248 | break; | ||
1249 | |||
1250 | case cmd_mac_reg_access: | ||
1251 | case cmd_bbp_reg_access: | ||
1252 | case cmd_rf_reg_access: | ||
1253 | ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1254 | break; | ||
1255 | |||
1256 | case cmd_802_11_rf_channel: | ||
1257 | ret = wlan_cmd_802_11_rf_channel(priv, cmdptr, | ||
1258 | cmd_action, pdata_buf); | ||
1259 | break; | ||
1260 | |||
1261 | case cmd_802_11_rf_tx_power: | ||
1262 | ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr, | ||
1263 | cmd_action, pdata_buf); | ||
1264 | break; | ||
1265 | |||
1266 | case cmd_802_11_radio_control: | ||
1267 | ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action); | ||
1268 | break; | ||
1269 | |||
1270 | case cmd_802_11_rf_antenna: | ||
1271 | ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr, | ||
1272 | cmd_action, pdata_buf); | ||
1273 | break; | ||
1274 | |||
1275 | case cmd_802_11_data_rate: | ||
1276 | ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action); | ||
1277 | break; | ||
1278 | case cmd_802_11_rate_adapt_rateset: | ||
1279 | ret = wlan_cmd_802_11_rate_adapt_rateset(priv, | ||
1280 | cmdptr, cmd_action); | ||
1281 | break; | ||
1282 | |||
1283 | case cmd_mac_multicast_adr: | ||
1284 | ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action); | ||
1285 | break; | ||
1286 | |||
1287 | case cmd_802_11_ad_hoc_join: | ||
1288 | ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf); | ||
1289 | break; | ||
1290 | |||
1291 | case cmd_802_11_rssi: | ||
1292 | ret = wlan_cmd_802_11_rssi(priv, cmdptr); | ||
1293 | break; | ||
1294 | |||
1295 | case cmd_802_11_ad_hoc_stop: | ||
1296 | ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr); | ||
1297 | break; | ||
1298 | |||
1299 | case cmd_802_11_enable_rsn: | ||
1300 | ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action); | ||
1301 | break; | ||
1302 | |||
1303 | case cmd_802_11_key_material: | ||
1304 | ret = wlan_cmd_802_11_key_material(priv, cmdptr, | ||
1305 | cmd_action, cmd_oid, | ||
1306 | pdata_buf); | ||
1307 | break; | ||
1308 | |||
1309 | case cmd_802_11_pairwise_tsc: | ||
1310 | break; | ||
1311 | case cmd_802_11_group_tsc: | ||
1312 | break; | ||
1313 | |||
1314 | case cmd_802_11_mac_address: | ||
1315 | ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action); | ||
1316 | break; | ||
1317 | |||
1318 | case cmd_802_11_eeprom_access: | ||
1319 | ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr, | ||
1320 | cmd_action, pdata_buf); | ||
1321 | break; | ||
1322 | |||
1323 | case cmd_802_11_set_afc: | ||
1324 | case cmd_802_11_get_afc: | ||
1325 | |||
1326 | cmdptr->command = cpu_to_le16(cmd_no); | ||
1327 | cmdptr->size = | ||
1328 | cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + | ||
1329 | S_DS_GEN); | ||
1330 | |||
1331 | memmove(&cmdptr->params.afc, | ||
1332 | pdata_buf, sizeof(struct cmd_ds_802_11_afc)); | ||
1333 | |||
1334 | ret = 0; | ||
1335 | goto done; | ||
1336 | |||
1337 | case cmd_802_11d_domain_info: | ||
1338 | ret = libertas_cmd_802_11d_domain_info(priv, cmdptr, | ||
1339 | cmd_no, cmd_action); | ||
1340 | break; | ||
1341 | |||
1342 | case cmd_802_11_sleep_params: | ||
1343 | ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action); | ||
1344 | break; | ||
1345 | case cmd_802_11_inactivity_timeout: | ||
1346 | ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr, | ||
1347 | cmd_action, pdata_buf); | ||
1348 | libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf); | ||
1349 | break; | ||
1350 | |||
1351 | case cmd_802_11_tpc_cfg: | ||
1352 | cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg); | ||
1353 | cmdptr->size = | ||
1354 | cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + | ||
1355 | S_DS_GEN); | ||
1356 | |||
1357 | memmove(&cmdptr->params.tpccfg, | ||
1358 | pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); | ||
1359 | |||
1360 | ret = 0; | ||
1361 | break; | ||
1362 | case cmd_802_11_led_gpio_ctrl: | ||
1363 | { | ||
1364 | struct mrvlietypes_ledgpio *gpio = | ||
1365 | (struct mrvlietypes_ledgpio*) | ||
1366 | cmdptr->params.ledgpio.data; | ||
1367 | |||
1368 | memmove(&cmdptr->params.ledgpio, | ||
1369 | pdata_buf, | ||
1370 | sizeof(struct cmd_ds_802_11_led_ctrl)); | ||
1371 | |||
1372 | cmdptr->command = | ||
1373 | cpu_to_le16(cmd_802_11_led_gpio_ctrl); | ||
1374 | |||
1375 | #define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 | ||
1376 | cmdptr->size = | ||
1377 | cpu_to_le16(gpio->header.len + S_DS_GEN + | ||
1378 | ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); | ||
1379 | gpio->header.len = cpu_to_le16(gpio->header.len); | ||
1380 | |||
1381 | ret = 0; | ||
1382 | break; | ||
1383 | } | ||
1384 | case cmd_802_11_pwr_cfg: | ||
1385 | cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg); | ||
1386 | cmdptr->size = | ||
1387 | cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) + | ||
1388 | S_DS_GEN); | ||
1389 | memmove(&cmdptr->params.pwrcfg, pdata_buf, | ||
1390 | sizeof(struct cmd_ds_802_11_pwr_cfg)); | ||
1391 | |||
1392 | ret = 0; | ||
1393 | break; | ||
1394 | case cmd_bt_access: | ||
1395 | ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1396 | break; | ||
1397 | |||
1398 | case cmd_fwt_access: | ||
1399 | ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1400 | break; | ||
1401 | |||
1402 | case cmd_mesh_access: | ||
1403 | ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1404 | break; | ||
1405 | |||
1406 | case cmd_get_tsf: | ||
1407 | cmdptr->command = cpu_to_le16(cmd_get_tsf); | ||
1408 | cmdptr->size = | ||
1409 | cpu_to_le16(sizeof(struct cmd_ds_get_tsf) | ||
1410 | + S_DS_GEN); | ||
1411 | ret = 0; | ||
1412 | break; | ||
1413 | case cmd_802_11_tx_rate_query: | ||
1414 | cmdptr->command = | ||
1415 | cpu_to_le16(cmd_802_11_tx_rate_query); | ||
1416 | cmdptr->size = | ||
1417 | cpu_to_le16(sizeof(struct cmd_tx_rate_query) + | ||
1418 | S_DS_GEN); | ||
1419 | adapter->txrate = 0; | ||
1420 | ret = 0; | ||
1421 | break; | ||
1422 | default: | ||
1423 | lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no); | ||
1424 | ret = -1; | ||
1425 | break; | ||
1426 | } | ||
1427 | |||
1428 | /* return error, since the command preparation failed */ | ||
1429 | if (ret != 0) { | ||
1430 | lbs_pr_debug(1, "PREP_CMD: command preparation failed\n"); | ||
1431 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1432 | ret = -1; | ||
1433 | goto done; | ||
1434 | } | ||
1435 | |||
1436 | cmdnode->cmdwaitqwoken = 0; | ||
1437 | |||
1438 | libertas_queue_cmd(adapter, cmdnode, 1); | ||
1439 | adapter->nr_cmd_pending++; | ||
1440 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1441 | |||
1442 | if (wait_option & cmd_option_waitforrsp) { | ||
1443 | lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n"); | ||
1444 | might_sleep(); | ||
1445 | wait_event_interruptible(cmdnode->cmdwait_q, | ||
1446 | cmdnode->cmdwaitqwoken); | ||
1447 | } | ||
1448 | |||
1449 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1450 | if (adapter->cur_cmd_retcode) { | ||
1451 | lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n", | ||
1452 | adapter->cur_cmd_retcode); | ||
1453 | adapter->cur_cmd_retcode = 0; | ||
1454 | ret = -1; | ||
1455 | } | ||
1456 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1457 | |||
1458 | done: | ||
1459 | LEAVE(); | ||
1460 | return ret; | ||
1461 | } | ||
1462 | |||
1463 | /** | ||
1464 | * @brief This function allocates the command buffer and link | ||
1465 | * it to command free queue. | ||
1466 | * | ||
1467 | * @param priv A pointer to wlan_private structure | ||
1468 | * @return 0 or -1 | ||
1469 | */ | ||
1470 | int libertas_allocate_cmd_buffer(wlan_private * priv) | ||
1471 | { | ||
1472 | int ret = 0; | ||
1473 | u32 ulbufsize; | ||
1474 | u32 i; | ||
1475 | struct cmd_ctrl_node *tempcmd_array; | ||
1476 | u8 *ptempvirtualaddr; | ||
1477 | wlan_adapter *adapter = priv->adapter; | ||
1478 | |||
1479 | ENTER(); | ||
1480 | |||
1481 | /* Allocate and initialize cmdCtrlNode */ | ||
1482 | ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; | ||
1483 | |||
1484 | if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) { | ||
1485 | lbs_pr_debug(1, | ||
1486 | "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n"); | ||
1487 | ret = -1; | ||
1488 | goto done; | ||
1489 | } | ||
1490 | |||
1491 | adapter->cmd_array = tempcmd_array; | ||
1492 | memset(adapter->cmd_array, 0, ulbufsize); | ||
1493 | |||
1494 | /* Allocate and initialize command buffers */ | ||
1495 | ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; | ||
1496 | for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { | ||
1497 | if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) { | ||
1498 | lbs_pr_debug(1, | ||
1499 | "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n"); | ||
1500 | ret = -1; | ||
1501 | goto done; | ||
1502 | } | ||
1503 | |||
1504 | memset(ptempvirtualaddr, 0, ulbufsize); | ||
1505 | |||
1506 | /* Update command buffer virtual */ | ||
1507 | tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr; | ||
1508 | } | ||
1509 | |||
1510 | for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { | ||
1511 | init_waitqueue_head(&tempcmd_array[i].cmdwait_q); | ||
1512 | libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]); | ||
1513 | } | ||
1514 | |||
1515 | ret = 0; | ||
1516 | done: | ||
1517 | LEAVE(); | ||
1518 | return ret; | ||
1519 | } | ||
1520 | |||
1521 | /** | ||
1522 | * @brief This function frees the command buffer. | ||
1523 | * | ||
1524 | * @param priv A pointer to wlan_private structure | ||
1525 | * @return 0 or -1 | ||
1526 | */ | ||
1527 | int libertas_free_cmd_buffer(wlan_private * priv) | ||
1528 | { | ||
1529 | u32 ulbufsize; | ||
1530 | unsigned int i; | ||
1531 | struct cmd_ctrl_node *tempcmd_array; | ||
1532 | wlan_adapter *adapter = priv->adapter; | ||
1533 | |||
1534 | ENTER(); | ||
1535 | |||
1536 | /* need to check if cmd array is allocated or not */ | ||
1537 | if (adapter->cmd_array == NULL) { | ||
1538 | lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n"); | ||
1539 | goto done; | ||
1540 | } | ||
1541 | |||
1542 | tempcmd_array = adapter->cmd_array; | ||
1543 | |||
1544 | /* Release shared memory buffers */ | ||
1545 | ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; | ||
1546 | for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { | ||
1547 | if (tempcmd_array[i].bufvirtualaddr) { | ||
1548 | lbs_pr_debug(1, "Free all the array\n"); | ||
1549 | kfree(tempcmd_array[i].bufvirtualaddr); | ||
1550 | tempcmd_array[i].bufvirtualaddr = NULL; | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1554 | /* Release cmd_ctrl_node */ | ||
1555 | if (adapter->cmd_array) { | ||
1556 | lbs_pr_debug(1, "Free cmd_array\n"); | ||
1557 | kfree(adapter->cmd_array); | ||
1558 | adapter->cmd_array = NULL; | ||
1559 | } | ||
1560 | |||
1561 | done: | ||
1562 | LEAVE(); | ||
1563 | return 0; | ||
1564 | } | ||
1565 | |||
1566 | /** | ||
1567 | * @brief This function gets a free command node if available in | ||
1568 | * command free queue. | ||
1569 | * | ||
1570 | * @param priv A pointer to wlan_private structure | ||
1571 | * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL | ||
1572 | */ | ||
1573 | struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv) | ||
1574 | { | ||
1575 | struct cmd_ctrl_node *tempnode; | ||
1576 | wlan_adapter *adapter = priv->adapter; | ||
1577 | unsigned long flags; | ||
1578 | |||
1579 | if (!adapter) | ||
1580 | return NULL; | ||
1581 | |||
1582 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1583 | |||
1584 | if (!list_empty(&adapter->cmdfreeq)) { | ||
1585 | tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next; | ||
1586 | list_del((struct list_head *)tempnode); | ||
1587 | } else { | ||
1588 | lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); | ||
1589 | tempnode = NULL; | ||
1590 | } | ||
1591 | |||
1592 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1593 | |||
1594 | if (tempnode) { | ||
1595 | lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n"); | ||
1596 | lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n", | ||
1597 | tempnode); | ||
1598 | cleanup_cmdnode(tempnode); | ||
1599 | } | ||
1600 | |||
1601 | return tempnode; | ||
1602 | } | ||
1603 | |||
1604 | /** | ||
1605 | * @brief This function cleans command node. | ||
1606 | * | ||
1607 | * @param ptempnode A pointer to cmdCtrlNode structure | ||
1608 | * @return n/a | ||
1609 | */ | ||
1610 | static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode) | ||
1611 | { | ||
1612 | if (!ptempnode) | ||
1613 | return; | ||
1614 | ptempnode->cmdwaitqwoken = 1; | ||
1615 | wake_up_interruptible(&ptempnode->cmdwait_q); | ||
1616 | ptempnode->status = 0; | ||
1617 | ptempnode->cmd_oid = (u32) 0; | ||
1618 | ptempnode->wait_option = 0; | ||
1619 | ptempnode->pdata_buf = NULL; | ||
1620 | |||
1621 | if (ptempnode->bufvirtualaddr != NULL) | ||
1622 | memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); | ||
1623 | return; | ||
1624 | } | ||
1625 | |||
1626 | /** | ||
1627 | * @brief This function initializes the command node. | ||
1628 | * | ||
1629 | * @param priv A pointer to wlan_private structure | ||
1630 | * @param ptempnode A pointer to cmd_ctrl_node structure | ||
1631 | * @param cmd_oid cmd oid: treated as sub command | ||
1632 | * @param wait_option wait option: wait response or not | ||
1633 | * @param pdata_buf A pointer to informaion buffer | ||
1634 | * @return 0 or -1 | ||
1635 | */ | ||
1636 | void libertas_set_cmd_ctrl_node(wlan_private * priv, | ||
1637 | struct cmd_ctrl_node *ptempnode, | ||
1638 | u32 cmd_oid, u16 wait_option, void *pdata_buf) | ||
1639 | { | ||
1640 | ENTER(); | ||
1641 | |||
1642 | if (!ptempnode) | ||
1643 | return; | ||
1644 | |||
1645 | ptempnode->cmd_oid = cmd_oid; | ||
1646 | ptempnode->wait_option = wait_option; | ||
1647 | ptempnode->pdata_buf = pdata_buf; | ||
1648 | |||
1649 | LEAVE(); | ||
1650 | } | ||
1651 | |||
1652 | /** | ||
1653 | * @brief This function executes next command in command | ||
1654 | * pending queue. It will put fimware back to PS mode | ||
1655 | * if applicable. | ||
1656 | * | ||
1657 | * @param priv A pointer to wlan_private structure | ||
1658 | * @return 0 or -1 | ||
1659 | */ | ||
1660 | int libertas_execute_next_command(wlan_private * priv) | ||
1661 | { | ||
1662 | wlan_adapter *adapter = priv->adapter; | ||
1663 | struct cmd_ctrl_node *cmdnode = NULL; | ||
1664 | struct cmd_ds_command *cmdptr; | ||
1665 | unsigned long flags; | ||
1666 | int ret = 0; | ||
1667 | |||
1668 | lbs_pr_debug(1, "libertas_execute_next_command\n"); | ||
1669 | |||
1670 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1671 | |||
1672 | if (adapter->cur_cmd) { | ||
1673 | lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n"); | ||
1674 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1675 | ret = -1; | ||
1676 | goto done; | ||
1677 | } | ||
1678 | |||
1679 | if (!list_empty(&adapter->cmdpendingq)) { | ||
1680 | cmdnode = (struct cmd_ctrl_node *) | ||
1681 | adapter->cmdpendingq.next; | ||
1682 | } | ||
1683 | |||
1684 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1685 | |||
1686 | if (cmdnode) { | ||
1687 | lbs_pr_debug(1, | ||
1688 | "EXEC_NEXT_CMD: Got next command from cmdpendingq\n"); | ||
1689 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
1690 | |||
1691 | if (is_command_allowed_in_ps(cmdptr->command)) { | ||
1692 | if ((adapter->psstate == PS_STATE_SLEEP) | ||
1693 | || (adapter->psstate == PS_STATE_PRE_SLEEP) | ||
1694 | ) { | ||
1695 | lbs_pr_debug(1, | ||
1696 | "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n", | ||
1697 | cmdptr->command, adapter->psstate); | ||
1698 | ret = -1; | ||
1699 | goto done; | ||
1700 | } | ||
1701 | lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command " | ||
1702 | "0x%x in psstate %d\n", | ||
1703 | cmdptr->command, adapter->psstate); | ||
1704 | } else if (adapter->psstate != PS_STATE_FULL_POWER) { | ||
1705 | /* | ||
1706 | * 1. Non-PS command: | ||
1707 | * Queue it. set needtowakeup to TRUE if current state | ||
1708 | * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS. | ||
1709 | * 2. PS command but not Exit_PS: | ||
1710 | * Ignore it. | ||
1711 | * 3. PS command Exit_PS: | ||
1712 | * Set needtowakeup to TRUE if current state is SLEEP, | ||
1713 | * otherwise send this command down to firmware | ||
1714 | * immediately. | ||
1715 | */ | ||
1716 | if (cmdptr->command != | ||
1717 | cpu_to_le16(cmd_802_11_ps_mode)) { | ||
1718 | /* Prepare to send Exit PS, | ||
1719 | * this non PS command will be sent later */ | ||
1720 | if ((adapter->psstate == PS_STATE_SLEEP) | ||
1721 | || (adapter->psstate == PS_STATE_PRE_SLEEP) | ||
1722 | ) { | ||
1723 | /* w/ new scheme, it will not reach here. | ||
1724 | since it is blocked in main_thread. */ | ||
1725 | adapter->needtowakeup = 1; | ||
1726 | } else | ||
1727 | libertas_ps_wakeup(priv, 0); | ||
1728 | |||
1729 | ret = 0; | ||
1730 | goto done; | ||
1731 | } else { | ||
1732 | /* | ||
1733 | * PS command. Ignore it if it is not Exit_PS. | ||
1734 | * otherwise send it down immediately. | ||
1735 | */ | ||
1736 | struct cmd_ds_802_11_ps_mode *psm = | ||
1737 | &cmdptr->params.psmode; | ||
1738 | |||
1739 | lbs_pr_debug(1, | ||
1740 | "EXEC_NEXT_CMD: PS cmd- action=0x%x\n", | ||
1741 | psm->action); | ||
1742 | if (psm->action != | ||
1743 | cpu_to_le16(cmd_subcmd_exit_ps)) { | ||
1744 | lbs_pr_debug(1, | ||
1745 | "EXEC_NEXT_CMD: Ignore Enter PS cmd\n"); | ||
1746 | list_del((struct list_head *)cmdnode); | ||
1747 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1748 | |||
1749 | ret = 0; | ||
1750 | goto done; | ||
1751 | } | ||
1752 | |||
1753 | if ((adapter->psstate == PS_STATE_SLEEP) | ||
1754 | || (adapter->psstate == PS_STATE_PRE_SLEEP) | ||
1755 | ) { | ||
1756 | lbs_pr_debug(1, | ||
1757 | "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n"); | ||
1758 | list_del((struct list_head *)cmdnode); | ||
1759 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1760 | adapter->needtowakeup = 1; | ||
1761 | |||
1762 | ret = 0; | ||
1763 | goto done; | ||
1764 | } | ||
1765 | |||
1766 | lbs_pr_debug(1, | ||
1767 | "EXEC_NEXT_CMD: Sending Exit_PS down...\n"); | ||
1768 | } | ||
1769 | } | ||
1770 | list_del((struct list_head *)cmdnode); | ||
1771 | lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n", | ||
1772 | cmdptr->command); | ||
1773 | DownloadcommandToStation(priv, cmdnode); | ||
1774 | } else { | ||
1775 | /* | ||
1776 | * check if in power save mode, if yes, put the device back | ||
1777 | * to PS mode | ||
1778 | */ | ||
1779 | if ((adapter->psmode != wlan802_11powermodecam) && | ||
1780 | (adapter->psstate == PS_STATE_FULL_POWER) && | ||
1781 | (adapter->connect_status == libertas_connected)) { | ||
1782 | if (adapter->secinfo.WPAenabled | ||
1783 | || adapter->secinfo.WPA2enabled) { | ||
1784 | /* check for valid WPA group keys */ | ||
1785 | if (adapter->wpa_mcast_key.len | ||
1786 | || adapter->wpa_unicast_key.len) { | ||
1787 | lbs_pr_debug(1, | ||
1788 | "EXEC_NEXT_CMD: WPA enabled and GTK_SET" | ||
1789 | " go back to PS_SLEEP"); | ||
1790 | libertas_ps_sleep(priv, 0); | ||
1791 | } | ||
1792 | } else { | ||
1793 | lbs_pr_debug(1, | ||
1794 | "EXEC_NEXT_CMD: command PendQ is empty," | ||
1795 | " go back to PS_SLEEP"); | ||
1796 | libertas_ps_sleep(priv, 0); | ||
1797 | } | ||
1798 | } | ||
1799 | } | ||
1800 | |||
1801 | ret = 0; | ||
1802 | done: | ||
1803 | return ret; | ||
1804 | } | ||
1805 | |||
1806 | void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str) | ||
1807 | { | ||
1808 | union iwreq_data iwrq; | ||
1809 | u8 buf[50]; | ||
1810 | |||
1811 | ENTER(); | ||
1812 | |||
1813 | memset(&iwrq, 0, sizeof(union iwreq_data)); | ||
1814 | memset(buf, 0, sizeof(buf)); | ||
1815 | |||
1816 | snprintf(buf, sizeof(buf) - 1, "%s", str); | ||
1817 | |||
1818 | iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; | ||
1819 | |||
1820 | /* Send Event to upper layer */ | ||
1821 | lbs_pr_debug(1, "Event Indication string = %s\n", | ||
1822 | (char *)buf); | ||
1823 | lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length); | ||
1824 | |||
1825 | lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str); | ||
1826 | wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf); | ||
1827 | |||
1828 | LEAVE(); | ||
1829 | return; | ||
1830 | } | ||
1831 | |||
1832 | static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size) | ||
1833 | { | ||
1834 | unsigned long flags; | ||
1835 | wlan_adapter *adapter = priv->adapter; | ||
1836 | int ret = 0; | ||
1837 | |||
1838 | ENTER(); | ||
1839 | |||
1840 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n", | ||
1841 | size); | ||
1842 | |||
1843 | lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size); | ||
1844 | |||
1845 | ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size); | ||
1846 | priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; | ||
1847 | |||
1848 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1849 | if (adapter->intcounter || adapter->currenttxskb) | ||
1850 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n", | ||
1851 | adapter->intcounter, adapter->currenttxskb); | ||
1852 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1853 | |||
1854 | if (ret) { | ||
1855 | lbs_pr_alert( | ||
1856 | "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n"); | ||
1857 | } else { | ||
1858 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1859 | if (!adapter->intcounter) { | ||
1860 | adapter->psstate = PS_STATE_SLEEP; | ||
1861 | } else { | ||
1862 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n", | ||
1863 | adapter->intcounter); | ||
1864 | } | ||
1865 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1866 | |||
1867 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n"); | ||
1868 | lbs_pr_debug(1, "+"); | ||
1869 | } | ||
1870 | |||
1871 | LEAVE(); | ||
1872 | return ret; | ||
1873 | } | ||
1874 | |||
1875 | void libertas_ps_sleep(wlan_private * priv, int wait_option) | ||
1876 | { | ||
1877 | |||
1878 | ENTER(); | ||
1879 | |||
1880 | /* | ||
1881 | * PS is currently supported only in Infrastructure mode | ||
1882 | * Remove this check if it is to be supported in IBSS mode also | ||
1883 | */ | ||
1884 | |||
1885 | libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, | ||
1886 | cmd_subcmd_enter_ps, wait_option, 0, NULL); | ||
1887 | |||
1888 | LEAVE(); | ||
1889 | return; | ||
1890 | } | ||
1891 | |||
1892 | /** | ||
1893 | * @brief This function sends Eixt_PS command to firmware. | ||
1894 | * | ||
1895 | * @param priv A pointer to wlan_private structure | ||
1896 | * @param wait_option wait response or not | ||
1897 | * @return n/a | ||
1898 | */ | ||
1899 | void libertas_ps_wakeup(wlan_private * priv, int wait_option) | ||
1900 | { | ||
1901 | enum WLAN_802_11_POWER_MODE Localpsmode; | ||
1902 | |||
1903 | ENTER(); | ||
1904 | |||
1905 | Localpsmode = wlan802_11powermodecam; | ||
1906 | |||
1907 | lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode); | ||
1908 | |||
1909 | libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, | ||
1910 | cmd_subcmd_exit_ps, | ||
1911 | wait_option, 0, &Localpsmode); | ||
1912 | |||
1913 | LEAVE(); | ||
1914 | return; | ||
1915 | } | ||
1916 | |||
1917 | /** | ||
1918 | * @brief This function checks condition and prepares to | ||
1919 | * send sleep confirm command to firmware if ok. | ||
1920 | * | ||
1921 | * @param priv A pointer to wlan_private structure | ||
1922 | * @param psmode Power Saving mode | ||
1923 | * @return n/a | ||
1924 | */ | ||
1925 | void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode) | ||
1926 | { | ||
1927 | unsigned long flags =0; | ||
1928 | wlan_adapter *adapter = priv->adapter; | ||
1929 | u8 allowed = 1; | ||
1930 | |||
1931 | ENTER(); | ||
1932 | |||
1933 | if (priv->wlan_dev.dnld_sent) { | ||
1934 | allowed = 0; | ||
1935 | lbs_pr_debug(1, "D"); | ||
1936 | } | ||
1937 | |||
1938 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1939 | if (adapter->cur_cmd) { | ||
1940 | allowed = 0; | ||
1941 | lbs_pr_debug(1, "C"); | ||
1942 | } | ||
1943 | if (adapter->intcounter > 0) { | ||
1944 | allowed = 0; | ||
1945 | lbs_pr_debug(1, "I%d", adapter->intcounter); | ||
1946 | } | ||
1947 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1948 | |||
1949 | if (allowed) { | ||
1950 | lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n"); | ||
1951 | sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep, | ||
1952 | sizeof(struct PS_CMD_ConfirmSleep)); | ||
1953 | } else { | ||
1954 | lbs_pr_debug(1, "Sleep Confirm has been delayed\n"); | ||
1955 | } | ||
1956 | |||
1957 | LEAVE(); | ||
1958 | } | ||
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c new file mode 100644 index 000000000000..cdb012c7e9cf --- /dev/null +++ b/drivers/net/wireless/libertas/cmdresp.c | |||
@@ -0,0 +1,1031 @@ | |||
1 | /** | ||
2 | * This file contains the handling of command | ||
3 | * responses as well as events generated by firmware. | ||
4 | */ | ||
5 | #include <linux/delay.h> | ||
6 | #include <linux/if_arp.h> | ||
7 | #include <linux/netdevice.h> | ||
8 | |||
9 | #include <net/iw_handler.h> | ||
10 | |||
11 | #include "host.h" | ||
12 | #include "sbi.h" | ||
13 | #include "decl.h" | ||
14 | #include "defs.h" | ||
15 | #include "dev.h" | ||
16 | #include "join.h" | ||
17 | #include "wext.h" | ||
18 | |||
19 | /** | ||
20 | * @brief This function handles disconnect event. it | ||
21 | * reports disconnect to upper layer, clean tx/rx packets, | ||
22 | * reset link state etc. | ||
23 | * | ||
24 | * @param priv A pointer to wlan_private structure | ||
25 | * @return n/a | ||
26 | */ | ||
27 | void libertas_mac_event_disconnected(wlan_private * priv) | ||
28 | { | ||
29 | wlan_adapter *adapter = priv->adapter; | ||
30 | union iwreq_data wrqu; | ||
31 | |||
32 | if (adapter->connect_status != libertas_connected) | ||
33 | return; | ||
34 | |||
35 | lbs_pr_debug(1, "Handles disconnect event.\n"); | ||
36 | |||
37 | memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); | ||
38 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
39 | |||
40 | /* | ||
41 | * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. | ||
42 | * It causes problem in the Supplicant | ||
43 | */ | ||
44 | |||
45 | msleep_interruptible(1000); | ||
46 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); | ||
47 | |||
48 | /* Free Tx and Rx packets */ | ||
49 | kfree_skb(priv->adapter->currenttxskb); | ||
50 | priv->adapter->currenttxskb = NULL; | ||
51 | |||
52 | /* report disconnect to upper layer */ | ||
53 | netif_stop_queue(priv->wlan_dev.netdev); | ||
54 | netif_carrier_off(priv->wlan_dev.netdev); | ||
55 | |||
56 | /* reset SNR/NF/RSSI values */ | ||
57 | memset(adapter->SNR, 0x00, sizeof(adapter->SNR)); | ||
58 | memset(adapter->NF, 0x00, sizeof(adapter->NF)); | ||
59 | memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI)); | ||
60 | memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); | ||
61 | memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); | ||
62 | adapter->nextSNRNF = 0; | ||
63 | adapter->numSNRNF = 0; | ||
64 | adapter->rxpd_rate = 0; | ||
65 | lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n", | ||
66 | adapter->curbssparams.ssid.ssid, | ||
67 | adapter->curbssparams.ssid.ssidlength); | ||
68 | lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n", | ||
69 | adapter->previousssid.ssid, adapter->previousssid.ssidlength); | ||
70 | |||
71 | /* reset internal flags */ | ||
72 | adapter->secinfo.WPAenabled = 0; | ||
73 | adapter->secinfo.WPA2enabled = 0; | ||
74 | adapter->wpa_ie_len = 0; | ||
75 | adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; | ||
76 | adapter->secinfo.Encryptionmode = CIPHER_NONE; | ||
77 | |||
78 | adapter->connect_status = libertas_disconnected; | ||
79 | |||
80 | /* | ||
81 | * memorize the previous SSID and BSSID | ||
82 | * it could be used for re-assoc | ||
83 | */ | ||
84 | memcpy(&adapter->previousssid, | ||
85 | &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID)); | ||
86 | memcpy(adapter->previousbssid, | ||
87 | adapter->curbssparams.bssid, ETH_ALEN); | ||
88 | |||
89 | /* need to erase the current SSID and BSSID info */ | ||
90 | adapter->pattemptedbssdesc = NULL; | ||
91 | memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); | ||
92 | |||
93 | if (adapter->psstate != PS_STATE_FULL_POWER) { | ||
94 | /* make firmware to exit PS mode */ | ||
95 | lbs_pr_debug(1, "Disconnected, so exit PS mode.\n"); | ||
96 | libertas_ps_wakeup(priv, 0); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * @brief This function handles MIC failure event. | ||
102 | * | ||
103 | * @param priv A pointer to wlan_private structure | ||
104 | * @para event the event id | ||
105 | * @return n/a | ||
106 | */ | ||
107 | static void handle_mic_failureevent(wlan_private * priv, u32 event) | ||
108 | { | ||
109 | char buf[50]; | ||
110 | |||
111 | memset(buf, 0, sizeof(buf)); | ||
112 | |||
113 | sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); | ||
114 | |||
115 | if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { | ||
116 | strcat(buf, "unicast "); | ||
117 | } else { | ||
118 | strcat(buf, "multicast "); | ||
119 | } | ||
120 | |||
121 | libertas_send_iwevcustom_event(priv, buf); | ||
122 | } | ||
123 | |||
124 | static int wlan_ret_reg_access(wlan_private * priv, | ||
125 | u16 type, struct cmd_ds_command *resp) | ||
126 | { | ||
127 | wlan_adapter *adapter = priv->adapter; | ||
128 | |||
129 | ENTER(); | ||
130 | |||
131 | switch (type) { | ||
132 | case cmd_ret_mac_reg_access: | ||
133 | { | ||
134 | struct cmd_ds_mac_reg_access *reg; | ||
135 | |||
136 | reg = | ||
137 | (struct cmd_ds_mac_reg_access *)&resp->params. | ||
138 | macreg; | ||
139 | |||
140 | adapter->offsetvalue.offset = reg->offset; | ||
141 | adapter->offsetvalue.value = reg->value; | ||
142 | break; | ||
143 | } | ||
144 | |||
145 | case cmd_ret_bbp_reg_access: | ||
146 | { | ||
147 | struct cmd_ds_bbp_reg_access *reg; | ||
148 | reg = | ||
149 | (struct cmd_ds_bbp_reg_access *)&resp->params. | ||
150 | bbpreg; | ||
151 | |||
152 | adapter->offsetvalue.offset = reg->offset; | ||
153 | adapter->offsetvalue.value = reg->value; | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | case cmd_ret_rf_reg_access: | ||
158 | { | ||
159 | struct cmd_ds_rf_reg_access *reg; | ||
160 | reg = | ||
161 | (struct cmd_ds_rf_reg_access *)&resp->params. | ||
162 | rfreg; | ||
163 | |||
164 | adapter->offsetvalue.offset = reg->offset; | ||
165 | adapter->offsetvalue.value = reg->value; | ||
166 | break; | ||
167 | } | ||
168 | |||
169 | default: | ||
170 | LEAVE(); | ||
171 | return -1; | ||
172 | } | ||
173 | |||
174 | LEAVE(); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int wlan_ret_get_hw_spec(wlan_private * priv, | ||
179 | struct cmd_ds_command *resp) | ||
180 | { | ||
181 | u32 i; | ||
182 | struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; | ||
183 | wlan_adapter *adapter = priv->adapter; | ||
184 | int ret = 0; | ||
185 | |||
186 | ENTER(); | ||
187 | |||
188 | adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); | ||
189 | |||
190 | adapter->fwreleasenumber = hwspec->fwreleasenumber; | ||
191 | |||
192 | lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n", | ||
193 | adapter->fwreleasenumber); | ||
194 | lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", | ||
195 | hwspec->permanentaddr[0], hwspec->permanentaddr[1], | ||
196 | hwspec->permanentaddr[2], hwspec->permanentaddr[3], | ||
197 | hwspec->permanentaddr[4], hwspec->permanentaddr[5]); | ||
198 | lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X version=0x%X\n", | ||
199 | hwspec->hwifversion, hwspec->version); | ||
200 | |||
201 | adapter->regioncode = le16_to_cpu(hwspec->regioncode); | ||
202 | |||
203 | for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { | ||
204 | /* use the region code to search for the index */ | ||
205 | if (adapter->regioncode == libertas_region_code_to_index[i]) { | ||
206 | adapter->regiontableindex = (u16) i; | ||
207 | break; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* if it's unidentified region code, use the default (USA) */ | ||
212 | if (i >= MRVDRV_MAX_REGION_CODE) { | ||
213 | adapter->regioncode = 0x10; | ||
214 | adapter->regiontableindex = 0; | ||
215 | lbs_pr_info( | ||
216 | "unidentified region code, use the default (USA)\n"); | ||
217 | } | ||
218 | |||
219 | if (adapter->current_addr[0] == 0xff) { | ||
220 | memmove(adapter->current_addr, hwspec->permanentaddr, | ||
221 | ETH_ALEN); | ||
222 | } | ||
223 | |||
224 | memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN); | ||
225 | memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); | ||
226 | |||
227 | if (libertas_set_regiontable(priv, adapter->regioncode, 0)) { | ||
228 | ret = -1; | ||
229 | goto done; | ||
230 | } | ||
231 | |||
232 | if (libertas_set_universaltable(priv, 0)) { | ||
233 | ret = -1; | ||
234 | goto done; | ||
235 | } | ||
236 | |||
237 | done: | ||
238 | LEAVE(); | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | static int wlan_ret_802_11_sleep_params(wlan_private * priv, | ||
243 | struct cmd_ds_command *resp) | ||
244 | { | ||
245 | struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; | ||
246 | wlan_adapter *adapter = priv->adapter; | ||
247 | |||
248 | ENTER(); | ||
249 | |||
250 | lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n" | ||
251 | " extsleepclk=%x\n", sp->error, sp->offset, | ||
252 | sp->stabletime, sp->calcontrol, sp->externalsleepclk); | ||
253 | adapter->sp.sp_error = le16_to_cpu(sp->error); | ||
254 | adapter->sp.sp_offset = le16_to_cpu(sp->offset); | ||
255 | adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime); | ||
256 | adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol); | ||
257 | adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk); | ||
258 | adapter->sp.sp_reserved = le16_to_cpu(sp->reserved); | ||
259 | |||
260 | LEAVE(); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int wlan_ret_802_11_stat(wlan_private * priv, | ||
265 | struct cmd_ds_command *resp) | ||
266 | { | ||
267 | /* currently adapter->wlan802_11Stat is unused | ||
268 | |||
269 | struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; | ||
270 | wlan_adapter *adapter = priv->adapter; | ||
271 | |||
272 | // TODO Convert it to Big endian befor copy | ||
273 | memcpy(&adapter->wlan802_11Stat, | ||
274 | p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); | ||
275 | */ | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int wlan_ret_802_11_snmp_mib(wlan_private * priv, | ||
280 | struct cmd_ds_command *resp) | ||
281 | { | ||
282 | struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; | ||
283 | u16 oid = le16_to_cpu(smib->oid); | ||
284 | u16 querytype = le16_to_cpu(smib->querytype); | ||
285 | |||
286 | ENTER(); | ||
287 | |||
288 | lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid, | ||
289 | querytype); | ||
290 | lbs_pr_debug(1, "SNMP_RESP: Buf size = %x\n", | ||
291 | le16_to_cpu(smib->bufsize)); | ||
292 | |||
293 | if (querytype == cmd_act_get) { | ||
294 | switch (oid) { | ||
295 | case fragthresh_i: | ||
296 | priv->adapter->fragthsd = | ||
297 | le16_to_cpu(* | ||
298 | ((unsigned short *)(smib->value))); | ||
299 | lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n", | ||
300 | priv->adapter->fragthsd); | ||
301 | break; | ||
302 | case rtsthresh_i: | ||
303 | priv->adapter->rtsthsd = | ||
304 | le16_to_cpu(* | ||
305 | ((unsigned short *)(smib->value))); | ||
306 | lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n", | ||
307 | priv->adapter->rtsthsd); | ||
308 | break; | ||
309 | case short_retrylim_i: | ||
310 | priv->adapter->txretrycount = | ||
311 | le16_to_cpu(* | ||
312 | ((unsigned short *)(smib->value))); | ||
313 | lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n", | ||
314 | priv->adapter->rtsthsd); | ||
315 | break; | ||
316 | default: | ||
317 | break; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | LEAVE(); | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int wlan_ret_802_11_key_material(wlan_private * priv, | ||
326 | struct cmd_ds_command *resp) | ||
327 | { | ||
328 | struct cmd_ds_802_11_key_material *pkeymaterial = | ||
329 | &resp->params.keymaterial; | ||
330 | wlan_adapter *adapter = priv->adapter; | ||
331 | u16 action = le16_to_cpu(pkeymaterial->action); | ||
332 | |||
333 | ENTER(); | ||
334 | |||
335 | /* Copy the returned key to driver private data */ | ||
336 | if (action == cmd_act_get) { | ||
337 | u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; | ||
338 | u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); | ||
339 | |||
340 | while (buf_ptr < resp_end) { | ||
341 | struct MrvlIEtype_keyParamSet * pkeyparamset = | ||
342 | (struct MrvlIEtype_keyParamSet *) buf_ptr; | ||
343 | struct WLAN_802_11_KEY * pkey; | ||
344 | u16 key_info = le16_to_cpu(pkeyparamset->keyinfo); | ||
345 | u16 param_set_len = le16_to_cpu(pkeyparamset->length); | ||
346 | u8 * end; | ||
347 | u16 key_len = le16_to_cpu(pkeyparamset->keylen); | ||
348 | |||
349 | end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) | ||
350 | + sizeof (pkeyparamset->length) | ||
351 | + param_set_len; | ||
352 | /* Make sure we don't access past the end of the IEs */ | ||
353 | if (end > resp_end) | ||
354 | break; | ||
355 | |||
356 | if (key_info & KEY_INFO_WPA_UNICAST) | ||
357 | pkey = &adapter->wpa_unicast_key; | ||
358 | else if (key_info & KEY_INFO_WPA_MCAST) | ||
359 | pkey = &adapter->wpa_mcast_key; | ||
360 | else | ||
361 | break; | ||
362 | |||
363 | /* Copy returned key into driver */ | ||
364 | memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); | ||
365 | if (key_len > sizeof(pkey->key)) | ||
366 | break; | ||
367 | pkey->type = le16_to_cpu(pkeyparamset->keytypeid); | ||
368 | pkey->flags = le16_to_cpu(pkeyparamset->keyinfo); | ||
369 | pkey->len = le16_to_cpu(pkeyparamset->keylen); | ||
370 | memcpy(pkey->key, pkeyparamset->key, pkey->len); | ||
371 | |||
372 | buf_ptr = end + 1; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | LEAVE(); | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int wlan_ret_802_11_mac_address(wlan_private * priv, | ||
381 | struct cmd_ds_command *resp) | ||
382 | { | ||
383 | struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; | ||
384 | wlan_adapter *adapter = priv->adapter; | ||
385 | |||
386 | ENTER(); | ||
387 | |||
388 | memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN); | ||
389 | |||
390 | LEAVE(); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int wlan_ret_802_11_rf_tx_power(wlan_private * priv, | ||
395 | struct cmd_ds_command *resp) | ||
396 | { | ||
397 | struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; | ||
398 | wlan_adapter *adapter = priv->adapter; | ||
399 | |||
400 | ENTER(); | ||
401 | |||
402 | adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel); | ||
403 | |||
404 | lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel); | ||
405 | |||
406 | LEAVE(); | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static int wlan_ret_802_11_rf_antenna(wlan_private * priv, | ||
411 | struct cmd_ds_command *resp) | ||
412 | { | ||
413 | struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant; | ||
414 | wlan_adapter *adapter = priv->adapter; | ||
415 | u16 action = le16_to_cpu(pAntenna->action); | ||
416 | |||
417 | if (action == cmd_act_get_rx) | ||
418 | adapter->rxantennamode = | ||
419 | le16_to_cpu(pAntenna->antennamode); | ||
420 | |||
421 | if (action == cmd_act_get_tx) | ||
422 | adapter->txantennamode = | ||
423 | le16_to_cpu(pAntenna->antennamode); | ||
424 | |||
425 | lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n", | ||
426 | action, le16_to_cpu(pAntenna->antennamode)); | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv, | ||
432 | struct cmd_ds_command *resp) | ||
433 | { | ||
434 | struct cmd_ds_802_11_rate_adapt_rateset *rates = | ||
435 | &resp->params.rateset; | ||
436 | wlan_adapter *adapter = priv->adapter; | ||
437 | |||
438 | ENTER(); | ||
439 | |||
440 | if (rates->action == cmd_act_get) { | ||
441 | adapter->enablehwauto = rates->enablehwauto; | ||
442 | adapter->ratebitmap = rates->bitmap; | ||
443 | } | ||
444 | |||
445 | LEAVE(); | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int wlan_ret_802_11_data_rate(wlan_private * priv, | ||
451 | struct cmd_ds_command *resp) | ||
452 | { | ||
453 | struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; | ||
454 | wlan_adapter *adapter = priv->adapter; | ||
455 | u8 dot11datarate; | ||
456 | |||
457 | ENTER(); | ||
458 | |||
459 | lbs_dbg_hex("DATA_RATE_RESP: data_rate- ", | ||
460 | (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate)); | ||
461 | |||
462 | dot11datarate = pdatarate->datarate[0]; | ||
463 | if (pdatarate->action == cmd_act_get_tx_rate) { | ||
464 | memcpy(adapter->libertas_supported_rates, pdatarate->datarate, | ||
465 | sizeof(adapter->libertas_supported_rates)); | ||
466 | } | ||
467 | adapter->datarate = libertas_index_to_data_rate(dot11datarate); | ||
468 | |||
469 | LEAVE(); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int wlan_ret_802_11_rf_channel(wlan_private * priv, | ||
474 | struct cmd_ds_command *resp) | ||
475 | { | ||
476 | struct cmd_ds_802_11_rf_channel *rfchannel = | ||
477 | &resp->params.rfchannel; | ||
478 | wlan_adapter *adapter = priv->adapter; | ||
479 | u16 action = le16_to_cpu(rfchannel->action); | ||
480 | u16 newchannel = le16_to_cpu(rfchannel->currentchannel); | ||
481 | |||
482 | ENTER(); | ||
483 | |||
484 | if (action == cmd_opt_802_11_rf_channel_get | ||
485 | && adapter->curbssparams.channel != newchannel) { | ||
486 | lbs_pr_debug(1, "channel Switch: %d to %d\n", | ||
487 | adapter->curbssparams.channel, newchannel); | ||
488 | |||
489 | /* Update the channel again */ | ||
490 | adapter->curbssparams.channel = newchannel; | ||
491 | } | ||
492 | |||
493 | LEAVE(); | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static int wlan_ret_802_11_rssi(wlan_private * priv, | ||
498 | struct cmd_ds_command *resp) | ||
499 | { | ||
500 | struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; | ||
501 | wlan_adapter *adapter = priv->adapter; | ||
502 | |||
503 | /* store the non average value */ | ||
504 | adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); | ||
505 | adapter->NF[TYPE_BEACON][TYPE_NOAVG] = | ||
506 | le16_to_cpu(rssirsp->noisefloor); | ||
507 | |||
508 | adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); | ||
509 | adapter->NF[TYPE_BEACON][TYPE_AVG] = | ||
510 | le16_to_cpu(rssirsp->avgnoisefloor); | ||
511 | |||
512 | adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = | ||
513 | CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], | ||
514 | adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | ||
515 | |||
516 | adapter->RSSI[TYPE_BEACON][TYPE_AVG] = | ||
517 | CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, | ||
518 | adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); | ||
519 | |||
520 | lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n", | ||
521 | adapter->RSSI[TYPE_BEACON][TYPE_AVG]); | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int wlan_ret_802_11_eeprom_access(wlan_private * priv, | ||
527 | struct cmd_ds_command *resp) | ||
528 | { | ||
529 | wlan_adapter *adapter = priv->adapter; | ||
530 | struct wlan_ioctl_regrdwr *pbuf; | ||
531 | pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom; | ||
532 | |||
533 | lbs_pr_debug(1, "eeprom read len=%x\n", | ||
534 | le16_to_cpu(resp->params.rdeeprom.bytecount)); | ||
535 | if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { | ||
536 | pbuf->NOB = 0; | ||
537 | lbs_pr_debug(1, "eeprom read return length is too big\n"); | ||
538 | return -1; | ||
539 | } | ||
540 | pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); | ||
541 | if (pbuf->NOB > 0) { | ||
542 | |||
543 | memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, | ||
544 | le16_to_cpu(resp->params.rdeeprom.bytecount)); | ||
545 | lbs_dbg_hex("adapter", (char *)&pbuf->value, | ||
546 | le16_to_cpu(resp->params.rdeeprom.bytecount)); | ||
547 | } | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static int wlan_ret_get_log(wlan_private * priv, | ||
552 | struct cmd_ds_command *resp) | ||
553 | { | ||
554 | struct cmd_ds_802_11_get_log *logmessage = | ||
555 | (struct cmd_ds_802_11_get_log *)&resp->params.glog; | ||
556 | wlan_adapter *adapter = priv->adapter; | ||
557 | |||
558 | ENTER(); | ||
559 | |||
560 | /* TODO Convert it to Big Endian before copy */ | ||
561 | memcpy(&adapter->logmsg, logmessage, | ||
562 | sizeof(struct cmd_ds_802_11_get_log)); | ||
563 | |||
564 | LEAVE(); | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | static inline int handle_cmd_response(u16 respcmd, | ||
569 | struct cmd_ds_command *resp, | ||
570 | wlan_private *priv) | ||
571 | { | ||
572 | int ret = 0; | ||
573 | unsigned long flags; | ||
574 | wlan_adapter *adapter = priv->adapter; | ||
575 | |||
576 | switch (respcmd) { | ||
577 | case cmd_ret_mac_reg_access: | ||
578 | case cmd_ret_bbp_reg_access: | ||
579 | case cmd_ret_rf_reg_access: | ||
580 | ret = wlan_ret_reg_access(priv, respcmd, resp); | ||
581 | break; | ||
582 | |||
583 | case cmd_ret_hw_spec_info: | ||
584 | ret = wlan_ret_get_hw_spec(priv, resp); | ||
585 | break; | ||
586 | |||
587 | case cmd_ret_802_11_scan: | ||
588 | ret = libertas_ret_80211_scan(priv, resp); | ||
589 | break; | ||
590 | |||
591 | case cmd_ret_802_11_get_log: | ||
592 | ret = wlan_ret_get_log(priv, resp); | ||
593 | break; | ||
594 | |||
595 | case cmd_ret_802_11_associate: | ||
596 | case cmd_ret_802_11_reassociate: | ||
597 | ret = libertas_ret_80211_associate(priv, resp); | ||
598 | break; | ||
599 | |||
600 | case cmd_ret_802_11_disassociate: | ||
601 | case cmd_ret_802_11_deauthenticate: | ||
602 | ret = libertas_ret_80211_disassociate(priv, resp); | ||
603 | break; | ||
604 | |||
605 | case cmd_ret_802_11_ad_hoc_start: | ||
606 | case cmd_ret_802_11_ad_hoc_join: | ||
607 | ret = libertas_ret_80211_ad_hoc_start(priv, resp); | ||
608 | break; | ||
609 | |||
610 | case cmd_ret_802_11_stat: | ||
611 | ret = wlan_ret_802_11_stat(priv, resp); | ||
612 | break; | ||
613 | |||
614 | case cmd_ret_802_11_snmp_mib: | ||
615 | ret = wlan_ret_802_11_snmp_mib(priv, resp); | ||
616 | break; | ||
617 | |||
618 | case cmd_ret_802_11_rf_tx_power: | ||
619 | ret = wlan_ret_802_11_rf_tx_power(priv, resp); | ||
620 | break; | ||
621 | |||
622 | case cmd_ret_802_11_set_afc: | ||
623 | case cmd_ret_802_11_get_afc: | ||
624 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
625 | memmove(adapter->cur_cmd->pdata_buf, | ||
626 | &resp->params.afc, | ||
627 | sizeof(struct cmd_ds_802_11_afc)); | ||
628 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
629 | |||
630 | break; | ||
631 | case cmd_ret_802_11_rf_antenna: | ||
632 | ret = wlan_ret_802_11_rf_antenna(priv, resp); | ||
633 | break; | ||
634 | |||
635 | case cmd_ret_mac_multicast_adr: | ||
636 | case cmd_ret_mac_control: | ||
637 | case cmd_ret_802_11_set_wep: | ||
638 | case cmd_ret_802_11_reset: | ||
639 | case cmd_ret_802_11_authenticate: | ||
640 | case cmd_ret_802_11_radio_control: | ||
641 | case cmd_ret_802_11_beacon_stop: | ||
642 | case cmd_ret_802_11_enable_rsn: | ||
643 | break; | ||
644 | |||
645 | case cmd_ret_802_11_data_rate: | ||
646 | ret = wlan_ret_802_11_data_rate(priv, resp); | ||
647 | break; | ||
648 | case cmd_ret_802_11_rate_adapt_rateset: | ||
649 | ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp); | ||
650 | break; | ||
651 | case cmd_ret_802_11_rf_channel: | ||
652 | ret = wlan_ret_802_11_rf_channel(priv, resp); | ||
653 | break; | ||
654 | |||
655 | case cmd_ret_802_11_rssi: | ||
656 | ret = wlan_ret_802_11_rssi(priv, resp); | ||
657 | break; | ||
658 | |||
659 | case cmd_ret_802_11_mac_address: | ||
660 | ret = wlan_ret_802_11_mac_address(priv, resp); | ||
661 | break; | ||
662 | |||
663 | case cmd_ret_802_11_ad_hoc_stop: | ||
664 | ret = libertas_ret_80211_ad_hoc_stop(priv, resp); | ||
665 | break; | ||
666 | |||
667 | case cmd_ret_802_11_key_material: | ||
668 | lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n"); | ||
669 | ret = wlan_ret_802_11_key_material(priv, resp); | ||
670 | break; | ||
671 | |||
672 | case cmd_ret_802_11_eeprom_access: | ||
673 | ret = wlan_ret_802_11_eeprom_access(priv, resp); | ||
674 | break; | ||
675 | |||
676 | case cmd_ret_802_11d_domain_info: | ||
677 | ret = libertas_ret_802_11d_domain_info(priv, resp); | ||
678 | break; | ||
679 | |||
680 | case cmd_ret_802_11_sleep_params: | ||
681 | ret = wlan_ret_802_11_sleep_params(priv, resp); | ||
682 | break; | ||
683 | case cmd_ret_802_11_inactivity_timeout: | ||
684 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
685 | *((u16 *) adapter->cur_cmd->pdata_buf) = | ||
686 | le16_to_cpu(resp->params.inactivity_timeout.timeout); | ||
687 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
688 | break; | ||
689 | |||
690 | case cmd_ret_802_11_tpc_cfg: | ||
691 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
692 | memmove(adapter->cur_cmd->pdata_buf, | ||
693 | &resp->params.tpccfg, | ||
694 | sizeof(struct cmd_ds_802_11_tpc_cfg)); | ||
695 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
696 | break; | ||
697 | case cmd_ret_802_11_led_gpio_ctrl: | ||
698 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
699 | memmove(adapter->cur_cmd->pdata_buf, | ||
700 | &resp->params.ledgpio, | ||
701 | sizeof(struct cmd_ds_802_11_led_ctrl)); | ||
702 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
703 | break; | ||
704 | case cmd_ret_802_11_pwr_cfg: | ||
705 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
706 | memmove(adapter->cur_cmd->pdata_buf, | ||
707 | &resp->params.pwrcfg, | ||
708 | sizeof(struct cmd_ds_802_11_pwr_cfg)); | ||
709 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
710 | |||
711 | break; | ||
712 | |||
713 | case cmd_ret_get_tsf: | ||
714 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
715 | memcpy(priv->adapter->cur_cmd->pdata_buf, | ||
716 | &resp->params.gettsf.tsfvalue, sizeof(u64)); | ||
717 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
718 | break; | ||
719 | case cmd_ret_bt_access: | ||
720 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
721 | if (adapter->cur_cmd->pdata_buf) | ||
722 | memcpy(adapter->cur_cmd->pdata_buf, | ||
723 | &resp->params.bt.addr1, 2 * ETH_ALEN); | ||
724 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
725 | break; | ||
726 | case cmd_ret_fwt_access: | ||
727 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
728 | if (adapter->cur_cmd->pdata_buf) | ||
729 | memcpy(adapter->cur_cmd->pdata_buf, | ||
730 | &resp->params.fwt, | ||
731 | sizeof(resp->params.fwt)); | ||
732 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
733 | break; | ||
734 | case cmd_ret_mesh_access: | ||
735 | if (adapter->cur_cmd->pdata_buf) | ||
736 | memcpy(adapter->cur_cmd->pdata_buf, | ||
737 | &resp->params.mesh, | ||
738 | sizeof(resp->params.mesh)); | ||
739 | break; | ||
740 | case cmd_rte_802_11_tx_rate_query: | ||
741 | priv->adapter->txrate = resp->params.txrate.txrate; | ||
742 | break; | ||
743 | default: | ||
744 | lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n", | ||
745 | resp->command); | ||
746 | break; | ||
747 | } | ||
748 | return ret; | ||
749 | } | ||
750 | |||
751 | int libertas_process_rx_command(wlan_private * priv) | ||
752 | { | ||
753 | u16 respcmd; | ||
754 | struct cmd_ds_command *resp; | ||
755 | wlan_adapter *adapter = priv->adapter; | ||
756 | int ret = 0; | ||
757 | ulong flags; | ||
758 | u16 result; | ||
759 | |||
760 | ENTER(); | ||
761 | |||
762 | lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies); | ||
763 | |||
764 | /* Now we got response from FW, cancel the command timer */ | ||
765 | del_timer(&adapter->command_timer); | ||
766 | |||
767 | mutex_lock(&adapter->lock); | ||
768 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
769 | |||
770 | if (!adapter->cur_cmd) { | ||
771 | lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd); | ||
772 | ret = -1; | ||
773 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
774 | goto done; | ||
775 | } | ||
776 | resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr); | ||
777 | |||
778 | lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr, | ||
779 | priv->wlan_dev.upld_len); | ||
780 | |||
781 | respcmd = le16_to_cpu(resp->command); | ||
782 | |||
783 | result = le16_to_cpu(resp->result); | ||
784 | |||
785 | lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd, | ||
786 | result, priv->wlan_dev.upld_len); | ||
787 | |||
788 | if (!(respcmd & 0x8000)) { | ||
789 | lbs_pr_debug(1, "Invalid response to command!"); | ||
790 | adapter->cur_cmd_retcode = -1; | ||
791 | __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); | ||
792 | adapter->nr_cmd_pending--; | ||
793 | adapter->cur_cmd = NULL; | ||
794 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
795 | ret = -1; | ||
796 | goto done; | ||
797 | } | ||
798 | |||
799 | /* Store the response code to cur_cmd_retcode. */ | ||
800 | adapter->cur_cmd_retcode = le16_to_cpu(resp->result); | ||
801 | |||
802 | if (respcmd == cmd_ret_802_11_ps_mode) { | ||
803 | struct cmd_ds_802_11_ps_mode *psmode; | ||
804 | |||
805 | psmode = &resp->params.psmode; | ||
806 | lbs_pr_debug(1, | ||
807 | "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", | ||
808 | resp->result, psmode->action); | ||
809 | psmode->action = cpu_to_le16(psmode->action); | ||
810 | |||
811 | if (result) { | ||
812 | lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n", | ||
813 | resp->result); | ||
814 | if (adapter->inframode == wlan802_11ibss) { | ||
815 | /* | ||
816 | * We should not re-try enter-ps command in | ||
817 | * ad-hoc mode. It takes place in | ||
818 | * libertas_execute_next_command(). | ||
819 | */ | ||
820 | if (psmode->action == cmd_subcmd_enter_ps) | ||
821 | adapter->psmode = | ||
822 | wlan802_11powermodecam; | ||
823 | } | ||
824 | } else if (psmode->action == cmd_subcmd_enter_ps) { | ||
825 | adapter->needtowakeup = 0; | ||
826 | adapter->psstate = PS_STATE_AWAKE; | ||
827 | |||
828 | lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n"); | ||
829 | if (adapter->connect_status != libertas_connected) { | ||
830 | /* | ||
831 | * When Deauth Event received before Enter_PS command | ||
832 | * response, We need to wake up the firmware. | ||
833 | */ | ||
834 | lbs_pr_debug(1, | ||
835 | "Disconnected, Going to invoke libertas_ps_wakeup\n"); | ||
836 | |||
837 | mutex_unlock(&adapter->lock); | ||
838 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
839 | libertas_ps_wakeup(priv, 0); | ||
840 | mutex_lock(&adapter->lock); | ||
841 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
842 | } | ||
843 | } else if (psmode->action == cmd_subcmd_exit_ps) { | ||
844 | adapter->needtowakeup = 0; | ||
845 | adapter->psstate = PS_STATE_FULL_POWER; | ||
846 | lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n"); | ||
847 | } else { | ||
848 | lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n", | ||
849 | psmode->action); | ||
850 | } | ||
851 | |||
852 | __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); | ||
853 | adapter->nr_cmd_pending--; | ||
854 | adapter->cur_cmd = NULL; | ||
855 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
856 | |||
857 | ret = 0; | ||
858 | goto done; | ||
859 | } | ||
860 | |||
861 | if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) { | ||
862 | /* Copy the response back to response buffer */ | ||
863 | memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size); | ||
864 | |||
865 | adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD; | ||
866 | } | ||
867 | |||
868 | /* If the command is not successful, cleanup and return failure */ | ||
869 | if ((result != 0 || !(respcmd & 0x8000))) { | ||
870 | lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n", | ||
871 | resp->command, resp->result); | ||
872 | /* | ||
873 | * Handling errors here | ||
874 | */ | ||
875 | switch (respcmd) { | ||
876 | case cmd_ret_hw_spec_info: | ||
877 | case cmd_ret_802_11_reset: | ||
878 | lbs_pr_debug(1, "CMD_RESP: Reset command failed\n"); | ||
879 | break; | ||
880 | |||
881 | } | ||
882 | |||
883 | __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); | ||
884 | adapter->nr_cmd_pending--; | ||
885 | adapter->cur_cmd = NULL; | ||
886 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
887 | |||
888 | ret = -1; | ||
889 | goto done; | ||
890 | } | ||
891 | |||
892 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
893 | |||
894 | ret = handle_cmd_response(respcmd, resp, priv); | ||
895 | |||
896 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
897 | if (adapter->cur_cmd) { | ||
898 | /* Clean up and Put current command back to cmdfreeq */ | ||
899 | __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); | ||
900 | adapter->nr_cmd_pending--; | ||
901 | WARN_ON(adapter->nr_cmd_pending > 128); | ||
902 | adapter->cur_cmd = NULL; | ||
903 | } | ||
904 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
905 | |||
906 | done: | ||
907 | mutex_unlock(&adapter->lock); | ||
908 | LEAVE(); | ||
909 | return ret; | ||
910 | } | ||
911 | |||
912 | int libertas_process_event(wlan_private * priv) | ||
913 | { | ||
914 | int ret = 0; | ||
915 | wlan_adapter *adapter = priv->adapter; | ||
916 | u32 eventcause; | ||
917 | |||
918 | spin_lock_irq(&adapter->driver_lock); | ||
919 | eventcause = adapter->eventcause; | ||
920 | spin_unlock_irq(&adapter->driver_lock); | ||
921 | |||
922 | ENTER(); | ||
923 | |||
924 | lbs_pr_debug(1, "EVENT Cause %x\n", eventcause); | ||
925 | |||
926 | switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) { | ||
927 | case MACREG_INT_CODE_LINK_SENSED: | ||
928 | lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n"); | ||
929 | break; | ||
930 | |||
931 | case MACREG_INT_CODE_DEAUTHENTICATED: | ||
932 | lbs_pr_debug(1, "EVENT: Deauthenticated\n"); | ||
933 | libertas_mac_event_disconnected(priv); | ||
934 | break; | ||
935 | |||
936 | case MACREG_INT_CODE_DISASSOCIATED: | ||
937 | lbs_pr_debug(1, "EVENT: Disassociated\n"); | ||
938 | libertas_mac_event_disconnected(priv); | ||
939 | break; | ||
940 | |||
941 | case MACREG_INT_CODE_LINK_LOSE_NO_SCAN: | ||
942 | lbs_pr_debug(1, "EVENT: Link lost\n"); | ||
943 | libertas_mac_event_disconnected(priv); | ||
944 | break; | ||
945 | |||
946 | case MACREG_INT_CODE_PS_SLEEP: | ||
947 | lbs_pr_debug(1, "EVENT: SLEEP\n"); | ||
948 | lbs_pr_debug(1, "_"); | ||
949 | |||
950 | /* handle unexpected PS SLEEP event */ | ||
951 | if (adapter->psstate == PS_STATE_FULL_POWER) { | ||
952 | lbs_pr_debug(1, | ||
953 | "EVENT: In FULL POWER mode - ignore PS SLEEP\n"); | ||
954 | break; | ||
955 | } | ||
956 | adapter->psstate = PS_STATE_PRE_SLEEP; | ||
957 | |||
958 | libertas_ps_confirm_sleep(priv, (u16) adapter->psmode); | ||
959 | |||
960 | break; | ||
961 | |||
962 | case MACREG_INT_CODE_PS_AWAKE: | ||
963 | lbs_pr_debug(1, "EVENT: AWAKE \n"); | ||
964 | lbs_pr_debug(1, "|"); | ||
965 | |||
966 | /* handle unexpected PS AWAKE event */ | ||
967 | if (adapter->psstate == PS_STATE_FULL_POWER) { | ||
968 | lbs_pr_debug(1, | ||
969 | "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); | ||
970 | break; | ||
971 | } | ||
972 | |||
973 | adapter->psstate = PS_STATE_AWAKE; | ||
974 | |||
975 | if (adapter->needtowakeup) { | ||
976 | /* | ||
977 | * wait for the command processing to finish | ||
978 | * before resuming sending | ||
979 | * adapter->needtowakeup will be set to FALSE | ||
980 | * in libertas_ps_wakeup() | ||
981 | */ | ||
982 | lbs_pr_debug(1, "Waking up...\n"); | ||
983 | libertas_ps_wakeup(priv, 0); | ||
984 | } | ||
985 | break; | ||
986 | |||
987 | case MACREG_INT_CODE_MIC_ERR_UNICAST: | ||
988 | lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n"); | ||
989 | handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); | ||
990 | break; | ||
991 | |||
992 | case MACREG_INT_CODE_MIC_ERR_MULTICAST: | ||
993 | lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n"); | ||
994 | handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); | ||
995 | break; | ||
996 | case MACREG_INT_CODE_MIB_CHANGED: | ||
997 | case MACREG_INT_CODE_INIT_DONE: | ||
998 | break; | ||
999 | |||
1000 | case MACREG_INT_CODE_ADHOC_BCN_LOST: | ||
1001 | lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n"); | ||
1002 | break; | ||
1003 | |||
1004 | case MACREG_INT_CODE_RSSI_LOW: | ||
1005 | lbs_pr_alert( "EVENT: RSSI_LOW\n"); | ||
1006 | break; | ||
1007 | case MACREG_INT_CODE_SNR_LOW: | ||
1008 | lbs_pr_alert( "EVENT: SNR_LOW\n"); | ||
1009 | break; | ||
1010 | case MACREG_INT_CODE_MAX_FAIL: | ||
1011 | lbs_pr_alert( "EVENT: MAX_FAIL\n"); | ||
1012 | break; | ||
1013 | case MACREG_INT_CODE_RSSI_HIGH: | ||
1014 | lbs_pr_alert( "EVENT: RSSI_HIGH\n"); | ||
1015 | break; | ||
1016 | case MACREG_INT_CODE_SNR_HIGH: | ||
1017 | lbs_pr_alert( "EVENT: SNR_HIGH\n"); | ||
1018 | break; | ||
1019 | |||
1020 | default: | ||
1021 | lbs_pr_alert( "EVENT: unknown event id: %#x\n", | ||
1022 | eventcause >> SBI_EVENT_CAUSE_SHIFT); | ||
1023 | break; | ||
1024 | } | ||
1025 | |||
1026 | spin_lock_irq(&adapter->driver_lock); | ||
1027 | adapter->eventcause = 0; | ||
1028 | spin_unlock_irq(&adapter->driver_lock); | ||
1029 | LEAVE(); | ||
1030 | return ret; | ||
1031 | } | ||
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c new file mode 100644 index 000000000000..3ad1e0339ed0 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.c | |||
@@ -0,0 +1,1968 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/dcache.h> | ||
3 | #include <linux/debugfs.h> | ||
4 | #include <linux/delay.h> | ||
5 | #include <linux/mm.h> | ||
6 | #include <net/iw_handler.h> | ||
7 | #include "dev.h" | ||
8 | #include "decl.h" | ||
9 | #include "host.h" | ||
10 | |||
11 | static struct dentry *libertas_dir = NULL; | ||
12 | static char *szStates[] = { | ||
13 | "Connected", | ||
14 | "Disconnected" | ||
15 | }; | ||
16 | |||
17 | void libertas_debug_init(wlan_private * priv, struct net_device *dev); | ||
18 | |||
19 | static int open_file_generic(struct inode *inode, struct file *file) | ||
20 | { | ||
21 | file->private_data = inode->i_private; | ||
22 | return 0; | ||
23 | } | ||
24 | |||
25 | static ssize_t write_file_dummy(struct file *file, const char __user *buf, | ||
26 | size_t count, loff_t *ppos) | ||
27 | { | ||
28 | return -EINVAL; | ||
29 | } | ||
30 | |||
31 | static const size_t len = PAGE_SIZE; | ||
32 | |||
33 | static ssize_t libertas_dev_info(struct file *file, char __user *userbuf, | ||
34 | size_t count, loff_t *ppos) | ||
35 | { | ||
36 | wlan_private *priv = file->private_data; | ||
37 | size_t pos = 0; | ||
38 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
39 | char *buf = (char *)addr; | ||
40 | ssize_t res; | ||
41 | |||
42 | pos += snprintf(buf+pos, len-pos, "state = %s\n", | ||
43 | szStates[priv->adapter->connect_status]); | ||
44 | pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", | ||
45 | (u32) priv->adapter->regioncode); | ||
46 | |||
47 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
48 | |||
49 | free_page(addr); | ||
50 | return res; | ||
51 | } | ||
52 | |||
53 | |||
54 | static ssize_t libertas_getscantable(struct file *file, char __user *userbuf, | ||
55 | size_t count, loff_t *ppos) | ||
56 | { | ||
57 | wlan_private *priv = file->private_data; | ||
58 | size_t pos = 0; | ||
59 | int numscansdone = 0, res; | ||
60 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
61 | char *buf = (char *)addr; | ||
62 | |||
63 | pos += snprintf(buf+pos, len-pos, | ||
64 | "---------------------------------------"); | ||
65 | pos += snprintf(buf+pos, len-pos, | ||
66 | "---------------------------------------\n"); | ||
67 | pos += snprintf(buf+pos, len-pos, | ||
68 | "# | ch | ss | bssid | cap | TSF | Qual | SSID \n"); | ||
69 | pos += snprintf(buf+pos, len-pos, | ||
70 | "---------------------------------------"); | ||
71 | pos += snprintf(buf+pos, len-pos, | ||
72 | "---------------------------------------\n"); | ||
73 | |||
74 | while (numscansdone < priv->adapter->numinscantable) { | ||
75 | struct bss_descriptor *pbssinfo; | ||
76 | u16 cap; | ||
77 | |||
78 | pbssinfo = &priv->adapter->scantable[numscansdone]; | ||
79 | memcpy(&cap, &pbssinfo->cap, sizeof(cap)); | ||
80 | pos += snprintf(buf+pos, len-pos, | ||
81 | "%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |", | ||
82 | numscansdone, pbssinfo->channel, pbssinfo->rssi, | ||
83 | pbssinfo->macaddress[0], pbssinfo->macaddress[1], | ||
84 | pbssinfo->macaddress[2], pbssinfo->macaddress[3], | ||
85 | pbssinfo->macaddress[4], pbssinfo->macaddress[5]); | ||
86 | pos += snprintf(buf+pos, len-pos, " %04x-", cap); | ||
87 | pos += snprintf(buf+pos, len-pos, "%c%c%c |", | ||
88 | pbssinfo->cap.ibss ? 'A' : 'I', | ||
89 | pbssinfo->cap.privacy ? 'P' : ' ', | ||
90 | pbssinfo->cap.spectrummgmt ? 'S' : ' '); | ||
91 | pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf); | ||
92 | pos += snprintf(buf+pos, len-pos, " %d |", | ||
93 | SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi)); | ||
94 | |||
95 | pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid); | ||
96 | |||
97 | numscansdone++; | ||
98 | } | ||
99 | |||
100 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
101 | |||
102 | free_page(addr); | ||
103 | return res; | ||
104 | } | ||
105 | |||
106 | static ssize_t libertas_sleepparams_write(struct file *file, | ||
107 | const char __user *user_buf, size_t count, | ||
108 | loff_t *ppos) | ||
109 | { | ||
110 | wlan_private *priv = file->private_data; | ||
111 | ssize_t buf_size, res; | ||
112 | int p1, p2, p3, p4, p5, p6; | ||
113 | struct sleep_params sp; | ||
114 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
115 | char *buf = (char *)addr; | ||
116 | |||
117 | buf_size = min(count, len - 1); | ||
118 | if (copy_from_user(buf, user_buf, buf_size)) { | ||
119 | res = -EFAULT; | ||
120 | goto out_unlock; | ||
121 | } | ||
122 | res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); | ||
123 | if (res != 6) { | ||
124 | res = -EFAULT; | ||
125 | goto out_unlock; | ||
126 | } | ||
127 | sp.sp_error = p1; | ||
128 | sp.sp_offset = p2; | ||
129 | sp.sp_stabletime = p3; | ||
130 | sp.sp_calcontrol = p4; | ||
131 | sp.sp_extsleepclk = p5; | ||
132 | sp.sp_reserved = p6; | ||
133 | |||
134 | memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params)); | ||
135 | |||
136 | res = libertas_prepare_and_send_command(priv, | ||
137 | cmd_802_11_sleep_params, | ||
138 | cmd_act_set, | ||
139 | cmd_option_waitforrsp, 0, NULL); | ||
140 | |||
141 | if (!res) | ||
142 | res = count; | ||
143 | else | ||
144 | res = -EINVAL; | ||
145 | |||
146 | out_unlock: | ||
147 | free_page(addr); | ||
148 | return res; | ||
149 | } | ||
150 | |||
151 | static ssize_t libertas_sleepparams_read(struct file *file, char __user *userbuf, | ||
152 | size_t count, loff_t *ppos) | ||
153 | { | ||
154 | wlan_private *priv = file->private_data; | ||
155 | wlan_adapter *adapter = priv->adapter; | ||
156 | ssize_t res; | ||
157 | size_t pos = 0; | ||
158 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
159 | char *buf = (char *)addr; | ||
160 | |||
161 | res = libertas_prepare_and_send_command(priv, | ||
162 | cmd_802_11_sleep_params, | ||
163 | cmd_act_get, | ||
164 | cmd_option_waitforrsp, 0, NULL); | ||
165 | if (res) { | ||
166 | res = -EFAULT; | ||
167 | goto out_unlock; | ||
168 | } | ||
169 | |||
170 | pos += snprintf(buf, len, "%d %d %d %d %d %d\n", adapter->sp.sp_error, | ||
171 | adapter->sp.sp_offset, adapter->sp.sp_stabletime, | ||
172 | adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk, | ||
173 | adapter->sp.sp_reserved); | ||
174 | |||
175 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
176 | |||
177 | out_unlock: | ||
178 | free_page(addr); | ||
179 | return res; | ||
180 | } | ||
181 | |||
182 | static ssize_t libertas_extscan(struct file *file, const char __user *userbuf, | ||
183 | size_t count, loff_t *ppos) | ||
184 | { | ||
185 | wlan_private *priv = file->private_data; | ||
186 | ssize_t res, buf_size; | ||
187 | struct WLAN_802_11_SSID extscan_ssid; | ||
188 | union iwreq_data wrqu; | ||
189 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
190 | char *buf = (char *)addr; | ||
191 | |||
192 | buf_size = min(count, len - 1); | ||
193 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
194 | res = -EFAULT; | ||
195 | goto out_unlock; | ||
196 | } | ||
197 | |||
198 | memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1); | ||
199 | extscan_ssid.ssidlength = strlen(buf)-1; | ||
200 | |||
201 | libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1); | ||
202 | |||
203 | memset(&wrqu, 0, sizeof(union iwreq_data)); | ||
204 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); | ||
205 | |||
206 | out_unlock: | ||
207 | free_page(addr); | ||
208 | return count; | ||
209 | } | ||
210 | |||
211 | static int libertas_parse_chan(char *buf, size_t count, | ||
212 | struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur) | ||
213 | { | ||
214 | char *start, *end, *hold, *str; | ||
215 | int i = 0; | ||
216 | |||
217 | start = strstr(buf, "chan="); | ||
218 | if (!start) | ||
219 | return -EINVAL; | ||
220 | start += 5; | ||
221 | end = strstr(start, " "); | ||
222 | if (!end) | ||
223 | end = buf + count; | ||
224 | hold = kzalloc((end - start)+1, GFP_KERNEL); | ||
225 | if (!hold) | ||
226 | return -ENOMEM; | ||
227 | strncpy(hold, start, end - start); | ||
228 | hold[(end-start)+1] = '\0'; | ||
229 | while(hold && (str = strsep(&hold, ","))) { | ||
230 | int chan; | ||
231 | char band, passive = 0; | ||
232 | sscanf(str, "%d%c%c", &chan, &band, &passive); | ||
233 | scan_cfg->chanlist[i].channumber = chan; | ||
234 | scan_cfg->chanlist[i].scantype = passive ? 1 : 0; | ||
235 | if (band == 'b' || band == 'g') | ||
236 | scan_cfg->chanlist[i].radiotype = 0; | ||
237 | else if (band == 'a') | ||
238 | scan_cfg->chanlist[i].radiotype = 1; | ||
239 | |||
240 | scan_cfg->chanlist[i].scantime = dur; | ||
241 | i++; | ||
242 | } | ||
243 | |||
244 | kfree(hold); | ||
245 | return i; | ||
246 | } | ||
247 | |||
248 | static void libertas_parse_bssid(char *buf, size_t count, | ||
249 | struct wlan_ioctl_user_scan_cfg *scan_cfg) | ||
250 | { | ||
251 | char *hold; | ||
252 | unsigned int mac[ETH_ALEN]; | ||
253 | int i; | ||
254 | |||
255 | hold = strstr(buf, "bssid="); | ||
256 | if (!hold) | ||
257 | return; | ||
258 | hold += 6; | ||
259 | sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3, | ||
260 | mac+4, mac+5); | ||
261 | for(i=0;i<ETH_ALEN;i++) | ||
262 | scan_cfg->specificBSSID[i] = mac[i]; | ||
263 | } | ||
264 | |||
265 | static void libertas_parse_ssid(char *buf, size_t count, | ||
266 | struct wlan_ioctl_user_scan_cfg *scan_cfg) | ||
267 | { | ||
268 | char *hold, *end; | ||
269 | ssize_t size; | ||
270 | |||
271 | hold = strstr(buf, "ssid="); | ||
272 | if (!hold) | ||
273 | return; | ||
274 | hold += 5; | ||
275 | end = strstr(hold, " "); | ||
276 | if (!end) | ||
277 | end = buf + count - 1; | ||
278 | |||
279 | size = min(IW_ESSID_MAX_SIZE, end - hold); | ||
280 | strncpy(scan_cfg->specificSSID, hold, size); | ||
281 | |||
282 | return; | ||
283 | } | ||
284 | |||
285 | static void libertas_parse_keep(char *buf, size_t count, | ||
286 | struct wlan_ioctl_user_scan_cfg *scan_cfg) | ||
287 | { | ||
288 | char *hold; | ||
289 | int val; | ||
290 | |||
291 | hold = strstr(buf, "keep="); | ||
292 | if (!hold) | ||
293 | return; | ||
294 | hold += 5; | ||
295 | sscanf(hold, "%d", &val); | ||
296 | |||
297 | if (val != 0) | ||
298 | val = 1; | ||
299 | |||
300 | scan_cfg->keeppreviousscan = val; | ||
301 | return; | ||
302 | } | ||
303 | |||
304 | static int libertas_parse_dur(char *buf, size_t count, | ||
305 | struct wlan_ioctl_user_scan_cfg *scan_cfg) | ||
306 | { | ||
307 | char *hold; | ||
308 | int val; | ||
309 | |||
310 | hold = strstr(buf, "dur="); | ||
311 | if (!hold) | ||
312 | return 0; | ||
313 | hold += 4; | ||
314 | sscanf(hold, "%d", &val); | ||
315 | |||
316 | return val; | ||
317 | } | ||
318 | |||
319 | static void libertas_parse_probes(char *buf, size_t count, | ||
320 | struct wlan_ioctl_user_scan_cfg *scan_cfg) | ||
321 | { | ||
322 | char *hold; | ||
323 | int val; | ||
324 | |||
325 | hold = strstr(buf, "probes="); | ||
326 | if (!hold) | ||
327 | return; | ||
328 | hold += 7; | ||
329 | sscanf(hold, "%d", &val); | ||
330 | |||
331 | scan_cfg->numprobes = val; | ||
332 | |||
333 | return; | ||
334 | } | ||
335 | |||
336 | static void libertas_parse_type(char *buf, size_t count, | ||
337 | struct wlan_ioctl_user_scan_cfg *scan_cfg) | ||
338 | { | ||
339 | char *hold; | ||
340 | int val; | ||
341 | |||
342 | hold = strstr(buf, "type="); | ||
343 | if (!hold) | ||
344 | return; | ||
345 | hold += 5; | ||
346 | sscanf(hold, "%d", &val); | ||
347 | |||
348 | /* type=1,2 or 3 */ | ||
349 | if (val < 1 || val > 3) | ||
350 | return; | ||
351 | |||
352 | scan_cfg->bsstype = val; | ||
353 | |||
354 | return; | ||
355 | } | ||
356 | |||
357 | static ssize_t libertas_setuserscan(struct file *file, | ||
358 | const char __user *userbuf, | ||
359 | size_t count, loff_t *ppos) | ||
360 | { | ||
361 | wlan_private *priv = file->private_data; | ||
362 | ssize_t res, buf_size; | ||
363 | struct wlan_ioctl_user_scan_cfg *scan_cfg; | ||
364 | union iwreq_data wrqu; | ||
365 | int dur; | ||
366 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
367 | char *buf = (char *)addr; | ||
368 | |||
369 | scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL); | ||
370 | if (!scan_cfg) | ||
371 | return -ENOMEM; | ||
372 | |||
373 | buf_size = min(count, len - 1); | ||
374 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
375 | res = -EFAULT; | ||
376 | goto out_unlock; | ||
377 | } | ||
378 | |||
379 | scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY; | ||
380 | |||
381 | dur = libertas_parse_dur(buf, count, scan_cfg); | ||
382 | libertas_parse_chan(buf, count, scan_cfg, dur); | ||
383 | libertas_parse_bssid(buf, count, scan_cfg); | ||
384 | libertas_parse_ssid(buf, count, scan_cfg); | ||
385 | libertas_parse_keep(buf, count, scan_cfg); | ||
386 | libertas_parse_probes(buf, count, scan_cfg); | ||
387 | libertas_parse_type(buf, count, scan_cfg); | ||
388 | |||
389 | wlan_scan_networks(priv, scan_cfg); | ||
390 | wait_event_interruptible(priv->adapter->cmd_pending, | ||
391 | !priv->adapter->nr_cmd_pending); | ||
392 | |||
393 | memset(&wrqu, 0x00, sizeof(union iwreq_data)); | ||
394 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); | ||
395 | |||
396 | out_unlock: | ||
397 | free_page(addr); | ||
398 | kfree(scan_cfg); | ||
399 | return count; | ||
400 | } | ||
401 | |||
402 | static int libertas_event_initcmd(wlan_private *priv, void **response_buf, | ||
403 | struct cmd_ctrl_node **cmdnode, | ||
404 | struct cmd_ds_command **cmd) | ||
405 | { | ||
406 | u16 wait_option = cmd_option_waitforrsp; | ||
407 | |||
408 | if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) { | ||
409 | lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n"); | ||
410 | return -ENOMEM; | ||
411 | } | ||
412 | if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) { | ||
413 | lbs_pr_debug(1, "failed to allocate response buffer!\n"); | ||
414 | return -ENOMEM; | ||
415 | } | ||
416 | libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL); | ||
417 | init_waitqueue_head(&(*cmdnode)->cmdwait_q); | ||
418 | (*cmdnode)->pdata_buf = *response_buf; | ||
419 | (*cmdnode)->cmdflags |= CMD_F_HOSTCMD; | ||
420 | (*cmdnode)->cmdwaitqwoken = 0; | ||
421 | *cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr; | ||
422 | (*cmd)->command = cmd_802_11_subscribe_event; | ||
423 | (*cmd)->seqnum = ++priv->adapter->seqnum; | ||
424 | (*cmd)->result = 0; | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf, | ||
429 | size_t count, loff_t *ppos) | ||
430 | { | ||
431 | wlan_private *priv = file->private_data; | ||
432 | wlan_adapter *adapter = priv->adapter; | ||
433 | struct cmd_ctrl_node *pcmdnode; | ||
434 | struct cmd_ds_command *pcmdptr; | ||
435 | struct cmd_ds_802_11_subscribe_event *event; | ||
436 | void *response_buf; | ||
437 | int res, cmd_len; | ||
438 | ssize_t pos = 0; | ||
439 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
440 | char *buf = (char *)addr; | ||
441 | |||
442 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
443 | if (res < 0) { | ||
444 | free_page(addr); | ||
445 | return res; | ||
446 | } | ||
447 | |||
448 | event = &pcmdptr->params.subscribe_event; | ||
449 | event->action = cmd_act_get; | ||
450 | pcmdptr->size = | ||
451 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
452 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
453 | wake_up_interruptible(&priv->mainthread.waitq); | ||
454 | |||
455 | /* Sleep until response is generated by FW */ | ||
456 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
457 | pcmdnode->cmdwaitqwoken); | ||
458 | |||
459 | pcmdptr = response_buf; | ||
460 | if (pcmdptr->result) { | ||
461 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
462 | pcmdptr->result); | ||
463 | kfree(response_buf); | ||
464 | free_page(addr); | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
469 | lbs_pr_err("command response incorrect!\n"); | ||
470 | kfree(response_buf); | ||
471 | free_page(addr); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
476 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
477 | while (cmd_len < pcmdptr->size) { | ||
478 | struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); | ||
479 | switch(header->type) { | ||
480 | struct mrvlietypes_rssithreshold *Lowrssi; | ||
481 | case TLV_TYPE_RSSI_LOW: | ||
482 | Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); | ||
483 | pos += snprintf(buf+pos, len-pos, "%d %d %d\n", | ||
484 | Lowrssi->rssivalue, | ||
485 | Lowrssi->rssifreq, | ||
486 | (event->events & 0x0001)?1:0); | ||
487 | default: | ||
488 | cmd_len += sizeof(struct mrvlietypes_snrthreshold); | ||
489 | break; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | kfree(response_buf); | ||
494 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
495 | free_page(addr); | ||
496 | return res; | ||
497 | } | ||
498 | |||
499 | static u16 libertas_get_events_bitmap(wlan_private *priv) | ||
500 | { | ||
501 | wlan_adapter *adapter = priv->adapter; | ||
502 | struct cmd_ctrl_node *pcmdnode; | ||
503 | struct cmd_ds_command *pcmdptr; | ||
504 | struct cmd_ds_802_11_subscribe_event *event; | ||
505 | void *response_buf; | ||
506 | int res; | ||
507 | u16 event_bitmap; | ||
508 | |||
509 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
510 | if (res < 0) | ||
511 | return res; | ||
512 | |||
513 | event = &pcmdptr->params.subscribe_event; | ||
514 | event->action = cmd_act_get; | ||
515 | pcmdptr->size = | ||
516 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
517 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
518 | wake_up_interruptible(&priv->mainthread.waitq); | ||
519 | |||
520 | /* Sleep until response is generated by FW */ | ||
521 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
522 | pcmdnode->cmdwaitqwoken); | ||
523 | |||
524 | pcmdptr = response_buf; | ||
525 | |||
526 | if (pcmdptr->result) { | ||
527 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
528 | pcmdptr->result); | ||
529 | kfree(response_buf); | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
534 | lbs_pr_err("command response incorrect!\n"); | ||
535 | kfree(response_buf); | ||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
540 | event_bitmap = event->events; | ||
541 | kfree(response_buf); | ||
542 | return event_bitmap; | ||
543 | } | ||
544 | |||
545 | static ssize_t libertas_lowrssi_write(struct file *file, | ||
546 | const char __user *userbuf, | ||
547 | size_t count, loff_t *ppos) | ||
548 | { | ||
549 | wlan_private *priv = file->private_data; | ||
550 | wlan_adapter *adapter = priv->adapter; | ||
551 | ssize_t res, buf_size; | ||
552 | int value, freq, subscribed, cmd_len; | ||
553 | struct cmd_ctrl_node *pcmdnode; | ||
554 | struct cmd_ds_command *pcmdptr; | ||
555 | struct cmd_ds_802_11_subscribe_event *event; | ||
556 | struct mrvlietypes_rssithreshold *rssi_threshold; | ||
557 | void *response_buf; | ||
558 | u16 event_bitmap; | ||
559 | u8 *ptr; | ||
560 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
561 | char *buf = (char *)addr; | ||
562 | |||
563 | buf_size = min(count, len - 1); | ||
564 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
565 | res = -EFAULT; | ||
566 | goto out_unlock; | ||
567 | } | ||
568 | res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); | ||
569 | if (res != 3) { | ||
570 | res = -EFAULT; | ||
571 | goto out_unlock; | ||
572 | } | ||
573 | |||
574 | event_bitmap = libertas_get_events_bitmap(priv); | ||
575 | |||
576 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
577 | if (res < 0) | ||
578 | goto out_unlock; | ||
579 | |||
580 | event = &pcmdptr->params.subscribe_event; | ||
581 | event->action = cmd_act_set; | ||
582 | pcmdptr->size = cpu_to_le16(S_DS_GEN + | ||
583 | sizeof(struct cmd_ds_802_11_subscribe_event) + | ||
584 | sizeof(struct mrvlietypes_rssithreshold)); | ||
585 | |||
586 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
587 | ptr = (u8*) pcmdptr+cmd_len; | ||
588 | rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); | ||
589 | rssi_threshold->header.type = cpu_to_le16(0x0104); | ||
590 | rssi_threshold->header.len = 2; | ||
591 | rssi_threshold->rssivalue = cpu_to_le16(value); | ||
592 | rssi_threshold->rssifreq = cpu_to_le16(freq); | ||
593 | event_bitmap |= subscribed ? 0x0001 : 0x0; | ||
594 | event->events = event_bitmap; | ||
595 | |||
596 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
597 | wake_up_interruptible(&priv->mainthread.waitq); | ||
598 | |||
599 | /* Sleep until response is generated by FW */ | ||
600 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
601 | pcmdnode->cmdwaitqwoken); | ||
602 | |||
603 | pcmdptr = response_buf; | ||
604 | |||
605 | if (pcmdptr->result) { | ||
606 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
607 | pcmdptr->result); | ||
608 | kfree(response_buf); | ||
609 | free_page(addr); | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
614 | lbs_pr_err("command response incorrect!\n"); | ||
615 | kfree(response_buf); | ||
616 | free_page(addr); | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | res = count; | ||
621 | out_unlock: | ||
622 | free_page(addr); | ||
623 | return res; | ||
624 | } | ||
625 | |||
626 | static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf, | ||
627 | size_t count, loff_t *ppos) | ||
628 | { | ||
629 | wlan_private *priv = file->private_data; | ||
630 | wlan_adapter *adapter = priv->adapter; | ||
631 | struct cmd_ctrl_node *pcmdnode; | ||
632 | struct cmd_ds_command *pcmdptr; | ||
633 | struct cmd_ds_802_11_subscribe_event *event; | ||
634 | void *response_buf; | ||
635 | int res, cmd_len; | ||
636 | ssize_t pos = 0; | ||
637 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
638 | char *buf = (char *)addr; | ||
639 | |||
640 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
641 | if (res < 0) { | ||
642 | free_page(addr); | ||
643 | return res; | ||
644 | } | ||
645 | |||
646 | event = &pcmdptr->params.subscribe_event; | ||
647 | event->action = cmd_act_get; | ||
648 | pcmdptr->size = | ||
649 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
650 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
651 | wake_up_interruptible(&priv->mainthread.waitq); | ||
652 | |||
653 | /* Sleep until response is generated by FW */ | ||
654 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
655 | pcmdnode->cmdwaitqwoken); | ||
656 | |||
657 | pcmdptr = response_buf; | ||
658 | |||
659 | if (pcmdptr->result) { | ||
660 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
661 | pcmdptr->result); | ||
662 | kfree(response_buf); | ||
663 | free_page(addr); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
668 | lbs_pr_err("command response incorrect!\n"); | ||
669 | kfree(response_buf); | ||
670 | free_page(addr); | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
675 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
676 | while (cmd_len < pcmdptr->size) { | ||
677 | struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); | ||
678 | switch(header->type) { | ||
679 | struct mrvlietypes_snrthreshold *LowSnr; | ||
680 | case TLV_TYPE_SNR_LOW: | ||
681 | LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); | ||
682 | pos += snprintf(buf+pos, len-pos, "%d %d %d\n", | ||
683 | LowSnr->snrvalue, | ||
684 | LowSnr->snrfreq, | ||
685 | (event->events & 0x0002)?1:0); | ||
686 | default: | ||
687 | cmd_len += sizeof(struct mrvlietypes_snrthreshold); | ||
688 | break; | ||
689 | } | ||
690 | } | ||
691 | |||
692 | kfree(response_buf); | ||
693 | |||
694 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
695 | free_page(addr); | ||
696 | return res; | ||
697 | } | ||
698 | |||
699 | static ssize_t libertas_lowsnr_write(struct file *file, | ||
700 | const char __user *userbuf, | ||
701 | size_t count, loff_t *ppos) | ||
702 | { | ||
703 | wlan_private *priv = file->private_data; | ||
704 | wlan_adapter *adapter = priv->adapter; | ||
705 | ssize_t res, buf_size; | ||
706 | int value, freq, subscribed, cmd_len; | ||
707 | struct cmd_ctrl_node *pcmdnode; | ||
708 | struct cmd_ds_command *pcmdptr; | ||
709 | struct cmd_ds_802_11_subscribe_event *event; | ||
710 | struct mrvlietypes_snrthreshold *snr_threshold; | ||
711 | void *response_buf; | ||
712 | u16 event_bitmap; | ||
713 | u8 *ptr; | ||
714 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
715 | char *buf = (char *)addr; | ||
716 | |||
717 | buf_size = min(count, len - 1); | ||
718 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
719 | res = -EFAULT; | ||
720 | goto out_unlock; | ||
721 | } | ||
722 | res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); | ||
723 | if (res != 3) { | ||
724 | res = -EFAULT; | ||
725 | goto out_unlock; | ||
726 | } | ||
727 | |||
728 | event_bitmap = libertas_get_events_bitmap(priv); | ||
729 | |||
730 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
731 | if (res < 0) | ||
732 | goto out_unlock; | ||
733 | |||
734 | event = &pcmdptr->params.subscribe_event; | ||
735 | event->action = cmd_act_set; | ||
736 | pcmdptr->size = cpu_to_le16(S_DS_GEN + | ||
737 | sizeof(struct cmd_ds_802_11_subscribe_event) + | ||
738 | sizeof(struct mrvlietypes_snrthreshold)); | ||
739 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
740 | ptr = (u8*) pcmdptr+cmd_len; | ||
741 | snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); | ||
742 | snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW); | ||
743 | snr_threshold->header.len = 2; | ||
744 | snr_threshold->snrvalue = cpu_to_le16(value); | ||
745 | snr_threshold->snrfreq = cpu_to_le16(freq); | ||
746 | event_bitmap |= subscribed ? 0x0002 : 0x0; | ||
747 | event->events = event_bitmap; | ||
748 | |||
749 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
750 | wake_up_interruptible(&priv->mainthread.waitq); | ||
751 | |||
752 | /* Sleep until response is generated by FW */ | ||
753 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
754 | pcmdnode->cmdwaitqwoken); | ||
755 | |||
756 | pcmdptr = response_buf; | ||
757 | |||
758 | if (pcmdptr->result) { | ||
759 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
760 | pcmdptr->result); | ||
761 | kfree(response_buf); | ||
762 | free_page(addr); | ||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
767 | lbs_pr_err("command response incorrect!\n"); | ||
768 | kfree(response_buf); | ||
769 | free_page(addr); | ||
770 | return 0; | ||
771 | } | ||
772 | |||
773 | res = count; | ||
774 | |||
775 | out_unlock: | ||
776 | free_page(addr); | ||
777 | return res; | ||
778 | } | ||
779 | |||
780 | static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf, | ||
781 | size_t count, loff_t *ppos) | ||
782 | { | ||
783 | wlan_private *priv = file->private_data; | ||
784 | wlan_adapter *adapter = priv->adapter; | ||
785 | struct cmd_ctrl_node *pcmdnode; | ||
786 | struct cmd_ds_command *pcmdptr; | ||
787 | struct cmd_ds_802_11_subscribe_event *event; | ||
788 | void *response_buf; | ||
789 | int res, cmd_len; | ||
790 | ssize_t pos = 0; | ||
791 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
792 | char *buf = (char *)addr; | ||
793 | |||
794 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
795 | if (res < 0) { | ||
796 | free_page(addr); | ||
797 | return res; | ||
798 | } | ||
799 | |||
800 | event = &pcmdptr->params.subscribe_event; | ||
801 | event->action = cmd_act_get; | ||
802 | pcmdptr->size = | ||
803 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
804 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
805 | wake_up_interruptible(&priv->mainthread.waitq); | ||
806 | |||
807 | /* Sleep until response is generated by FW */ | ||
808 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
809 | pcmdnode->cmdwaitqwoken); | ||
810 | |||
811 | pcmdptr = response_buf; | ||
812 | |||
813 | if (pcmdptr->result) { | ||
814 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
815 | pcmdptr->result); | ||
816 | kfree(response_buf); | ||
817 | free_page(addr); | ||
818 | return 0; | ||
819 | } | ||
820 | |||
821 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
822 | lbs_pr_err("command response incorrect!\n"); | ||
823 | kfree(response_buf); | ||
824 | free_page(addr); | ||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
829 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
830 | while (cmd_len < pcmdptr->size) { | ||
831 | struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); | ||
832 | switch(header->type) { | ||
833 | struct mrvlietypes_failurecount *failcount; | ||
834 | case TLV_TYPE_FAILCOUNT: | ||
835 | failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len); | ||
836 | pos += snprintf(buf+pos, len-pos, "%d %d %d\n", | ||
837 | failcount->failvalue, | ||
838 | failcount->Failfreq, | ||
839 | (event->events & 0x0004)?1:0); | ||
840 | default: | ||
841 | cmd_len += sizeof(struct mrvlietypes_failurecount); | ||
842 | break; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | kfree(response_buf); | ||
847 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
848 | free_page(addr); | ||
849 | return res; | ||
850 | } | ||
851 | |||
852 | static ssize_t libertas_failcount_write(struct file *file, | ||
853 | const char __user *userbuf, | ||
854 | size_t count, loff_t *ppos) | ||
855 | { | ||
856 | wlan_private *priv = file->private_data; | ||
857 | wlan_adapter *adapter = priv->adapter; | ||
858 | ssize_t res, buf_size; | ||
859 | int value, freq, subscribed, cmd_len; | ||
860 | struct cmd_ctrl_node *pcmdnode; | ||
861 | struct cmd_ds_command *pcmdptr; | ||
862 | struct cmd_ds_802_11_subscribe_event *event; | ||
863 | struct mrvlietypes_failurecount *failcount; | ||
864 | void *response_buf; | ||
865 | u16 event_bitmap; | ||
866 | u8 *ptr; | ||
867 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
868 | char *buf = (char *)addr; | ||
869 | |||
870 | buf_size = min(count, len - 1); | ||
871 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
872 | res = -EFAULT; | ||
873 | goto out_unlock; | ||
874 | } | ||
875 | res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); | ||
876 | if (res != 3) { | ||
877 | res = -EFAULT; | ||
878 | goto out_unlock; | ||
879 | } | ||
880 | |||
881 | event_bitmap = libertas_get_events_bitmap(priv); | ||
882 | |||
883 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
884 | if (res < 0) | ||
885 | goto out_unlock; | ||
886 | |||
887 | event = &pcmdptr->params.subscribe_event; | ||
888 | event->action = cmd_act_set; | ||
889 | pcmdptr->size = cpu_to_le16(S_DS_GEN + | ||
890 | sizeof(struct cmd_ds_802_11_subscribe_event) + | ||
891 | sizeof(struct mrvlietypes_failurecount)); | ||
892 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
893 | ptr = (u8*) pcmdptr+cmd_len; | ||
894 | failcount = (struct mrvlietypes_failurecount *)(ptr); | ||
895 | failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT); | ||
896 | failcount->header.len = 2; | ||
897 | failcount->failvalue = cpu_to_le16(value); | ||
898 | failcount->Failfreq = cpu_to_le16(freq); | ||
899 | event_bitmap |= subscribed ? 0x0004 : 0x0; | ||
900 | event->events = event_bitmap; | ||
901 | |||
902 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
903 | wake_up_interruptible(&priv->mainthread.waitq); | ||
904 | |||
905 | /* Sleep until response is generated by FW */ | ||
906 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
907 | pcmdnode->cmdwaitqwoken); | ||
908 | |||
909 | pcmdptr = (struct cmd_ds_command *)response_buf; | ||
910 | |||
911 | if (pcmdptr->result) { | ||
912 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
913 | pcmdptr->result); | ||
914 | kfree(response_buf); | ||
915 | free_page(addr); | ||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
920 | lbs_pr_err("command response incorrect!\n"); | ||
921 | kfree(response_buf); | ||
922 | free_page(addr); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | res = count; | ||
927 | out_unlock: | ||
928 | free_page(addr); | ||
929 | return res; | ||
930 | } | ||
931 | |||
932 | static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf, | ||
933 | size_t count, loff_t *ppos) | ||
934 | { | ||
935 | wlan_private *priv = file->private_data; | ||
936 | wlan_adapter *adapter = priv->adapter; | ||
937 | struct cmd_ctrl_node *pcmdnode; | ||
938 | struct cmd_ds_command *pcmdptr; | ||
939 | struct cmd_ds_802_11_subscribe_event *event; | ||
940 | void *response_buf; | ||
941 | int res, cmd_len; | ||
942 | ssize_t pos = 0; | ||
943 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
944 | char *buf = (char *)addr; | ||
945 | |||
946 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
947 | if (res < 0) { | ||
948 | free_page(addr); | ||
949 | return res; | ||
950 | } | ||
951 | |||
952 | event = &pcmdptr->params.subscribe_event; | ||
953 | event->action = cmd_act_get; | ||
954 | pcmdptr->size = | ||
955 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
956 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
957 | wake_up_interruptible(&priv->mainthread.waitq); | ||
958 | |||
959 | /* Sleep until response is generated by FW */ | ||
960 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
961 | pcmdnode->cmdwaitqwoken); | ||
962 | |||
963 | pcmdptr = response_buf; | ||
964 | |||
965 | if (pcmdptr->result) { | ||
966 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
967 | pcmdptr->result); | ||
968 | free_page(addr); | ||
969 | kfree(response_buf); | ||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
974 | lbs_pr_err("command response incorrect!\n"); | ||
975 | free_page(addr); | ||
976 | kfree(response_buf); | ||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
981 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
982 | while (cmd_len < pcmdptr->size) { | ||
983 | struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); | ||
984 | switch(header->type) { | ||
985 | struct mrvlietypes_beaconsmissed *bcnmiss; | ||
986 | case TLV_TYPE_BCNMISS: | ||
987 | bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len); | ||
988 | pos += snprintf(buf+pos, len-pos, "%d N/A %d\n", | ||
989 | bcnmiss->beaconmissed, | ||
990 | (event->events & 0x0008)?1:0); | ||
991 | default: | ||
992 | cmd_len += sizeof(struct mrvlietypes_beaconsmissed); | ||
993 | break; | ||
994 | } | ||
995 | } | ||
996 | |||
997 | kfree(response_buf); | ||
998 | |||
999 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
1000 | free_page(addr); | ||
1001 | return res; | ||
1002 | } | ||
1003 | |||
1004 | static ssize_t libertas_bcnmiss_write(struct file *file, | ||
1005 | const char __user *userbuf, | ||
1006 | size_t count, loff_t *ppos) | ||
1007 | { | ||
1008 | wlan_private *priv = file->private_data; | ||
1009 | wlan_adapter *adapter = priv->adapter; | ||
1010 | ssize_t res, buf_size; | ||
1011 | int value, freq, subscribed, cmd_len; | ||
1012 | struct cmd_ctrl_node *pcmdnode; | ||
1013 | struct cmd_ds_command *pcmdptr; | ||
1014 | struct cmd_ds_802_11_subscribe_event *event; | ||
1015 | struct mrvlietypes_beaconsmissed *bcnmiss; | ||
1016 | void *response_buf; | ||
1017 | u16 event_bitmap; | ||
1018 | u8 *ptr; | ||
1019 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1020 | char *buf = (char *)addr; | ||
1021 | |||
1022 | buf_size = min(count, len - 1); | ||
1023 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1024 | res = -EFAULT; | ||
1025 | goto out_unlock; | ||
1026 | } | ||
1027 | res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); | ||
1028 | if (res != 3) { | ||
1029 | res = -EFAULT; | ||
1030 | goto out_unlock; | ||
1031 | } | ||
1032 | |||
1033 | event_bitmap = libertas_get_events_bitmap(priv); | ||
1034 | |||
1035 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
1036 | if (res < 0) | ||
1037 | goto out_unlock; | ||
1038 | |||
1039 | event = &pcmdptr->params.subscribe_event; | ||
1040 | event->action = cmd_act_set; | ||
1041 | pcmdptr->size = cpu_to_le16(S_DS_GEN + | ||
1042 | sizeof(struct cmd_ds_802_11_subscribe_event) + | ||
1043 | sizeof(struct mrvlietypes_beaconsmissed)); | ||
1044 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
1045 | ptr = (u8*) pcmdptr+cmd_len; | ||
1046 | bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr); | ||
1047 | bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS); | ||
1048 | bcnmiss->header.len = 2; | ||
1049 | bcnmiss->beaconmissed = cpu_to_le16(value); | ||
1050 | event_bitmap |= subscribed ? 0x0008 : 0x0; | ||
1051 | event->events = event_bitmap; | ||
1052 | |||
1053 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
1054 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1055 | |||
1056 | /* Sleep until response is generated by FW */ | ||
1057 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
1058 | pcmdnode->cmdwaitqwoken); | ||
1059 | |||
1060 | pcmdptr = response_buf; | ||
1061 | |||
1062 | if (pcmdptr->result) { | ||
1063 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
1064 | pcmdptr->result); | ||
1065 | kfree(response_buf); | ||
1066 | free_page(addr); | ||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
1071 | lbs_pr_err("command response incorrect!\n"); | ||
1072 | free_page(addr); | ||
1073 | kfree(response_buf); | ||
1074 | return 0; | ||
1075 | } | ||
1076 | |||
1077 | res = count; | ||
1078 | out_unlock: | ||
1079 | free_page(addr); | ||
1080 | return res; | ||
1081 | } | ||
1082 | |||
1083 | static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf, | ||
1084 | size_t count, loff_t *ppos) | ||
1085 | { | ||
1086 | wlan_private *priv = file->private_data; | ||
1087 | wlan_adapter *adapter = priv->adapter; | ||
1088 | struct cmd_ctrl_node *pcmdnode; | ||
1089 | struct cmd_ds_command *pcmdptr; | ||
1090 | struct cmd_ds_802_11_subscribe_event *event; | ||
1091 | void *response_buf; | ||
1092 | int res, cmd_len; | ||
1093 | ssize_t pos = 0; | ||
1094 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1095 | char *buf = (char *)addr; | ||
1096 | |||
1097 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
1098 | if (res < 0) { | ||
1099 | free_page(addr); | ||
1100 | return res; | ||
1101 | } | ||
1102 | |||
1103 | event = &pcmdptr->params.subscribe_event; | ||
1104 | event->action = cmd_act_get; | ||
1105 | pcmdptr->size = | ||
1106 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
1107 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
1108 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1109 | |||
1110 | /* Sleep until response is generated by FW */ | ||
1111 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
1112 | pcmdnode->cmdwaitqwoken); | ||
1113 | |||
1114 | pcmdptr = response_buf; | ||
1115 | |||
1116 | if (pcmdptr->result) { | ||
1117 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
1118 | pcmdptr->result); | ||
1119 | kfree(response_buf); | ||
1120 | free_page(addr); | ||
1121 | return 0; | ||
1122 | } | ||
1123 | |||
1124 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
1125 | lbs_pr_err("command response incorrect!\n"); | ||
1126 | kfree(response_buf); | ||
1127 | free_page(addr); | ||
1128 | return 0; | ||
1129 | } | ||
1130 | |||
1131 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
1132 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
1133 | while (cmd_len < pcmdptr->size) { | ||
1134 | struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); | ||
1135 | switch(header->type) { | ||
1136 | struct mrvlietypes_rssithreshold *Highrssi; | ||
1137 | case TLV_TYPE_RSSI_HIGH: | ||
1138 | Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); | ||
1139 | pos += snprintf(buf+pos, len-pos, "%d %d %d\n", | ||
1140 | Highrssi->rssivalue, | ||
1141 | Highrssi->rssifreq, | ||
1142 | (event->events & 0x0010)?1:0); | ||
1143 | default: | ||
1144 | cmd_len += sizeof(struct mrvlietypes_snrthreshold); | ||
1145 | break; | ||
1146 | } | ||
1147 | } | ||
1148 | |||
1149 | kfree(response_buf); | ||
1150 | |||
1151 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
1152 | free_page(addr); | ||
1153 | return res; | ||
1154 | } | ||
1155 | |||
1156 | static ssize_t libertas_highrssi_write(struct file *file, | ||
1157 | const char __user *userbuf, | ||
1158 | size_t count, loff_t *ppos) | ||
1159 | { | ||
1160 | wlan_private *priv = file->private_data; | ||
1161 | wlan_adapter *adapter = priv->adapter; | ||
1162 | ssize_t res, buf_size; | ||
1163 | int value, freq, subscribed, cmd_len; | ||
1164 | struct cmd_ctrl_node *pcmdnode; | ||
1165 | struct cmd_ds_command *pcmdptr; | ||
1166 | struct cmd_ds_802_11_subscribe_event *event; | ||
1167 | struct mrvlietypes_rssithreshold *rssi_threshold; | ||
1168 | void *response_buf; | ||
1169 | u16 event_bitmap; | ||
1170 | u8 *ptr; | ||
1171 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1172 | char *buf = (char *)addr; | ||
1173 | |||
1174 | buf_size = min(count, len - 1); | ||
1175 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1176 | res = -EFAULT; | ||
1177 | goto out_unlock; | ||
1178 | } | ||
1179 | res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); | ||
1180 | if (res != 3) { | ||
1181 | res = -EFAULT; | ||
1182 | goto out_unlock; | ||
1183 | } | ||
1184 | |||
1185 | event_bitmap = libertas_get_events_bitmap(priv); | ||
1186 | |||
1187 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
1188 | if (res < 0) | ||
1189 | goto out_unlock; | ||
1190 | |||
1191 | event = &pcmdptr->params.subscribe_event; | ||
1192 | event->action = cmd_act_set; | ||
1193 | pcmdptr->size = cpu_to_le16(S_DS_GEN + | ||
1194 | sizeof(struct cmd_ds_802_11_subscribe_event) + | ||
1195 | sizeof(struct mrvlietypes_rssithreshold)); | ||
1196 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
1197 | ptr = (u8*) pcmdptr+cmd_len; | ||
1198 | rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); | ||
1199 | rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); | ||
1200 | rssi_threshold->header.len = 2; | ||
1201 | rssi_threshold->rssivalue = cpu_to_le16(value); | ||
1202 | rssi_threshold->rssifreq = cpu_to_le16(freq); | ||
1203 | event_bitmap |= subscribed ? 0x0010 : 0x0; | ||
1204 | event->events = event_bitmap; | ||
1205 | |||
1206 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
1207 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1208 | |||
1209 | /* Sleep until response is generated by FW */ | ||
1210 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
1211 | pcmdnode->cmdwaitqwoken); | ||
1212 | |||
1213 | pcmdptr = response_buf; | ||
1214 | |||
1215 | if (pcmdptr->result) { | ||
1216 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
1217 | pcmdptr->result); | ||
1218 | kfree(response_buf); | ||
1219 | return 0; | ||
1220 | } | ||
1221 | |||
1222 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
1223 | lbs_pr_err("command response incorrect!\n"); | ||
1224 | kfree(response_buf); | ||
1225 | return 0; | ||
1226 | } | ||
1227 | |||
1228 | res = count; | ||
1229 | out_unlock: | ||
1230 | free_page(addr); | ||
1231 | return res; | ||
1232 | } | ||
1233 | |||
1234 | static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf, | ||
1235 | size_t count, loff_t *ppos) | ||
1236 | { | ||
1237 | wlan_private *priv = file->private_data; | ||
1238 | wlan_adapter *adapter = priv->adapter; | ||
1239 | struct cmd_ctrl_node *pcmdnode; | ||
1240 | struct cmd_ds_command *pcmdptr; | ||
1241 | struct cmd_ds_802_11_subscribe_event *event; | ||
1242 | void *response_buf; | ||
1243 | int res, cmd_len; | ||
1244 | ssize_t pos = 0; | ||
1245 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1246 | char *buf = (char *)addr; | ||
1247 | |||
1248 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
1249 | if (res < 0) { | ||
1250 | free_page(addr); | ||
1251 | return res; | ||
1252 | } | ||
1253 | |||
1254 | event = &pcmdptr->params.subscribe_event; | ||
1255 | event->action = cmd_act_get; | ||
1256 | pcmdptr->size = | ||
1257 | cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); | ||
1258 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
1259 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1260 | |||
1261 | /* Sleep until response is generated by FW */ | ||
1262 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
1263 | pcmdnode->cmdwaitqwoken); | ||
1264 | |||
1265 | pcmdptr = response_buf; | ||
1266 | |||
1267 | if (pcmdptr->result) { | ||
1268 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
1269 | pcmdptr->result); | ||
1270 | kfree(response_buf); | ||
1271 | free_page(addr); | ||
1272 | return 0; | ||
1273 | } | ||
1274 | |||
1275 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
1276 | lbs_pr_err("command response incorrect!\n"); | ||
1277 | kfree(response_buf); | ||
1278 | free_page(addr); | ||
1279 | return 0; | ||
1280 | } | ||
1281 | |||
1282 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
1283 | event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); | ||
1284 | while (cmd_len < pcmdptr->size) { | ||
1285 | struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); | ||
1286 | switch(header->type) { | ||
1287 | struct mrvlietypes_snrthreshold *HighSnr; | ||
1288 | case TLV_TYPE_SNR_HIGH: | ||
1289 | HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); | ||
1290 | pos += snprintf(buf+pos, len-pos, "%d %d %d\n", | ||
1291 | HighSnr->snrvalue, | ||
1292 | HighSnr->snrfreq, | ||
1293 | (event->events & 0x0020)?1:0); | ||
1294 | default: | ||
1295 | cmd_len += sizeof(struct mrvlietypes_snrthreshold); | ||
1296 | break; | ||
1297 | } | ||
1298 | } | ||
1299 | |||
1300 | kfree(response_buf); | ||
1301 | |||
1302 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
1303 | free_page(addr); | ||
1304 | return res; | ||
1305 | } | ||
1306 | |||
1307 | static ssize_t libertas_highsnr_write(struct file *file, | ||
1308 | const char __user *userbuf, | ||
1309 | size_t count, loff_t *ppos) | ||
1310 | { | ||
1311 | wlan_private *priv = file->private_data; | ||
1312 | wlan_adapter *adapter = priv->adapter; | ||
1313 | ssize_t res, buf_size; | ||
1314 | int value, freq, subscribed, cmd_len; | ||
1315 | struct cmd_ctrl_node *pcmdnode; | ||
1316 | struct cmd_ds_command *pcmdptr; | ||
1317 | struct cmd_ds_802_11_subscribe_event *event; | ||
1318 | struct mrvlietypes_snrthreshold *snr_threshold; | ||
1319 | void *response_buf; | ||
1320 | u16 event_bitmap; | ||
1321 | u8 *ptr; | ||
1322 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1323 | char *buf = (char *)addr; | ||
1324 | |||
1325 | buf_size = min(count, len - 1); | ||
1326 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1327 | res = -EFAULT; | ||
1328 | goto out_unlock; | ||
1329 | } | ||
1330 | res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); | ||
1331 | if (res != 3) { | ||
1332 | res = -EFAULT; | ||
1333 | goto out_unlock; | ||
1334 | } | ||
1335 | |||
1336 | event_bitmap = libertas_get_events_bitmap(priv); | ||
1337 | |||
1338 | res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); | ||
1339 | if (res < 0) | ||
1340 | goto out_unlock; | ||
1341 | |||
1342 | event = &pcmdptr->params.subscribe_event; | ||
1343 | event->action = cmd_act_set; | ||
1344 | pcmdptr->size = cpu_to_le16(S_DS_GEN + | ||
1345 | sizeof(struct cmd_ds_802_11_subscribe_event) + | ||
1346 | sizeof(struct mrvlietypes_snrthreshold)); | ||
1347 | cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); | ||
1348 | ptr = (u8*) pcmdptr+cmd_len; | ||
1349 | snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); | ||
1350 | snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH); | ||
1351 | snr_threshold->header.len = 2; | ||
1352 | snr_threshold->snrvalue = cpu_to_le16(value); | ||
1353 | snr_threshold->snrfreq = cpu_to_le16(freq); | ||
1354 | event_bitmap |= subscribed ? 0x0020 : 0x0; | ||
1355 | event->events = event_bitmap; | ||
1356 | |||
1357 | libertas_queue_cmd(adapter, pcmdnode, 1); | ||
1358 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1359 | |||
1360 | /* Sleep until response is generated by FW */ | ||
1361 | wait_event_interruptible(pcmdnode->cmdwait_q, | ||
1362 | pcmdnode->cmdwaitqwoken); | ||
1363 | |||
1364 | pcmdptr = response_buf; | ||
1365 | |||
1366 | if (pcmdptr->result) { | ||
1367 | lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, | ||
1368 | pcmdptr->result); | ||
1369 | kfree(response_buf); | ||
1370 | free_page(addr); | ||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { | ||
1375 | lbs_pr_err("command response incorrect!\n"); | ||
1376 | kfree(response_buf); | ||
1377 | free_page(addr); | ||
1378 | return 0; | ||
1379 | } | ||
1380 | |||
1381 | res = count; | ||
1382 | out_unlock: | ||
1383 | free_page(addr); | ||
1384 | return res; | ||
1385 | } | ||
1386 | |||
1387 | static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf, | ||
1388 | size_t count, loff_t *ppos) | ||
1389 | { | ||
1390 | wlan_private *priv = file->private_data; | ||
1391 | wlan_adapter *adapter = priv->adapter; | ||
1392 | struct wlan_offset_value offval; | ||
1393 | ssize_t pos = 0; | ||
1394 | int ret; | ||
1395 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1396 | char *buf = (char *)addr; | ||
1397 | |||
1398 | offval.offset = priv->mac_offset; | ||
1399 | offval.value = 0; | ||
1400 | |||
1401 | ret = libertas_prepare_and_send_command(priv, | ||
1402 | cmd_mac_reg_access, 0, | ||
1403 | cmd_option_waitforrsp, 0, &offval); | ||
1404 | mdelay(10); | ||
1405 | pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", | ||
1406 | priv->mac_offset, adapter->offsetvalue.value); | ||
1407 | |||
1408 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
1409 | free_page(addr); | ||
1410 | return ret; | ||
1411 | } | ||
1412 | |||
1413 | static ssize_t libertas_rdmac_write(struct file *file, | ||
1414 | const char __user *userbuf, | ||
1415 | size_t count, loff_t *ppos) | ||
1416 | { | ||
1417 | wlan_private *priv = file->private_data; | ||
1418 | ssize_t res, buf_size; | ||
1419 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1420 | char *buf = (char *)addr; | ||
1421 | |||
1422 | buf_size = min(count, len - 1); | ||
1423 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1424 | res = -EFAULT; | ||
1425 | goto out_unlock; | ||
1426 | } | ||
1427 | priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); | ||
1428 | res = count; | ||
1429 | out_unlock: | ||
1430 | free_page(addr); | ||
1431 | return res; | ||
1432 | } | ||
1433 | |||
1434 | static ssize_t libertas_wrmac_write(struct file *file, | ||
1435 | const char __user *userbuf, | ||
1436 | size_t count, loff_t *ppos) | ||
1437 | { | ||
1438 | |||
1439 | wlan_private *priv = file->private_data; | ||
1440 | ssize_t res, buf_size; | ||
1441 | u32 offset, value; | ||
1442 | struct wlan_offset_value offval; | ||
1443 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1444 | char *buf = (char *)addr; | ||
1445 | |||
1446 | buf_size = min(count, len - 1); | ||
1447 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1448 | res = -EFAULT; | ||
1449 | goto out_unlock; | ||
1450 | } | ||
1451 | res = sscanf(buf, "%x %x", &offset, &value); | ||
1452 | if (res != 2) { | ||
1453 | res = -EFAULT; | ||
1454 | goto out_unlock; | ||
1455 | } | ||
1456 | |||
1457 | offval.offset = offset; | ||
1458 | offval.value = value; | ||
1459 | res = libertas_prepare_and_send_command(priv, | ||
1460 | cmd_mac_reg_access, 1, | ||
1461 | cmd_option_waitforrsp, 0, &offval); | ||
1462 | mdelay(10); | ||
1463 | |||
1464 | res = count; | ||
1465 | out_unlock: | ||
1466 | free_page(addr); | ||
1467 | return res; | ||
1468 | } | ||
1469 | |||
1470 | static ssize_t libertas_rdbbp_read(struct file *file, char __user *userbuf, | ||
1471 | size_t count, loff_t *ppos) | ||
1472 | { | ||
1473 | wlan_private *priv = file->private_data; | ||
1474 | wlan_adapter *adapter = priv->adapter; | ||
1475 | struct wlan_offset_value offval; | ||
1476 | ssize_t pos = 0; | ||
1477 | int ret; | ||
1478 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1479 | char *buf = (char *)addr; | ||
1480 | |||
1481 | offval.offset = priv->bbp_offset; | ||
1482 | offval.value = 0; | ||
1483 | |||
1484 | ret = libertas_prepare_and_send_command(priv, | ||
1485 | cmd_bbp_reg_access, 0, | ||
1486 | cmd_option_waitforrsp, 0, &offval); | ||
1487 | mdelay(10); | ||
1488 | pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", | ||
1489 | priv->bbp_offset, adapter->offsetvalue.value); | ||
1490 | |||
1491 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
1492 | free_page(addr); | ||
1493 | |||
1494 | return ret; | ||
1495 | } | ||
1496 | |||
1497 | static ssize_t libertas_rdbbp_write(struct file *file, | ||
1498 | const char __user *userbuf, | ||
1499 | size_t count, loff_t *ppos) | ||
1500 | { | ||
1501 | wlan_private *priv = file->private_data; | ||
1502 | ssize_t res, buf_size; | ||
1503 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1504 | char *buf = (char *)addr; | ||
1505 | |||
1506 | buf_size = min(count, len - 1); | ||
1507 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1508 | res = -EFAULT; | ||
1509 | goto out_unlock; | ||
1510 | } | ||
1511 | priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); | ||
1512 | res = count; | ||
1513 | out_unlock: | ||
1514 | free_page(addr); | ||
1515 | return res; | ||
1516 | } | ||
1517 | |||
1518 | static ssize_t libertas_wrbbp_write(struct file *file, | ||
1519 | const char __user *userbuf, | ||
1520 | size_t count, loff_t *ppos) | ||
1521 | { | ||
1522 | |||
1523 | wlan_private *priv = file->private_data; | ||
1524 | ssize_t res, buf_size; | ||
1525 | u32 offset, value; | ||
1526 | struct wlan_offset_value offval; | ||
1527 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1528 | char *buf = (char *)addr; | ||
1529 | |||
1530 | buf_size = min(count, len - 1); | ||
1531 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1532 | res = -EFAULT; | ||
1533 | goto out_unlock; | ||
1534 | } | ||
1535 | res = sscanf(buf, "%x %x", &offset, &value); | ||
1536 | if (res != 2) { | ||
1537 | res = -EFAULT; | ||
1538 | goto out_unlock; | ||
1539 | } | ||
1540 | |||
1541 | offval.offset = offset; | ||
1542 | offval.value = value; | ||
1543 | res = libertas_prepare_and_send_command(priv, | ||
1544 | cmd_bbp_reg_access, 1, | ||
1545 | cmd_option_waitforrsp, 0, &offval); | ||
1546 | mdelay(10); | ||
1547 | |||
1548 | res = count; | ||
1549 | out_unlock: | ||
1550 | free_page(addr); | ||
1551 | return res; | ||
1552 | } | ||
1553 | |||
1554 | static ssize_t libertas_rdrf_read(struct file *file, char __user *userbuf, | ||
1555 | size_t count, loff_t *ppos) | ||
1556 | { | ||
1557 | wlan_private *priv = file->private_data; | ||
1558 | wlan_adapter *adapter = priv->adapter; | ||
1559 | struct wlan_offset_value offval; | ||
1560 | ssize_t pos = 0; | ||
1561 | int ret; | ||
1562 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1563 | char *buf = (char *)addr; | ||
1564 | |||
1565 | offval.offset = priv->rf_offset; | ||
1566 | offval.value = 0; | ||
1567 | |||
1568 | ret = libertas_prepare_and_send_command(priv, | ||
1569 | cmd_rf_reg_access, 0, | ||
1570 | cmd_option_waitforrsp, 0, &offval); | ||
1571 | mdelay(10); | ||
1572 | pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", | ||
1573 | priv->rf_offset, adapter->offsetvalue.value); | ||
1574 | |||
1575 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
1576 | free_page(addr); | ||
1577 | |||
1578 | return ret; | ||
1579 | } | ||
1580 | |||
1581 | static ssize_t libertas_rdrf_write(struct file *file, | ||
1582 | const char __user *userbuf, | ||
1583 | size_t count, loff_t *ppos) | ||
1584 | { | ||
1585 | wlan_private *priv = file->private_data; | ||
1586 | ssize_t res, buf_size; | ||
1587 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1588 | char *buf = (char *)addr; | ||
1589 | |||
1590 | buf_size = min(count, len - 1); | ||
1591 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1592 | res = -EFAULT; | ||
1593 | goto out_unlock; | ||
1594 | } | ||
1595 | priv->rf_offset = simple_strtoul((char *)buf, NULL, 16); | ||
1596 | res = count; | ||
1597 | out_unlock: | ||
1598 | free_page(addr); | ||
1599 | return res; | ||
1600 | } | ||
1601 | |||
1602 | static ssize_t libertas_wrrf_write(struct file *file, | ||
1603 | const char __user *userbuf, | ||
1604 | size_t count, loff_t *ppos) | ||
1605 | { | ||
1606 | |||
1607 | wlan_private *priv = file->private_data; | ||
1608 | ssize_t res, buf_size; | ||
1609 | u32 offset, value; | ||
1610 | struct wlan_offset_value offval; | ||
1611 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1612 | char *buf = (char *)addr; | ||
1613 | |||
1614 | buf_size = min(count, len - 1); | ||
1615 | if (copy_from_user(buf, userbuf, buf_size)) { | ||
1616 | res = -EFAULT; | ||
1617 | goto out_unlock; | ||
1618 | } | ||
1619 | res = sscanf(buf, "%x %x", &offset, &value); | ||
1620 | if (res != 2) { | ||
1621 | res = -EFAULT; | ||
1622 | goto out_unlock; | ||
1623 | } | ||
1624 | |||
1625 | offval.offset = offset; | ||
1626 | offval.value = value; | ||
1627 | res = libertas_prepare_and_send_command(priv, | ||
1628 | cmd_rf_reg_access, 1, | ||
1629 | cmd_option_waitforrsp, 0, &offval); | ||
1630 | mdelay(10); | ||
1631 | |||
1632 | res = count; | ||
1633 | out_unlock: | ||
1634 | free_page(addr); | ||
1635 | return res; | ||
1636 | } | ||
1637 | |||
1638 | #define FOPS(fread, fwrite) { \ | ||
1639 | .owner = THIS_MODULE, \ | ||
1640 | .open = open_file_generic, \ | ||
1641 | .read = (fread), \ | ||
1642 | .write = (fwrite), \ | ||
1643 | } | ||
1644 | |||
1645 | struct libertas_debugfs_files { | ||
1646 | char *name; | ||
1647 | int perm; | ||
1648 | struct file_operations fops; | ||
1649 | }; | ||
1650 | |||
1651 | struct libertas_debugfs_files debugfs_files[] = { | ||
1652 | { "info", 0444, FOPS(libertas_dev_info, write_file_dummy), }, | ||
1653 | { "getscantable", 0444, FOPS(libertas_getscantable, | ||
1654 | write_file_dummy), }, | ||
1655 | { "sleepparams", 0644, FOPS(libertas_sleepparams_read, | ||
1656 | libertas_sleepparams_write), }, | ||
1657 | { "extscan", 0600, FOPS(NULL, libertas_extscan), }, | ||
1658 | { "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), }, | ||
1659 | }; | ||
1660 | |||
1661 | struct libertas_debugfs_files debugfs_events_files[] = { | ||
1662 | {"low_rssi", 0644, FOPS(libertas_lowrssi_read, | ||
1663 | libertas_lowrssi_write), }, | ||
1664 | {"low_snr", 0644, FOPS(libertas_lowsnr_read, | ||
1665 | libertas_lowsnr_write), }, | ||
1666 | {"failure_count", 0644, FOPS(libertas_failcount_read, | ||
1667 | libertas_failcount_write), }, | ||
1668 | {"beacon_missed", 0644, FOPS(libertas_bcnmiss_read, | ||
1669 | libertas_bcnmiss_write), }, | ||
1670 | {"high_rssi", 0644, FOPS(libertas_highrssi_read, | ||
1671 | libertas_highrssi_write), }, | ||
1672 | {"high_snr", 0644, FOPS(libertas_highsnr_read, | ||
1673 | libertas_highsnr_write), }, | ||
1674 | }; | ||
1675 | |||
1676 | struct libertas_debugfs_files debugfs_regs_files[] = { | ||
1677 | {"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), }, | ||
1678 | {"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), }, | ||
1679 | {"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), }, | ||
1680 | {"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), }, | ||
1681 | {"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), }, | ||
1682 | {"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), }, | ||
1683 | }; | ||
1684 | |||
1685 | void libertas_debugfs_init(void) | ||
1686 | { | ||
1687 | if (!libertas_dir) | ||
1688 | libertas_dir = debugfs_create_dir("libertas_wireless", NULL); | ||
1689 | |||
1690 | return; | ||
1691 | } | ||
1692 | |||
1693 | void libertas_debugfs_remove(void) | ||
1694 | { | ||
1695 | if (libertas_dir) | ||
1696 | debugfs_remove(libertas_dir); | ||
1697 | return; | ||
1698 | } | ||
1699 | |||
1700 | void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev) | ||
1701 | { | ||
1702 | int i; | ||
1703 | struct libertas_debugfs_files *files; | ||
1704 | if (!libertas_dir) | ||
1705 | goto exit; | ||
1706 | |||
1707 | priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_dir); | ||
1708 | if (!priv->debugfs_dir) | ||
1709 | goto exit; | ||
1710 | |||
1711 | for (i=0; i<ARRAY_SIZE(debugfs_files); i++) { | ||
1712 | files = &debugfs_files[i]; | ||
1713 | priv->debugfs_files[i] = debugfs_create_file(files->name, | ||
1714 | files->perm, | ||
1715 | priv->debugfs_dir, | ||
1716 | priv, | ||
1717 | &files->fops); | ||
1718 | } | ||
1719 | |||
1720 | priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); | ||
1721 | if (!priv->events_dir) | ||
1722 | goto exit; | ||
1723 | |||
1724 | for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) { | ||
1725 | files = &debugfs_events_files[i]; | ||
1726 | priv->debugfs_events_files[i] = debugfs_create_file(files->name, | ||
1727 | files->perm, | ||
1728 | priv->events_dir, | ||
1729 | priv, | ||
1730 | &files->fops); | ||
1731 | } | ||
1732 | |||
1733 | priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); | ||
1734 | if (!priv->regs_dir) | ||
1735 | goto exit; | ||
1736 | |||
1737 | for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) { | ||
1738 | files = &debugfs_regs_files[i]; | ||
1739 | priv->debugfs_regs_files[i] = debugfs_create_file(files->name, | ||
1740 | files->perm, | ||
1741 | priv->regs_dir, | ||
1742 | priv, | ||
1743 | &files->fops); | ||
1744 | } | ||
1745 | |||
1746 | #ifdef PROC_DEBUG | ||
1747 | libertas_debug_init(priv, dev); | ||
1748 | #endif | ||
1749 | exit: | ||
1750 | return; | ||
1751 | } | ||
1752 | |||
1753 | void libertas_debugfs_remove_one(wlan_private *priv) | ||
1754 | { | ||
1755 | int i; | ||
1756 | |||
1757 | for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) | ||
1758 | debugfs_remove(priv->debugfs_regs_files[i]); | ||
1759 | |||
1760 | debugfs_remove(priv->regs_dir); | ||
1761 | |||
1762 | for(i=0; i<ARRAY_SIZE(debugfs_files); i++) | ||
1763 | debugfs_remove(priv->debugfs_events_files[i]); | ||
1764 | |||
1765 | debugfs_remove(priv->events_dir); | ||
1766 | #ifdef PROC_DEBUG | ||
1767 | debugfs_remove(priv->debugfs_debug); | ||
1768 | #endif | ||
1769 | for(i=0; i<ARRAY_SIZE(debugfs_files); i++) | ||
1770 | debugfs_remove(priv->debugfs_files[i]); | ||
1771 | } | ||
1772 | |||
1773 | /* debug entry */ | ||
1774 | |||
1775 | #define item_size(n) (sizeof ((wlan_adapter *)0)->n) | ||
1776 | #define item_addr(n) ((u32) &((wlan_adapter *)0)->n) | ||
1777 | |||
1778 | struct debug_data { | ||
1779 | char name[32]; | ||
1780 | u32 size; | ||
1781 | u32 addr; | ||
1782 | }; | ||
1783 | |||
1784 | /* To debug any member of wlan_adapter, simply add one line here. | ||
1785 | */ | ||
1786 | static struct debug_data items[] = { | ||
1787 | {"intcounter", item_size(intcounter), item_addr(intcounter)}, | ||
1788 | {"psmode", item_size(psmode), item_addr(psmode)}, | ||
1789 | {"psstate", item_size(psstate), item_addr(psstate)}, | ||
1790 | }; | ||
1791 | |||
1792 | static int num_of_items = sizeof(items) / sizeof(items[0]); | ||
1793 | |||
1794 | /** | ||
1795 | * @brief convert string to number | ||
1796 | * | ||
1797 | * @param s pointer to numbered string | ||
1798 | * @return converted number from string s | ||
1799 | */ | ||
1800 | static int string_to_number(char *s) | ||
1801 | { | ||
1802 | int r = 0; | ||
1803 | int base = 0; | ||
1804 | |||
1805 | if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) | ||
1806 | base = 16; | ||
1807 | else | ||
1808 | base = 10; | ||
1809 | |||
1810 | if (base == 16) | ||
1811 | s += 2; | ||
1812 | |||
1813 | for (s = s; *s != 0; s++) { | ||
1814 | if ((*s >= 48) && (*s <= 57)) | ||
1815 | r = (r * base) + (*s - 48); | ||
1816 | else if ((*s >= 65) && (*s <= 70)) | ||
1817 | r = (r * base) + (*s - 55); | ||
1818 | else if ((*s >= 97) && (*s <= 102)) | ||
1819 | r = (r * base) + (*s - 87); | ||
1820 | else | ||
1821 | break; | ||
1822 | } | ||
1823 | |||
1824 | return r; | ||
1825 | } | ||
1826 | |||
1827 | /** | ||
1828 | * @brief proc read function | ||
1829 | * | ||
1830 | * @param page pointer to buffer | ||
1831 | * @param s read data starting position | ||
1832 | * @param off offset | ||
1833 | * @param cnt counter | ||
1834 | * @param eof end of file flag | ||
1835 | * @param data data to output | ||
1836 | * @return number of output data | ||
1837 | */ | ||
1838 | static ssize_t wlan_debugfs_read(struct file *file, char __user *userbuf, | ||
1839 | size_t count, loff_t *ppos) | ||
1840 | { | ||
1841 | int val = 0; | ||
1842 | size_t pos = 0; | ||
1843 | ssize_t res; | ||
1844 | char *p; | ||
1845 | int i; | ||
1846 | struct debug_data *d; | ||
1847 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
1848 | char *buf = (char *)addr; | ||
1849 | |||
1850 | p = buf; | ||
1851 | |||
1852 | d = (struct debug_data *)file->private_data; | ||
1853 | |||
1854 | for (i = 0; i < num_of_items; i++) { | ||
1855 | if (d[i].size == 1) | ||
1856 | val = *((u8 *) d[i].addr); | ||
1857 | else if (d[i].size == 2) | ||
1858 | val = *((u16 *) d[i].addr); | ||
1859 | else if (d[i].size == 4) | ||
1860 | val = *((u32 *) d[i].addr); | ||
1861 | |||
1862 | pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); | ||
1863 | } | ||
1864 | |||
1865 | res = simple_read_from_buffer(userbuf, count, ppos, p, pos); | ||
1866 | |||
1867 | free_page(addr); | ||
1868 | return res; | ||
1869 | } | ||
1870 | |||
1871 | /** | ||
1872 | * @brief proc write function | ||
1873 | * | ||
1874 | * @param f file pointer | ||
1875 | * @param buf pointer to data buffer | ||
1876 | * @param cnt data number to write | ||
1877 | * @param data data to write | ||
1878 | * @return number of data | ||
1879 | */ | ||
1880 | static int wlan_debugfs_write(struct file *f, const char __user *buf, | ||
1881 | size_t cnt, loff_t *ppos) | ||
1882 | { | ||
1883 | int r, i; | ||
1884 | char *pdata; | ||
1885 | char *p; | ||
1886 | char *p0; | ||
1887 | char *p1; | ||
1888 | char *p2; | ||
1889 | struct debug_data *d = (struct debug_data *)f->private_data; | ||
1890 | |||
1891 | pdata = (char *)kmalloc(cnt, GFP_KERNEL); | ||
1892 | if (pdata == NULL) | ||
1893 | return 0; | ||
1894 | |||
1895 | if (copy_from_user(pdata, buf, cnt)) { | ||
1896 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
1897 | kfree(pdata); | ||
1898 | return 0; | ||
1899 | } | ||
1900 | |||
1901 | p0 = pdata; | ||
1902 | for (i = 0; i < num_of_items; i++) { | ||
1903 | do { | ||
1904 | p = strstr(p0, d[i].name); | ||
1905 | if (p == NULL) | ||
1906 | break; | ||
1907 | p1 = strchr(p, '\n'); | ||
1908 | if (p1 == NULL) | ||
1909 | break; | ||
1910 | p0 = p1++; | ||
1911 | p2 = strchr(p, '='); | ||
1912 | if (!p2) | ||
1913 | break; | ||
1914 | p2++; | ||
1915 | r = string_to_number(p2); | ||
1916 | if (d[i].size == 1) | ||
1917 | *((u8 *) d[i].addr) = (u8) r; | ||
1918 | else if (d[i].size == 2) | ||
1919 | *((u16 *) d[i].addr) = (u16) r; | ||
1920 | else if (d[i].size == 4) | ||
1921 | *((u32 *) d[i].addr) = (u32) r; | ||
1922 | break; | ||
1923 | } while (1); | ||
1924 | } | ||
1925 | kfree(pdata); | ||
1926 | |||
1927 | return cnt; | ||
1928 | } | ||
1929 | |||
1930 | static struct file_operations libertas_debug_fops = { | ||
1931 | .owner = THIS_MODULE, | ||
1932 | .open = open_file_generic, | ||
1933 | .write = wlan_debugfs_write, | ||
1934 | .read = wlan_debugfs_read, | ||
1935 | }; | ||
1936 | |||
1937 | /** | ||
1938 | * @brief create debug proc file | ||
1939 | * | ||
1940 | * @param priv pointer wlan_private | ||
1941 | * @param dev pointer net_device | ||
1942 | * @return N/A | ||
1943 | */ | ||
1944 | void libertas_debug_init(wlan_private * priv, struct net_device *dev) | ||
1945 | { | ||
1946 | int i; | ||
1947 | |||
1948 | if (!priv->debugfs_dir) | ||
1949 | return; | ||
1950 | |||
1951 | for (i = 0; i < num_of_items; i++) | ||
1952 | items[i].addr += (u32) priv->adapter; | ||
1953 | |||
1954 | priv->debugfs_debug = debugfs_create_file("debug", 0644, | ||
1955 | priv->debugfs_dir, &items[0], | ||
1956 | &libertas_debug_fops); | ||
1957 | } | ||
1958 | |||
1959 | /** | ||
1960 | * @brief remove proc file | ||
1961 | * | ||
1962 | * @param priv pointer wlan_private | ||
1963 | * @return N/A | ||
1964 | */ | ||
1965 | void libertas_debug_remove(wlan_private * priv) | ||
1966 | { | ||
1967 | debugfs_remove(priv->debugfs_debug); | ||
1968 | } | ||
diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h new file mode 100644 index 000000000000..880a11b95d26 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.h | |||
@@ -0,0 +1,6 @@ | |||
1 | void libertas_debugfs_init(void); | ||
2 | void libertas_debugfs_remove(void); | ||
3 | |||
4 | void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev); | ||
5 | void libertas_debugfs_remove_one(wlan_private *priv); | ||
6 | |||
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h new file mode 100644 index 000000000000..606bdd002be7 --- /dev/null +++ b/drivers/net/wireless/libertas/decl.h | |||
@@ -0,0 +1,83 @@ | |||
1 | /** | ||
2 | * This file contains declaration referring to | ||
3 | * functions defined in other source files | ||
4 | */ | ||
5 | |||
6 | #ifndef _WLAN_DECL_H_ | ||
7 | #define _WLAN_DECL_H_ | ||
8 | |||
9 | #include "defs.h" | ||
10 | |||
11 | /** Function Prototype Declaration */ | ||
12 | struct wlan_private; | ||
13 | struct sk_buff; | ||
14 | struct net_device; | ||
15 | |||
16 | extern char *libertas_fw_name; | ||
17 | |||
18 | void libertas_free_adapter(wlan_private * priv); | ||
19 | int libertas_set_mac_packet_filter(wlan_private * priv); | ||
20 | |||
21 | int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt); | ||
22 | void libertas_send_tx_feedback(wlan_private * priv); | ||
23 | u8 libertas_check_last_packet_indication(wlan_private * priv); | ||
24 | |||
25 | int libertas_free_cmd_buffer(wlan_private * priv); | ||
26 | struct cmd_ctrl_node; | ||
27 | struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv); | ||
28 | |||
29 | void libertas_set_cmd_ctrl_node(wlan_private * priv, | ||
30 | struct cmd_ctrl_node *ptempnode, | ||
31 | u32 cmd_oid, u16 wait_option, void *pdata_buf); | ||
32 | |||
33 | int libertas_prepare_and_send_command(wlan_private * priv, | ||
34 | u16 cmd_no, | ||
35 | u16 cmd_action, | ||
36 | u16 wait_option, u32 cmd_oid, void *pdata_buf); | ||
37 | |||
38 | void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail); | ||
39 | |||
40 | int libertas_allocate_cmd_buffer(wlan_private * priv); | ||
41 | int libertas_execute_next_command(wlan_private * priv); | ||
42 | int libertas_process_event(wlan_private * priv); | ||
43 | void libertas_interrupt(struct net_device *); | ||
44 | int libertas_set_radio_control(wlan_private * priv); | ||
45 | u32 libertas_index_to_data_rate(u8 index); | ||
46 | u8 libertas_data_rate_to_index(u32 rate); | ||
47 | void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen); | ||
48 | |||
49 | int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb); | ||
50 | |||
51 | /** The proc fs interface */ | ||
52 | int libertas_process_rx_command(wlan_private * priv); | ||
53 | int libertas_process_tx(wlan_private * priv, struct sk_buff *skb); | ||
54 | void libertas_cleanup_and_insert_cmd(wlan_private * priv, | ||
55 | struct cmd_ctrl_node *ptempcmd); | ||
56 | void __libertas_cleanup_and_insert_cmd(wlan_private * priv, | ||
57 | struct cmd_ctrl_node *ptempcmd); | ||
58 | |||
59 | int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band); | ||
60 | |||
61 | int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *); | ||
62 | |||
63 | void libertas_ps_sleep(wlan_private * priv, int wait_option); | ||
64 | void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode); | ||
65 | void libertas_ps_wakeup(wlan_private * priv, int wait_option); | ||
66 | |||
67 | void libertas_tx_runqueue(wlan_private *priv); | ||
68 | |||
69 | extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel( | ||
70 | wlan_adapter * adapter, u8 band, u16 channel); | ||
71 | |||
72 | extern void libertas_mac_event_disconnected(wlan_private * priv); | ||
73 | |||
74 | void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str); | ||
75 | |||
76 | int reset_device(wlan_private *priv); | ||
77 | /* main.c */ | ||
78 | extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, | ||
79 | int *cfp_no); | ||
80 | wlan_private *wlan_add_card(void *card); | ||
81 | int wlan_remove_card(void *card); | ||
82 | |||
83 | #endif /* _WLAN_DECL_H_ */ | ||
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h new file mode 100644 index 000000000000..fb1478c1b87d --- /dev/null +++ b/drivers/net/wireless/libertas/defs.h | |||
@@ -0,0 +1,369 @@ | |||
1 | /** | ||
2 | * This header file contains global constant/enum definitions, | ||
3 | * global variable declaration. | ||
4 | */ | ||
5 | #ifndef _WLAN_DEFS_H_ | ||
6 | #define _WLAN_DEFS_H_ | ||
7 | |||
8 | #include <linux/spinlock.h> | ||
9 | |||
10 | extern unsigned int libertas_debug; | ||
11 | |||
12 | #define DRV_NAME "usb8xxx" | ||
13 | |||
14 | #define lbs_pr_info(format, args...) \ | ||
15 | printk(KERN_INFO DRV_NAME": " format, ## args) | ||
16 | #define lbs_pr_err(format, args...) \ | ||
17 | printk(KERN_ERR DRV_NAME": " format, ## args) | ||
18 | #define lbs_pr_alert(format, args...) \ | ||
19 | printk(KERN_ALERT DRV_NAME": " format, ## args) | ||
20 | |||
21 | #ifdef DEBUG | ||
22 | #define lbs_pr_debug(level, format, args...) \ | ||
23 | do { if (libertas_debug >= level) \ | ||
24 | printk(KERN_INFO DRV_NAME": " format, ##args); } while (0) | ||
25 | #define lbs_dev_dbg(level, device, format, args...) \ | ||
26 | lbs_pr_debug(level, "%s: " format, \ | ||
27 | (device)->bus_id , ## args) | ||
28 | |||
29 | static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len) | ||
30 | { | ||
31 | int i = 0; | ||
32 | |||
33 | if (!libertas_debug) | ||
34 | return; | ||
35 | |||
36 | printk(KERN_DEBUG "%s: ", prompt); | ||
37 | for (i = 1; i <= len; i++) { | ||
38 | printk(KERN_DEBUG "%02x ", (u8) * buf); | ||
39 | buf++; | ||
40 | } | ||
41 | printk("\n"); | ||
42 | } | ||
43 | #else | ||
44 | #define lbs_pr_debug(level, format, args...) do {} while (0) | ||
45 | #define lbs_dev_dbg(level, device, format, args...) do {} while (0) | ||
46 | #define lbs_dbg_hex(x,y,z) do {} while (0) | ||
47 | #endif | ||
48 | |||
49 | #define ENTER() lbs_pr_debug(1, "Enter: %s, %s:%i\n", \ | ||
50 | __FUNCTION__, __FILE__, __LINE__) | ||
51 | #define LEAVE() lbs_pr_debug(1, "Leave: %s, %s:%i\n", \ | ||
52 | __FUNCTION__, __FILE__, __LINE__) | ||
53 | |||
54 | /** Buffer Constants */ | ||
55 | |||
56 | /* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical | ||
57 | * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas | ||
58 | * driver has more local TxPDs. Each TxPD on the host memory is associated | ||
59 | * with a Tx control node. The driver maintains 8 RxPD descriptors for | ||
60 | * station firmware to store Rx packet information. | ||
61 | * | ||
62 | * Current version of MAC has a 32x6 multicast address buffer. | ||
63 | * | ||
64 | * 802.11b can have up to 14 channels, the driver keeps the | ||
65 | * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. | ||
66 | */ | ||
67 | |||
68 | #define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 | ||
69 | #define MRVDRV_NUM_OF_CMD_BUFFER 10 | ||
70 | #define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) | ||
71 | #define MRVDRV_MAX_CHANNEL_SIZE 14 | ||
72 | #define MRVDRV_MAX_BSSID_LIST 64 | ||
73 | #define MRVDRV_ASSOCIATION_TIME_OUT 255 | ||
74 | #define MRVDRV_SNAP_HEADER_LEN 8 | ||
75 | |||
76 | #define WLAN_UPLD_SIZE 2312 | ||
77 | #define DEV_NAME_LEN 32 | ||
78 | |||
79 | /** Misc constants */ | ||
80 | /* This section defines 802.11 specific contants */ | ||
81 | |||
82 | #define MRVDRV_MAX_BSS_DESCRIPTS 16 | ||
83 | #define MRVDRV_MAX_REGION_CODE 6 | ||
84 | |||
85 | #define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe | ||
86 | #define MRVDRV_MIN_MULTIPLE_DTIM 1 | ||
87 | #define MRVDRV_MAX_MULTIPLE_DTIM 5 | ||
88 | #define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 | ||
89 | |||
90 | #define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 | ||
91 | |||
92 | #define MRVDRV_CHANNELS_PER_SCAN 4 | ||
93 | #define MRVDRV_MAX_CHANNELS_PER_SCAN 14 | ||
94 | |||
95 | #define MRVDRV_DEBUG_RX_PATH 0x00000001 | ||
96 | #define MRVDRV_DEBUG_TX_PATH 0x00000002 | ||
97 | |||
98 | #define MRVDRV_MIN_BEACON_INTERVAL 20 | ||
99 | #define MRVDRV_MAX_BEACON_INTERVAL 1000 | ||
100 | #define MRVDRV_BEACON_INTERVAL 100 | ||
101 | |||
102 | /** TxPD status */ | ||
103 | |||
104 | /* Station firmware use TxPD status field to report final Tx transmit | ||
105 | * result, Bit masks are used to present combined situations. | ||
106 | */ | ||
107 | |||
108 | #define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 | ||
109 | #define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 | ||
110 | |||
111 | /** Tx mesh flag */ | ||
112 | /* Currently we are using normal WDS flag as mesh flag. | ||
113 | * TODO: change to proper mesh flag when MAC understands it. | ||
114 | */ | ||
115 | #define TxPD_CONTROL_WDS_FRAME (1<<17) | ||
116 | #define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME | ||
117 | |||
118 | /** RxPD status */ | ||
119 | |||
120 | #define MRVDRV_RXPD_STATUS_OK 0x0001 | ||
121 | |||
122 | /** RxPD status - Received packet types */ | ||
123 | /** Rx mesh flag */ | ||
124 | /* Currently we are using normal WDS flag as mesh flag. | ||
125 | * TODO: change to proper mesh flag when MAC understands it. | ||
126 | */ | ||
127 | #define RxPD_CONTROL_WDS_FRAME (0x40) | ||
128 | #define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME | ||
129 | |||
130 | /** RSSI-related defines */ | ||
131 | /* RSSI constants are used to implement 802.11 RSSI threshold | ||
132 | * indication. if the Rx packet signal got too weak for 5 consecutive | ||
133 | * times, miniport driver (driver) will report this event to wrapper | ||
134 | */ | ||
135 | |||
136 | #define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) | ||
137 | |||
138 | /** RTS/FRAG related defines */ | ||
139 | #define MRVDRV_RTS_MIN_VALUE 0 | ||
140 | #define MRVDRV_RTS_MAX_VALUE 2347 | ||
141 | #define MRVDRV_FRAG_MIN_VALUE 256 | ||
142 | #define MRVDRV_FRAG_MAX_VALUE 2346 | ||
143 | |||
144 | /* This is for firmware specific length */ | ||
145 | #define EXTRA_LEN 36 | ||
146 | |||
147 | #define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ | ||
148 | (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) | ||
149 | |||
150 | #define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ | ||
151 | (ETH_FRAME_LEN + sizeof(struct rxpd) \ | ||
152 | + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) | ||
153 | |||
154 | #define CMD_F_HOSTCMD (1 << 0) | ||
155 | #define FW_CAPINFO_WPA (1 << 0) | ||
156 | |||
157 | /** WPA key LENGTH*/ | ||
158 | #define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 | ||
159 | |||
160 | #define KEY_LEN_WPA_AES 16 | ||
161 | #define KEY_LEN_WPA_TKIP 32 | ||
162 | #define KEY_LEN_WEP_104 13 | ||
163 | #define KEY_LEN_WEP_40 5 | ||
164 | |||
165 | #define RF_ANTENNA_1 0x1 | ||
166 | #define RF_ANTENNA_2 0x2 | ||
167 | #define RF_ANTENNA_AUTO 0xFFFF | ||
168 | |||
169 | #define BAND_B (0x01) | ||
170 | #define BAND_G (0x02) | ||
171 | #define ALL_802_11_BANDS (BAND_B | BAND_G) | ||
172 | |||
173 | /** MACRO DEFINITIONS */ | ||
174 | #define CAL_NF(NF) ((s32)(-(s32)(NF))) | ||
175 | #define CAL_RSSI(SNR, NF) ((s32)((s32)(SNR) + CAL_NF(NF))) | ||
176 | #define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) | ||
177 | |||
178 | #define DEFAULT_BCN_AVG_FACTOR 8 | ||
179 | #define DEFAULT_DATA_AVG_FACTOR 8 | ||
180 | #define AVG_SCALE 100 | ||
181 | #define CAL_AVG_SNR_NF(AVG, SNRNF, N) \ | ||
182 | (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \ | ||
183 | ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \ | ||
184 | AVG_SCALE)) / N)) | ||
185 | |||
186 | #define B_SUPPORTED_RATES 8 | ||
187 | #define G_SUPPORTED_RATES 14 | ||
188 | |||
189 | #define WLAN_SUPPORTED_RATES 14 | ||
190 | |||
191 | #define MAX_LEDS 8 | ||
192 | |||
193 | #define IS_MESH_FRAME(x) (x->cb[6]) | ||
194 | #define SET_MESH_FRAME(x) (x->cb[6]=1) | ||
195 | #define UNSET_MESH_FRAME(x) (x->cb[6]=0) | ||
196 | |||
197 | /** Global Variable Declaration */ | ||
198 | typedef struct _wlan_private wlan_private; | ||
199 | typedef struct _wlan_adapter wlan_adapter; | ||
200 | extern const char libertas_driver_version[]; | ||
201 | extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE]; | ||
202 | |||
203 | extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES]; | ||
204 | |||
205 | extern u8 libertas_supported_rates[G_SUPPORTED_RATES]; | ||
206 | |||
207 | extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES]; | ||
208 | |||
209 | extern u8 libertas_adhoc_rates_b[4]; | ||
210 | |||
211 | /** ENUM definition*/ | ||
212 | /** SNRNF_TYPE */ | ||
213 | enum SNRNF_TYPE { | ||
214 | TYPE_BEACON = 0, | ||
215 | TYPE_RXPD, | ||
216 | MAX_TYPE_B | ||
217 | }; | ||
218 | |||
219 | /** SNRNF_DATA*/ | ||
220 | enum SNRNF_DATA { | ||
221 | TYPE_NOAVG = 0, | ||
222 | TYPE_AVG, | ||
223 | MAX_TYPE_AVG | ||
224 | }; | ||
225 | |||
226 | /** WLAN_802_11_AUTH_ALG*/ | ||
227 | enum WLAN_802_11_AUTH_ALG { | ||
228 | AUTH_ALG_OPEN_SYSTEM = 1, | ||
229 | AUTH_ALG_SHARED_KEY = 2, | ||
230 | AUTH_ALG_NETWORK_EAP = 8, | ||
231 | }; | ||
232 | |||
233 | /** WLAN_802_1X_AUTH_ALG */ | ||
234 | enum WLAN_802_1X_AUTH_ALG { | ||
235 | WLAN_1X_AUTH_ALG_NONE = 1, | ||
236 | WLAN_1X_AUTH_ALG_LEAP = 2, | ||
237 | WLAN_1X_AUTH_ALG_TLS = 4, | ||
238 | WLAN_1X_AUTH_ALG_TTLS = 8, | ||
239 | WLAN_1X_AUTH_ALG_MD5 = 16, | ||
240 | }; | ||
241 | |||
242 | /** WLAN_802_11_ENCRYPTION_MODE */ | ||
243 | enum WLAN_802_11_ENCRYPTION_MODE { | ||
244 | CIPHER_NONE, | ||
245 | CIPHER_WEP40, | ||
246 | CIPHER_TKIP, | ||
247 | CIPHER_CCMP, | ||
248 | CIPHER_WEP104, | ||
249 | }; | ||
250 | |||
251 | /** WLAN_802_11_POWER_MODE */ | ||
252 | enum WLAN_802_11_POWER_MODE { | ||
253 | wlan802_11powermodecam, | ||
254 | wlan802_11powermodemax_psp, | ||
255 | wlan802_11Powermodefast_psp, | ||
256 | /*not a real mode, defined as an upper bound */ | ||
257 | wlan802_11powemodemax | ||
258 | }; | ||
259 | |||
260 | /** PS_STATE */ | ||
261 | enum PS_STATE { | ||
262 | PS_STATE_FULL_POWER, | ||
263 | PS_STATE_AWAKE, | ||
264 | PS_STATE_PRE_SLEEP, | ||
265 | PS_STATE_SLEEP | ||
266 | }; | ||
267 | |||
268 | /** DNLD_STATE */ | ||
269 | enum DNLD_STATE { | ||
270 | DNLD_RES_RECEIVED, | ||
271 | DNLD_DATA_SENT, | ||
272 | DNLD_CMD_SENT | ||
273 | }; | ||
274 | |||
275 | /** WLAN_MEDIA_STATE */ | ||
276 | enum WLAN_MEDIA_STATE { | ||
277 | libertas_connected, | ||
278 | libertas_disconnected | ||
279 | }; | ||
280 | |||
281 | /** WLAN_802_11_PRIVACY_FILTER */ | ||
282 | enum WLAN_802_11_PRIVACY_FILTER { | ||
283 | wlan802_11privfilteracceptall, | ||
284 | wlan802_11privfilter8021xWEP | ||
285 | }; | ||
286 | |||
287 | /** mv_ms_type */ | ||
288 | enum mv_ms_type { | ||
289 | MVMS_DAT = 0, | ||
290 | MVMS_CMD = 1, | ||
291 | MVMS_TXDONE = 2, | ||
292 | MVMS_EVENT | ||
293 | }; | ||
294 | |||
295 | /** WLAN_802_11_NETWORK_INFRASTRUCTURE */ | ||
296 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE { | ||
297 | wlan802_11ibss, | ||
298 | wlan802_11infrastructure, | ||
299 | wlan802_11autounknown, | ||
300 | /*defined as upper bound */ | ||
301 | wlan802_11infrastructuremax | ||
302 | }; | ||
303 | |||
304 | /** WLAN_802_11_AUTHENTICATION_MODE */ | ||
305 | enum WLAN_802_11_AUTHENTICATION_MODE { | ||
306 | wlan802_11authmodeopen = 0x00, | ||
307 | wlan802_11authmodeshared = 0x01, | ||
308 | wlan802_11authmodenetworkEAP = 0x80, | ||
309 | }; | ||
310 | |||
311 | /** WLAN_802_11_WEP_STATUS */ | ||
312 | enum WLAN_802_11_WEP_STATUS { | ||
313 | wlan802_11WEPenabled, | ||
314 | wlan802_11WEPdisabled, | ||
315 | }; | ||
316 | |||
317 | /** SNMP_MIB_INDEX_e */ | ||
318 | enum SNMP_MIB_INDEX_e { | ||
319 | desired_bsstype_i = 0, | ||
320 | op_rateset_i, | ||
321 | bcnperiod_i, | ||
322 | dtimperiod_i, | ||
323 | assocrsp_timeout_i, | ||
324 | rtsthresh_i, | ||
325 | short_retrylim_i, | ||
326 | long_retrylim_i, | ||
327 | fragthresh_i, | ||
328 | dot11d_i, | ||
329 | dot11h_i, | ||
330 | manufid_i, | ||
331 | prodID_i, | ||
332 | manuf_oui_i, | ||
333 | manuf_name_i, | ||
334 | manuf_prodname_i, | ||
335 | manuf_prodver_i, | ||
336 | }; | ||
337 | |||
338 | /** KEY_TYPE_ID */ | ||
339 | enum KEY_TYPE_ID { | ||
340 | KEY_TYPE_ID_WEP = 0, | ||
341 | KEY_TYPE_ID_TKIP, | ||
342 | KEY_TYPE_ID_AES | ||
343 | }; | ||
344 | |||
345 | /** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ | ||
346 | enum KEY_INFO_WPA { | ||
347 | KEY_INFO_WPA_MCAST = 0x01, | ||
348 | KEY_INFO_WPA_UNICAST = 0x02, | ||
349 | KEY_INFO_WPA_ENABLED = 0x04 | ||
350 | }; | ||
351 | |||
352 | /** SNMP_MIB_VALUE_e */ | ||
353 | enum SNMP_MIB_VALUE_e { | ||
354 | SNMP_MIB_VALUE_INFRA = 1, | ||
355 | SNMP_MIB_VALUE_ADHOC | ||
356 | }; | ||
357 | |||
358 | /* Default values for fwt commands. */ | ||
359 | #define FWT_DEFAULT_METRIC 0 | ||
360 | #define FWT_DEFAULT_DIR 1 | ||
361 | #define FWT_DEFAULT_SSN 0xffffffff | ||
362 | #define FWT_DEFAULT_DSN 0 | ||
363 | #define FWT_DEFAULT_HOPCOUNT 0 | ||
364 | #define FWT_DEFAULT_TTL 0 | ||
365 | #define FWT_DEFAULT_EXPIRATION 0 | ||
366 | #define FWT_DEFAULT_SLEEPMODE 0 | ||
367 | #define FWT_DEFAULT_SNR 0 | ||
368 | |||
369 | #endif /* _WLAN_DEFS_H_ */ | ||
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h new file mode 100644 index 000000000000..b1f876f9693b --- /dev/null +++ b/drivers/net/wireless/libertas/dev.h | |||
@@ -0,0 +1,403 @@ | |||
1 | /** | ||
2 | * This file contains definitions and data structures specific | ||
3 | * to Marvell 802.11 NIC. It contains the Device Information | ||
4 | * structure wlan_adapter. | ||
5 | */ | ||
6 | #ifndef _WLAN_DEV_H_ | ||
7 | #define _WLAN_DEV_H_ | ||
8 | |||
9 | #include <linux/netdevice.h> | ||
10 | #include <linux/wireless.h> | ||
11 | #include <linux/ethtool.h> | ||
12 | #include <linux/debugfs.h> | ||
13 | |||
14 | #include "defs.h" | ||
15 | #include "scan.h" | ||
16 | #include "thread.h" | ||
17 | |||
18 | extern struct ethtool_ops libertas_ethtool_ops; | ||
19 | |||
20 | #define MAX_BSSID_PER_CHANNEL 16 | ||
21 | |||
22 | #define NR_TX_QUEUE 3 | ||
23 | |||
24 | /* For the extended Scan */ | ||
25 | #define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \ | ||
26 | MRVDRV_MAX_CHANNEL_SIZE + 1 | ||
27 | |||
28 | #define MAX_REGION_CHANNEL_NUM 2 | ||
29 | |||
30 | /** Chan-freq-TxPower mapping table*/ | ||
31 | struct chan_freq_power { | ||
32 | /** channel Number */ | ||
33 | u16 channel; | ||
34 | /** frequency of this channel */ | ||
35 | u32 freq; | ||
36 | /** Max allowed Tx power level */ | ||
37 | u16 maxtxpower; | ||
38 | /** TRUE:channel unsupported; FLASE:supported*/ | ||
39 | u8 unsupported; | ||
40 | }; | ||
41 | |||
42 | /** region-band mapping table*/ | ||
43 | struct region_channel { | ||
44 | /** TRUE if this entry is valid */ | ||
45 | u8 valid; | ||
46 | /** region code for US, Japan ... */ | ||
47 | u8 region; | ||
48 | /** band B/G/A, used for BAND_CONFIG cmd */ | ||
49 | u8 band; | ||
50 | /** Actual No. of elements in the array below */ | ||
51 | u8 nrcfp; | ||
52 | /** chan-freq-txpower mapping table*/ | ||
53 | struct chan_freq_power *CFP; | ||
54 | }; | ||
55 | |||
56 | struct wlan_802_11_security { | ||
57 | u8 WPAenabled; | ||
58 | u8 WPA2enabled; | ||
59 | enum WLAN_802_11_WEP_STATUS WEPstatus; | ||
60 | enum WLAN_802_11_AUTHENTICATION_MODE authmode; | ||
61 | enum WLAN_802_1X_AUTH_ALG auth1xalg; | ||
62 | enum WLAN_802_11_ENCRYPTION_MODE Encryptionmode; | ||
63 | }; | ||
64 | |||
65 | /** Current Basic Service Set State Structure */ | ||
66 | struct current_bss_params { | ||
67 | struct bss_descriptor bssdescriptor; | ||
68 | /** bssid */ | ||
69 | u8 bssid[ETH_ALEN]; | ||
70 | /** ssid */ | ||
71 | struct WLAN_802_11_SSID ssid; | ||
72 | |||
73 | /** band */ | ||
74 | u8 band; | ||
75 | /** channel */ | ||
76 | u8 channel; | ||
77 | /** number of rates supported */ | ||
78 | int numofrates; | ||
79 | /** supported rates*/ | ||
80 | u8 datarates[WLAN_SUPPORTED_RATES]; | ||
81 | }; | ||
82 | |||
83 | /** sleep_params */ | ||
84 | struct sleep_params { | ||
85 | u16 sp_error; | ||
86 | u16 sp_offset; | ||
87 | u16 sp_stabletime; | ||
88 | u8 sp_calcontrol; | ||
89 | u8 sp_extsleepclk; | ||
90 | u16 sp_reserved; | ||
91 | }; | ||
92 | |||
93 | /** Data structure for the Marvell WLAN device */ | ||
94 | typedef struct _wlan_dev { | ||
95 | /** device name */ | ||
96 | char name[DEV_NAME_LEN]; | ||
97 | /** card pointer */ | ||
98 | void *card; | ||
99 | /** IO port */ | ||
100 | u32 ioport; | ||
101 | /** Upload received */ | ||
102 | u32 upld_rcv; | ||
103 | /** Upload type */ | ||
104 | u32 upld_typ; | ||
105 | /** Upload length */ | ||
106 | u32 upld_len; | ||
107 | /** netdev pointer */ | ||
108 | struct net_device *netdev; | ||
109 | /* Upload buffer */ | ||
110 | u8 upld_buf[WLAN_UPLD_SIZE]; | ||
111 | /* Download sent: | ||
112 | bit0 1/0=data_sent/data_tx_done, | ||
113 | bit1 1/0=cmd_sent/cmd_tx_done, | ||
114 | all other bits reserved 0 */ | ||
115 | u8 dnld_sent; | ||
116 | } wlan_dev_t, *pwlan_dev_t; | ||
117 | |||
118 | /* Mesh statistics */ | ||
119 | struct wlan_mesh_stats { | ||
120 | u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ | ||
121 | u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ | ||
122 | u32 fwd_drop_ttl; /* Fwd: TTL zero */ | ||
123 | u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ | ||
124 | u32 fwd_drop_noroute; /* Fwd: No route to Destination */ | ||
125 | u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ | ||
126 | u32 drop_blind; /* Rx: Dropped by blinding table */ | ||
127 | }; | ||
128 | |||
129 | /** Private structure for the MV device */ | ||
130 | struct _wlan_private { | ||
131 | int open; | ||
132 | int mesh_open; | ||
133 | int infra_open; | ||
134 | |||
135 | wlan_adapter *adapter; | ||
136 | wlan_dev_t wlan_dev; | ||
137 | |||
138 | struct net_device_stats stats; | ||
139 | struct net_device *mesh_dev ; /* Virtual device */ | ||
140 | |||
141 | struct iw_statistics wstats; | ||
142 | struct wlan_mesh_stats mstats; | ||
143 | struct dentry *debugfs_dir; | ||
144 | struct dentry *debugfs_debug; | ||
145 | struct dentry *debugfs_files[6]; | ||
146 | |||
147 | struct dentry *events_dir; | ||
148 | struct dentry *debugfs_events_files[6]; | ||
149 | |||
150 | struct dentry *regs_dir; | ||
151 | struct dentry *debugfs_regs_files[6]; | ||
152 | |||
153 | u32 mac_offset; | ||
154 | u32 bbp_offset; | ||
155 | u32 rf_offset; | ||
156 | |||
157 | const struct firmware *firmware; | ||
158 | struct device *hotplug_device; | ||
159 | |||
160 | /** thread to service interrupts */ | ||
161 | struct wlan_thread mainthread; | ||
162 | |||
163 | struct delayed_work assoc_work; | ||
164 | struct workqueue_struct *assoc_thread; | ||
165 | }; | ||
166 | |||
167 | /** Association request | ||
168 | * | ||
169 | * Encapsulates all the options that describe a specific assocation request | ||
170 | * or configuration of the wireless card's radio, mode, and security settings. | ||
171 | */ | ||
172 | struct assoc_request { | ||
173 | #define ASSOC_FLAG_SSID 1 | ||
174 | #define ASSOC_FLAG_CHANNEL 2 | ||
175 | #define ASSOC_FLAG_MODE 3 | ||
176 | #define ASSOC_FLAG_BSSID 4 | ||
177 | #define ASSOC_FLAG_WEP_KEYS 5 | ||
178 | #define ASSOC_FLAG_WEP_TX_KEYIDX 6 | ||
179 | #define ASSOC_FLAG_WPA_MCAST_KEY 7 | ||
180 | #define ASSOC_FLAG_WPA_UCAST_KEY 8 | ||
181 | #define ASSOC_FLAG_SECINFO 9 | ||
182 | #define ASSOC_FLAG_WPA_IE 10 | ||
183 | unsigned long flags; | ||
184 | |||
185 | struct WLAN_802_11_SSID ssid; | ||
186 | u8 channel; | ||
187 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode; | ||
188 | u8 bssid[ETH_ALEN]; | ||
189 | |||
190 | /** WEP keys */ | ||
191 | struct WLAN_802_11_KEY wep_keys[4]; | ||
192 | u16 wep_tx_keyidx; | ||
193 | |||
194 | /** WPA keys */ | ||
195 | struct WLAN_802_11_KEY wpa_mcast_key; | ||
196 | struct WLAN_802_11_KEY wpa_unicast_key; | ||
197 | |||
198 | struct wlan_802_11_security secinfo; | ||
199 | |||
200 | /** WPA Information Elements*/ | ||
201 | #define MAX_WPA_IE_LEN 64 | ||
202 | u8 wpa_ie[MAX_WPA_IE_LEN]; | ||
203 | u8 wpa_ie_len; | ||
204 | }; | ||
205 | |||
206 | /** Wlan adapter data structure*/ | ||
207 | struct _wlan_adapter { | ||
208 | /** STATUS variables */ | ||
209 | u32 fwreleasenumber; | ||
210 | u32 fwcapinfo; | ||
211 | /* protected with big lock */ | ||
212 | |||
213 | struct mutex lock; | ||
214 | |||
215 | u8 tmptxbuf[WLAN_UPLD_SIZE]; | ||
216 | /* protected by hard_start_xmit serialization */ | ||
217 | |||
218 | /** command-related variables */ | ||
219 | u16 seqnum; | ||
220 | /* protected by big lock */ | ||
221 | |||
222 | struct cmd_ctrl_node *cmd_array; | ||
223 | /** Current command */ | ||
224 | struct cmd_ctrl_node *cur_cmd; | ||
225 | int cur_cmd_retcode; | ||
226 | /** command Queues */ | ||
227 | /** Free command buffers */ | ||
228 | struct list_head cmdfreeq; | ||
229 | /** Pending command buffers */ | ||
230 | struct list_head cmdpendingq; | ||
231 | |||
232 | wait_queue_head_t cmd_pending; | ||
233 | u8 nr_cmd_pending; | ||
234 | /* command related variables protected by adapter->driver_lock */ | ||
235 | |||
236 | /** Async and Sync Event variables */ | ||
237 | u32 intcounter; | ||
238 | u32 eventcause; | ||
239 | u8 nodename[16]; /* nickname */ | ||
240 | |||
241 | /** spin locks */ | ||
242 | spinlock_t driver_lock; | ||
243 | |||
244 | /** Timers */ | ||
245 | struct timer_list command_timer; | ||
246 | |||
247 | /* TX queue used in PS mode */ | ||
248 | spinlock_t txqueue_lock; | ||
249 | struct sk_buff *tx_queue_ps[NR_TX_QUEUE]; | ||
250 | unsigned int tx_queue_idx; | ||
251 | |||
252 | u8 hisregcpy; | ||
253 | |||
254 | /** current ssid/bssid related parameters*/ | ||
255 | struct current_bss_params curbssparams; | ||
256 | |||
257 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; | ||
258 | |||
259 | struct bss_descriptor *pattemptedbssdesc; | ||
260 | |||
261 | struct WLAN_802_11_SSID previousssid; | ||
262 | u8 previousbssid[ETH_ALEN]; | ||
263 | |||
264 | struct bss_descriptor *scantable; | ||
265 | u32 numinscantable; | ||
266 | |||
267 | u8 scantype; | ||
268 | u32 scanmode; | ||
269 | |||
270 | u16 beaconperiod; | ||
271 | u8 adhoccreate; | ||
272 | |||
273 | /** capability Info used in Association, start, join */ | ||
274 | struct ieeetypes_capinfo capinfo; | ||
275 | |||
276 | /** MAC address information */ | ||
277 | u8 current_addr[ETH_ALEN]; | ||
278 | u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; | ||
279 | u32 nr_of_multicastmacaddr; | ||
280 | |||
281 | /** 802.11 statistics */ | ||
282 | // struct cmd_DS_802_11_GET_STAT wlan802_11Stat; | ||
283 | |||
284 | u16 enablehwauto; | ||
285 | u16 ratebitmap; | ||
286 | /** control G rates */ | ||
287 | u8 adhoc_grate_enabled; | ||
288 | |||
289 | u32 txantenna; | ||
290 | u32 rxantenna; | ||
291 | |||
292 | u8 adhocchannel; | ||
293 | u32 fragthsd; | ||
294 | u32 rtsthsd; | ||
295 | |||
296 | u32 datarate; | ||
297 | u8 is_datarate_auto; | ||
298 | |||
299 | u16 listeninterval; | ||
300 | u16 prescan; | ||
301 | u8 txretrycount; | ||
302 | |||
303 | /** Tx-related variables (for single packet tx) */ | ||
304 | struct sk_buff *currenttxskb; | ||
305 | u16 TxLockFlag; | ||
306 | |||
307 | /** NIC Operation characteristics */ | ||
308 | u16 currentpacketfilter; | ||
309 | u32 connect_status; | ||
310 | u16 regioncode; | ||
311 | u16 regiontableindex; | ||
312 | u16 txpowerlevel; | ||
313 | |||
314 | /** POWER MANAGEMENT AND PnP SUPPORT */ | ||
315 | u8 surpriseremoved; | ||
316 | u16 atimwindow; | ||
317 | |||
318 | u16 psmode; /* Wlan802_11PowermodeCAM=disable | ||
319 | Wlan802_11PowermodeMAX_PSP=enable */ | ||
320 | u16 multipledtim; | ||
321 | u32 psstate; | ||
322 | u8 needtowakeup; | ||
323 | |||
324 | struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep; | ||
325 | u16 locallisteninterval; | ||
326 | u16 nullpktinterval; | ||
327 | |||
328 | struct assoc_request * assoc_req; | ||
329 | |||
330 | /** Encryption parameter */ | ||
331 | struct wlan_802_11_security secinfo; | ||
332 | |||
333 | /** WEP keys */ | ||
334 | struct WLAN_802_11_KEY wep_keys[4]; | ||
335 | u16 wep_tx_keyidx; | ||
336 | |||
337 | /** WPA keys */ | ||
338 | struct WLAN_802_11_KEY wpa_mcast_key; | ||
339 | struct WLAN_802_11_KEY wpa_unicast_key; | ||
340 | |||
341 | /** WPA Information Elements*/ | ||
342 | #define MAX_WPA_IE_LEN 64 | ||
343 | u8 wpa_ie[MAX_WPA_IE_LEN]; | ||
344 | u8 wpa_ie_len; | ||
345 | |||
346 | u16 rxantennamode; | ||
347 | u16 txantennamode; | ||
348 | |||
349 | /** Requested Signal Strength*/ | ||
350 | u16 bcn_avg_factor; | ||
351 | u16 data_avg_factor; | ||
352 | u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG]; | ||
353 | u16 NF[MAX_TYPE_B][MAX_TYPE_AVG]; | ||
354 | u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG]; | ||
355 | u8 rawSNR[DEFAULT_DATA_AVG_FACTOR]; | ||
356 | u8 rawNF[DEFAULT_DATA_AVG_FACTOR]; | ||
357 | u16 nextSNRNF; | ||
358 | u16 numSNRNF; | ||
359 | u16 rxpd_rate; | ||
360 | |||
361 | u8 radioon; | ||
362 | u32 preamble; | ||
363 | |||
364 | /** Multi bands Parameter*/ | ||
365 | u8 libertas_supported_rates[G_SUPPORTED_RATES]; | ||
366 | |||
367 | /** Blue Tooth Co-existence Arbitration */ | ||
368 | |||
369 | /** sleep_params */ | ||
370 | struct sleep_params sp; | ||
371 | |||
372 | /** RF calibration data */ | ||
373 | |||
374 | #define MAX_REGION_CHANNEL_NUM 2 | ||
375 | /** region channel data */ | ||
376 | struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; | ||
377 | |||
378 | struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM]; | ||
379 | |||
380 | /** 11D and Domain Regulatory Data */ | ||
381 | struct wlan_802_11d_domain_reg domainreg; | ||
382 | struct parsed_region_chan_11d parsed_region_chan; | ||
383 | |||
384 | /** FSM variable for 11d support */ | ||
385 | u32 enable11d; | ||
386 | |||
387 | /** MISCELLANEOUS */ | ||
388 | u8 *prdeeprom; | ||
389 | struct wlan_offset_value offsetvalue; | ||
390 | |||
391 | struct cmd_ds_802_11_get_log logmsg; | ||
392 | u16 scanprobes; | ||
393 | |||
394 | u32 pkttxctrl; | ||
395 | |||
396 | u16 txrate; | ||
397 | u32 linkmode; | ||
398 | u32 radiomode; | ||
399 | u32 debugmode; | ||
400 | u8 fw_ready; | ||
401 | }; | ||
402 | |||
403 | #endif /* _WLAN_DEV_H_ */ | ||
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c new file mode 100644 index 000000000000..0064de542963 --- /dev/null +++ b/drivers/net/wireless/libertas/ethtool.c | |||
@@ -0,0 +1,184 @@ | |||
1 | |||
2 | #include <linux/netdevice.h> | ||
3 | #include <linux/ethtool.h> | ||
4 | #include <linux/delay.h> | ||
5 | |||
6 | #include "host.h" | ||
7 | #include "sbi.h" | ||
8 | #include "decl.h" | ||
9 | #include "defs.h" | ||
10 | #include "dev.h" | ||
11 | #include "join.h" | ||
12 | #include "wext.h" | ||
13 | static const char * mesh_stat_strings[]= { | ||
14 | "drop_duplicate_bcast", | ||
15 | "drop_ttl_zero", | ||
16 | "drop_no_fwd_route", | ||
17 | "drop_no_buffers", | ||
18 | "fwded_unicast_cnt", | ||
19 | "fwded_bcast_cnt", | ||
20 | "drop_blind_table" | ||
21 | }; | ||
22 | |||
23 | static void libertas_ethtool_get_drvinfo(struct net_device *dev, | ||
24 | struct ethtool_drvinfo *info) | ||
25 | { | ||
26 | wlan_private *priv = (wlan_private *) dev->priv; | ||
27 | char fwver[32]; | ||
28 | |||
29 | libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1); | ||
30 | |||
31 | strcpy(info->driver, "libertas"); | ||
32 | strcpy(info->version, libertas_driver_version); | ||
33 | strcpy(info->fw_version, fwver); | ||
34 | } | ||
35 | |||
36 | /* All 8388 parts have 16KiB EEPROM size at the time of writing. | ||
37 | * In case that changes this needs fixing. | ||
38 | */ | ||
39 | #define LIBERTAS_EEPROM_LEN 16384 | ||
40 | |||
41 | static int libertas_ethtool_get_eeprom_len(struct net_device *dev) | ||
42 | { | ||
43 | return LIBERTAS_EEPROM_LEN; | ||
44 | } | ||
45 | |||
46 | static int libertas_ethtool_get_eeprom(struct net_device *dev, | ||
47 | struct ethtool_eeprom *eeprom, u8 * bytes) | ||
48 | { | ||
49 | wlan_private *priv = (wlan_private *) dev->priv; | ||
50 | wlan_adapter *adapter = priv->adapter; | ||
51 | struct wlan_ioctl_regrdwr regctrl; | ||
52 | char *ptr; | ||
53 | int ret; | ||
54 | |||
55 | regctrl.action = 0; | ||
56 | regctrl.offset = eeprom->offset; | ||
57 | regctrl.NOB = eeprom->len; | ||
58 | |||
59 | if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN) | ||
60 | return -EINVAL; | ||
61 | |||
62 | // mutex_lock(&priv->mutex); | ||
63 | |||
64 | adapter->prdeeprom = | ||
65 | (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL); | ||
66 | if (!adapter->prdeeprom) | ||
67 | return -ENOMEM; | ||
68 | memcpy(adapter->prdeeprom, ®ctrl, sizeof(regctrl)); | ||
69 | |||
70 | /* +14 is for action, offset, and NOB in | ||
71 | * response */ | ||
72 | lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n", | ||
73 | regctrl.action, regctrl.offset, regctrl.NOB); | ||
74 | |||
75 | ret = libertas_prepare_and_send_command(priv, | ||
76 | cmd_802_11_eeprom_access, | ||
77 | regctrl.action, | ||
78 | cmd_option_waitforrsp, 0, | ||
79 | ®ctrl); | ||
80 | |||
81 | if (ret) { | ||
82 | if (adapter->prdeeprom) | ||
83 | kfree(adapter->prdeeprom); | ||
84 | LEAVE(); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | mdelay(10); | ||
89 | |||
90 | ptr = (char *)adapter->prdeeprom; | ||
91 | |||
92 | /* skip the command header, but include the "value" u32 variable */ | ||
93 | ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4; | ||
94 | |||
95 | /* | ||
96 | * Return the result back to the user | ||
97 | */ | ||
98 | memcpy(bytes, ptr, eeprom->len); | ||
99 | |||
100 | if (adapter->prdeeprom) | ||
101 | kfree(adapter->prdeeprom); | ||
102 | // mutex_unlock(&priv->mutex); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static void libertas_ethtool_get_stats(struct net_device * dev, | ||
108 | struct ethtool_stats * stats, u64 * data) | ||
109 | { | ||
110 | wlan_private *priv = dev->priv; | ||
111 | |||
112 | ENTER(); | ||
113 | |||
114 | stats->cmd = ETHTOOL_GSTATS; | ||
115 | BUG_ON(stats->n_stats != MESH_STATS_NUM); | ||
116 | |||
117 | data[0] = priv->mstats.fwd_drop_rbt; | ||
118 | data[1] = priv->mstats.fwd_drop_ttl; | ||
119 | data[2] = priv->mstats.fwd_drop_noroute; | ||
120 | data[3] = priv->mstats.fwd_drop_nobuf; | ||
121 | data[4] = priv->mstats.fwd_unicast_cnt; | ||
122 | data[5] = priv->mstats.fwd_bcast_cnt; | ||
123 | data[6] = priv->mstats.drop_blind; | ||
124 | |||
125 | LEAVE(); | ||
126 | } | ||
127 | |||
128 | static int libertas_ethtool_get_stats_count(struct net_device * dev) | ||
129 | { | ||
130 | int ret; | ||
131 | wlan_private *priv = dev->priv; | ||
132 | struct cmd_ds_mesh_access mesh_access; | ||
133 | |||
134 | ENTER(); | ||
135 | /* Get Mesh Statistics */ | ||
136 | ret = libertas_prepare_and_send_command(priv, | ||
137 | cmd_mesh_access, cmd_act_mesh_get_stats, | ||
138 | cmd_option_waitforrsp, 0, &mesh_access); | ||
139 | |||
140 | if (ret) { | ||
141 | LEAVE(); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | priv->mstats.fwd_drop_rbt = mesh_access.data[0]; | ||
146 | priv->mstats.fwd_drop_ttl = mesh_access.data[1]; | ||
147 | priv->mstats.fwd_drop_noroute = mesh_access.data[2]; | ||
148 | priv->mstats.fwd_drop_nobuf = mesh_access.data[3]; | ||
149 | priv->mstats.fwd_unicast_cnt = mesh_access.data[4]; | ||
150 | priv->mstats.fwd_bcast_cnt = mesh_access.data[5]; | ||
151 | priv->mstats.drop_blind = mesh_access.data[6]; | ||
152 | |||
153 | LEAVE(); | ||
154 | return MESH_STATS_NUM; | ||
155 | } | ||
156 | |||
157 | static void libertas_ethtool_get_strings (struct net_device * dev, | ||
158 | u32 stringset, | ||
159 | u8 * s) | ||
160 | { | ||
161 | int i; | ||
162 | |||
163 | ENTER(); | ||
164 | switch (stringset) { | ||
165 | case ETH_SS_STATS: | ||
166 | for (i=0; i < MESH_STATS_NUM; i++) { | ||
167 | memcpy(s + i * ETH_GSTRING_LEN, | ||
168 | mesh_stat_strings[i], | ||
169 | ETH_GSTRING_LEN); | ||
170 | } | ||
171 | break; | ||
172 | } | ||
173 | LEAVE(); | ||
174 | } | ||
175 | |||
176 | struct ethtool_ops libertas_ethtool_ops = { | ||
177 | .get_drvinfo = libertas_ethtool_get_drvinfo, | ||
178 | .get_eeprom = libertas_ethtool_get_eeprom, | ||
179 | .get_eeprom_len = libertas_ethtool_get_eeprom_len, | ||
180 | .get_stats_count = libertas_ethtool_get_stats_count, | ||
181 | .get_ethtool_stats = libertas_ethtool_get_stats, | ||
182 | .get_strings = libertas_ethtool_get_strings, | ||
183 | }; | ||
184 | |||
diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c new file mode 100644 index 000000000000..b194a4570791 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /** | ||
2 | * This file contains the initialization for FW and HW | ||
3 | */ | ||
4 | #include <linux/module.h> | ||
5 | #include <linux/moduleparam.h> | ||
6 | |||
7 | #include <linux/vmalloc.h> | ||
8 | #include <linux/firmware.h> | ||
9 | #include <linux/version.h> | ||
10 | |||
11 | #include "host.h" | ||
12 | #include "sbi.h" | ||
13 | #include "defs.h" | ||
14 | #include "decl.h" | ||
15 | #include "dev.h" | ||
16 | #include "fw.h" | ||
17 | #include "wext.h" | ||
18 | #include "if_usb.h" | ||
19 | |||
20 | char *libertas_fw_name = NULL; | ||
21 | module_param_named(fw_name, libertas_fw_name, charp, 0644); | ||
22 | |||
23 | unsigned int libertas_debug = 0; | ||
24 | module_param(libertas_debug, int, 0); | ||
25 | |||
26 | /** | ||
27 | * @brief This function checks the validity of Boot2/FW image. | ||
28 | * | ||
29 | * @param data pointer to image | ||
30 | * len image length | ||
31 | * @return 0 or -1 | ||
32 | */ | ||
33 | static int check_fwfile_format(u8 *data, u32 totlen) | ||
34 | { | ||
35 | u8 bincmd, exit; | ||
36 | u32 blksize, offset, len; | ||
37 | int ret; | ||
38 | |||
39 | ret = 1; | ||
40 | exit = len = 0; | ||
41 | |||
42 | do { | ||
43 | bincmd = *data; | ||
44 | blksize = *(u32*)(data + offsetof(struct fwheader, datalength)); | ||
45 | switch (bincmd) { | ||
46 | case FW_HAS_DATA_TO_RECV: | ||
47 | offset = sizeof(struct fwheader) + blksize; | ||
48 | data += offset; | ||
49 | len += offset; | ||
50 | if (len >= totlen) | ||
51 | exit = 1; | ||
52 | break; | ||
53 | case FW_HAS_LAST_BLOCK: | ||
54 | exit = 1; | ||
55 | ret = 0; | ||
56 | break; | ||
57 | default: | ||
58 | exit = 1; | ||
59 | break; | ||
60 | } | ||
61 | } while (!exit); | ||
62 | |||
63 | if (ret) | ||
64 | lbs_pr_err("bin file format check FAIL...\n"); | ||
65 | else | ||
66 | lbs_pr_debug(1, "bin file format check PASS...\n"); | ||
67 | |||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * @brief This function downloads firmware image, gets | ||
73 | * HW spec from firmware and set basic parameters to | ||
74 | * firmware. | ||
75 | * | ||
76 | * @param priv A pointer to wlan_private structure | ||
77 | * @return 0 or -1 | ||
78 | */ | ||
79 | static int wlan_setup_station_hw(wlan_private * priv) | ||
80 | { | ||
81 | int ret = -1; | ||
82 | wlan_adapter *adapter = priv->adapter; | ||
83 | |||
84 | ENTER(); | ||
85 | |||
86 | if ((ret = request_firmware(&priv->firmware, libertas_fw_name, | ||
87 | priv->hotplug_device)) < 0) { | ||
88 | lbs_pr_err("request_firmware() failed, error code = %#x\n", | ||
89 | ret); | ||
90 | lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name); | ||
91 | goto done; | ||
92 | } | ||
93 | |||
94 | if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) { | ||
95 | release_firmware(priv->firmware); | ||
96 | goto done; | ||
97 | } | ||
98 | |||
99 | ret = libertas_sbi_prog_firmware(priv); | ||
100 | |||
101 | release_firmware(priv->firmware); | ||
102 | |||
103 | if (ret) { | ||
104 | lbs_pr_debug(1, "Bootloader in invalid state!\n"); | ||
105 | ret = -1; | ||
106 | goto done; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * Read MAC address from HW | ||
111 | */ | ||
112 | memset(adapter->current_addr, 0xff, ETH_ALEN); | ||
113 | |||
114 | ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec, | ||
115 | 0, cmd_option_waitforrsp, 0, NULL); | ||
116 | |||
117 | if (ret) { | ||
118 | ret = -1; | ||
119 | goto done; | ||
120 | } | ||
121 | |||
122 | libertas_set_mac_packet_filter(priv); | ||
123 | |||
124 | /* Get the supported Data rates */ | ||
125 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, | ||
126 | cmd_act_get_tx_rate, | ||
127 | cmd_option_waitforrsp, 0, NULL); | ||
128 | |||
129 | if (ret) { | ||
130 | ret = -1; | ||
131 | goto done; | ||
132 | } | ||
133 | |||
134 | ret = 0; | ||
135 | done: | ||
136 | LEAVE(); | ||
137 | |||
138 | return (ret); | ||
139 | } | ||
140 | |||
141 | static int wlan_allocate_adapter(wlan_private * priv) | ||
142 | { | ||
143 | u32 ulbufsize; | ||
144 | wlan_adapter *adapter = priv->adapter; | ||
145 | |||
146 | struct bss_descriptor *ptempscantable; | ||
147 | |||
148 | /* Allocate buffer to store the BSSID list */ | ||
149 | ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST; | ||
150 | if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) { | ||
151 | libertas_free_adapter(priv); | ||
152 | return -1; | ||
153 | } | ||
154 | |||
155 | adapter->scantable = ptempscantable; | ||
156 | memset(adapter->scantable, 0, ulbufsize); | ||
157 | |||
158 | /* Allocate the command buffers */ | ||
159 | libertas_allocate_cmd_buffer(priv); | ||
160 | |||
161 | memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep)); | ||
162 | adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum); | ||
163 | adapter->libertas_ps_confirm_sleep.command = | ||
164 | cpu_to_le16(cmd_802_11_ps_mode); | ||
165 | adapter->libertas_ps_confirm_sleep.size = | ||
166 | cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep)); | ||
167 | adapter->libertas_ps_confirm_sleep.result = 0; | ||
168 | adapter->libertas_ps_confirm_sleep.action = | ||
169 | cpu_to_le16(cmd_subcmd_sleep_confirmed); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void wlan_init_adapter(wlan_private * priv) | ||
175 | { | ||
176 | wlan_adapter *adapter = priv->adapter; | ||
177 | int i; | ||
178 | |||
179 | adapter->scanprobes = 0; | ||
180 | |||
181 | adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; | ||
182 | adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; | ||
183 | |||
184 | /* ATIM params */ | ||
185 | adapter->atimwindow = 0; | ||
186 | |||
187 | adapter->connect_status = libertas_disconnected; | ||
188 | memset(adapter->current_addr, 0xff, ETH_ALEN); | ||
189 | |||
190 | /* scan type */ | ||
191 | adapter->scantype = cmd_scan_type_active; | ||
192 | |||
193 | /* scan mode */ | ||
194 | adapter->scanmode = cmd_bss_type_any; | ||
195 | |||
196 | /* 802.11 specific */ | ||
197 | adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; | ||
198 | for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]); | ||
199 | i++) | ||
200 | memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY)); | ||
201 | adapter->wep_tx_keyidx = 0; | ||
202 | adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; | ||
203 | adapter->secinfo.authmode = wlan802_11authmodeopen; | ||
204 | adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; | ||
205 | adapter->secinfo.Encryptionmode = CIPHER_NONE; | ||
206 | adapter->inframode = wlan802_11infrastructure; | ||
207 | |||
208 | adapter->assoc_req = NULL; | ||
209 | |||
210 | adapter->numinscantable = 0; | ||
211 | adapter->pattemptedbssdesc = NULL; | ||
212 | mutex_init(&adapter->lock); | ||
213 | |||
214 | adapter->prescan = 1; | ||
215 | |||
216 | memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); | ||
217 | |||
218 | /* PnP and power profile */ | ||
219 | adapter->surpriseremoved = 0; | ||
220 | |||
221 | adapter->currentpacketfilter = | ||
222 | cmd_act_mac_rx_on | cmd_act_mac_tx_on; | ||
223 | |||
224 | adapter->radioon = RADIO_ON; | ||
225 | adapter->txantenna = RF_ANTENNA_2; | ||
226 | adapter->rxantenna = RF_ANTENNA_AUTO; | ||
227 | |||
228 | adapter->is_datarate_auto = 1; | ||
229 | adapter->beaconperiod = MRVDRV_BEACON_INTERVAL; | ||
230 | |||
231 | // set default value of capinfo. | ||
232 | #define SHORT_PREAMBLE_ALLOWED 1 | ||
233 | memset(&adapter->capinfo, 0, sizeof(adapter->capinfo)); | ||
234 | adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED; | ||
235 | |||
236 | adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; | ||
237 | |||
238 | adapter->psmode = wlan802_11powermodecam; | ||
239 | adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; | ||
240 | |||
241 | adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL; | ||
242 | |||
243 | adapter->psstate = PS_STATE_FULL_POWER; | ||
244 | adapter->needtowakeup = 0; | ||
245 | adapter->locallisteninterval = 0; /* default value in firmware will be used */ | ||
246 | |||
247 | adapter->datarate = 0; // Initially indicate the rate as auto | ||
248 | |||
249 | adapter->adhoc_grate_enabled = 0; | ||
250 | |||
251 | adapter->intcounter = 0; | ||
252 | |||
253 | adapter->currenttxskb = NULL; | ||
254 | adapter->pkttxctrl = 0; | ||
255 | |||
256 | memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*)); | ||
257 | adapter->tx_queue_idx = 0; | ||
258 | spin_lock_init(&adapter->txqueue_lock); | ||
259 | |||
260 | return; | ||
261 | } | ||
262 | |||
263 | static void command_timer_fn(unsigned long data); | ||
264 | |||
265 | int libertas_init_fw(wlan_private * priv) | ||
266 | { | ||
267 | int ret = -1; | ||
268 | wlan_adapter *adapter = priv->adapter; | ||
269 | |||
270 | ENTER(); | ||
271 | |||
272 | /* Allocate adapter structure */ | ||
273 | if ((ret = wlan_allocate_adapter(priv)) != 0) | ||
274 | goto done; | ||
275 | |||
276 | /* init adapter structure */ | ||
277 | wlan_init_adapter(priv); | ||
278 | |||
279 | /* init timer etc. */ | ||
280 | setup_timer(&adapter->command_timer, command_timer_fn, | ||
281 | (unsigned long)priv); | ||
282 | |||
283 | /* download fimrware etc. */ | ||
284 | if ((ret = wlan_setup_station_hw(priv)) != 0) { | ||
285 | del_timer_sync(&adapter->command_timer); | ||
286 | goto done; | ||
287 | } | ||
288 | |||
289 | /* init 802.11d */ | ||
290 | libertas_init_11d(priv); | ||
291 | |||
292 | ret = 0; | ||
293 | done: | ||
294 | LEAVE(); | ||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | void libertas_free_adapter(wlan_private * priv) | ||
299 | { | ||
300 | wlan_adapter *adapter = priv->adapter; | ||
301 | |||
302 | if (!adapter) { | ||
303 | lbs_pr_debug(1, "Why double free adapter?:)\n"); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | lbs_pr_debug(1, "Free command buffer\n"); | ||
308 | libertas_free_cmd_buffer(priv); | ||
309 | |||
310 | lbs_pr_debug(1, "Free commandTimer\n"); | ||
311 | del_timer(&adapter->command_timer); | ||
312 | |||
313 | lbs_pr_debug(1, "Free scantable\n"); | ||
314 | if (adapter->scantable) { | ||
315 | kfree(adapter->scantable); | ||
316 | adapter->scantable = NULL; | ||
317 | } | ||
318 | |||
319 | lbs_pr_debug(1, "Free adapter\n"); | ||
320 | |||
321 | /* Free the adapter object itself */ | ||
322 | kfree(adapter); | ||
323 | priv->adapter = NULL; | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * This function handles the timeout of command sending. | ||
328 | * It will re-send the same command again. | ||
329 | */ | ||
330 | static void command_timer_fn(unsigned long data) | ||
331 | { | ||
332 | wlan_private *priv = (wlan_private *)data; | ||
333 | wlan_adapter *adapter = priv->adapter; | ||
334 | struct cmd_ctrl_node *ptempnode; | ||
335 | struct cmd_ds_command *cmd; | ||
336 | unsigned long flags; | ||
337 | |||
338 | ptempnode = adapter->cur_cmd; | ||
339 | cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr; | ||
340 | |||
341 | lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command); | ||
342 | |||
343 | if (!adapter->fw_ready) | ||
344 | return; | ||
345 | |||
346 | if (ptempnode == NULL) { | ||
347 | lbs_pr_debug(1, "PTempnode Empty\n"); | ||
348 | return; | ||
349 | } | ||
350 | |||
351 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
352 | adapter->cur_cmd = NULL; | ||
353 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
354 | |||
355 | lbs_pr_debug(1, "Re-sending same command as it timeout...!\n"); | ||
356 | libertas_queue_cmd(adapter, ptempnode, 0); | ||
357 | |||
358 | wake_up_interruptible(&priv->mainthread.waitq); | ||
359 | |||
360 | return; | ||
361 | } | ||
diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h new file mode 100644 index 000000000000..1f9ae267a9e0 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.h | |||
@@ -0,0 +1,13 @@ | |||
1 | /** | ||
2 | * This header file contains FW interface related definitions. | ||
3 | */ | ||
4 | #ifndef _WLAN_FW_H_ | ||
5 | #define _WLAN_FW_H_ | ||
6 | |||
7 | #ifndef DEV_NAME_LEN | ||
8 | #define DEV_NAME_LEN 32 | ||
9 | #endif | ||
10 | |||
11 | int libertas_init_fw(wlan_private * priv); | ||
12 | |||
13 | #endif /* _WLAN_FW_H_ */ | ||
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h new file mode 100644 index 000000000000..c0faaecaf5be --- /dev/null +++ b/drivers/net/wireless/libertas/host.h | |||
@@ -0,0 +1,338 @@ | |||
1 | /** | ||
2 | * This file contains definitions of WLAN commands. | ||
3 | */ | ||
4 | |||
5 | #ifndef _HOST_H_ | ||
6 | #define _HOST_H_ | ||
7 | |||
8 | /** PUBLIC DEFINITIONS */ | ||
9 | #define DEFAULT_AD_HOC_CHANNEL 6 | ||
10 | #define DEFAULT_AD_HOC_CHANNEL_A 36 | ||
11 | |||
12 | /** IEEE 802.11 oids */ | ||
13 | #define OID_802_11_SSID 0x00008002 | ||
14 | #define OID_802_11_INFRASTRUCTURE_MODE 0x00008008 | ||
15 | #define OID_802_11_FRAGMENTATION_THRESHOLD 0x00008009 | ||
16 | #define OID_802_11_RTS_THRESHOLD 0x0000800A | ||
17 | #define OID_802_11_TX_ANTENNA_SELECTED 0x0000800D | ||
18 | #define OID_802_11_SUPPORTED_RATES 0x0000800E | ||
19 | #define OID_802_11_STATISTICS 0x00008012 | ||
20 | #define OID_802_11_TX_RETRYCOUNT 0x0000801D | ||
21 | #define OID_802_11D_ENABLE 0x00008020 | ||
22 | |||
23 | #define cmd_option_waitforrsp 0x0002 | ||
24 | |||
25 | /** Host command ID */ | ||
26 | #define cmd_code_dnld 0x0002 | ||
27 | #define cmd_get_hw_spec 0x0003 | ||
28 | #define cmd_eeprom_update 0x0004 | ||
29 | #define cmd_802_11_reset 0x0005 | ||
30 | #define cmd_802_11_scan 0x0006 | ||
31 | #define cmd_802_11_get_log 0x000b | ||
32 | #define cmd_mac_multicast_adr 0x0010 | ||
33 | #define cmd_802_11_authenticate 0x0011 | ||
34 | #define cmd_802_11_eeprom_access 0x0059 | ||
35 | #define cmd_802_11_associate 0x0050 | ||
36 | #define cmd_802_11_set_wep 0x0013 | ||
37 | #define cmd_802_11_get_stat 0x0014 | ||
38 | #define cmd_802_3_get_stat 0x0015 | ||
39 | #define cmd_802_11_snmp_mib 0x0016 | ||
40 | #define cmd_mac_reg_map 0x0017 | ||
41 | #define cmd_bbp_reg_map 0x0018 | ||
42 | #define cmd_mac_reg_access 0x0019 | ||
43 | #define cmd_bbp_reg_access 0x001a | ||
44 | #define cmd_rf_reg_access 0x001b | ||
45 | #define cmd_802_11_radio_control 0x001c | ||
46 | #define cmd_802_11_rf_channel 0x001d | ||
47 | #define cmd_802_11_rf_tx_power 0x001e | ||
48 | #define cmd_802_11_rssi 0x001f | ||
49 | #define cmd_802_11_rf_antenna 0x0020 | ||
50 | |||
51 | #define cmd_802_11_ps_mode 0x0021 | ||
52 | |||
53 | #define cmd_802_11_data_rate 0x0022 | ||
54 | #define cmd_rf_reg_map 0x0023 | ||
55 | #define cmd_802_11_deauthenticate 0x0024 | ||
56 | #define cmd_802_11_reassociate 0x0025 | ||
57 | #define cmd_802_11_disassociate 0x0026 | ||
58 | #define cmd_mac_control 0x0028 | ||
59 | #define cmd_802_11_ad_hoc_start 0x002b | ||
60 | #define cmd_802_11_ad_hoc_join 0x002c | ||
61 | |||
62 | #define cmd_802_11_query_tkip_reply_cntrs 0x002e | ||
63 | #define cmd_802_11_enable_rsn 0x002f | ||
64 | #define cmd_802_11_pairwise_tsc 0x0036 | ||
65 | #define cmd_802_11_group_tsc 0x0037 | ||
66 | #define cmd_802_11_key_material 0x005e | ||
67 | |||
68 | #define cmd_802_11_set_afc 0x003c | ||
69 | #define cmd_802_11_get_afc 0x003d | ||
70 | |||
71 | #define cmd_802_11_ad_hoc_stop 0x0040 | ||
72 | |||
73 | #define cmd_802_11_beacon_stop 0x0049 | ||
74 | |||
75 | #define cmd_802_11_mac_address 0x004D | ||
76 | #define cmd_802_11_eeprom_access 0x0059 | ||
77 | |||
78 | #define cmd_802_11_band_config 0x0058 | ||
79 | |||
80 | #define cmd_802_11d_domain_info 0x005b | ||
81 | |||
82 | #define cmd_802_11_sleep_params 0x0066 | ||
83 | |||
84 | #define cmd_802_11_inactivity_timeout 0x0067 | ||
85 | |||
86 | #define cmd_802_11_tpc_cfg 0x0072 | ||
87 | #define cmd_802_11_pwr_cfg 0x0073 | ||
88 | |||
89 | #define cmd_802_11_led_gpio_ctrl 0x004e | ||
90 | |||
91 | #define cmd_802_11_subscribe_event 0x0075 | ||
92 | |||
93 | #define cmd_802_11_rate_adapt_rateset 0x0076 | ||
94 | |||
95 | #define cmd_802_11_tx_rate_query 0x007f | ||
96 | |||
97 | #define cmd_get_tsf 0x0080 | ||
98 | |||
99 | #define cmd_bt_access 0x0087 | ||
100 | #define cmd_ret_bt_access 0x8087 | ||
101 | |||
102 | #define cmd_fwt_access 0x0088 | ||
103 | #define cmd_ret_fwt_access 0x8088 | ||
104 | |||
105 | #define cmd_mesh_access 0x0090 | ||
106 | #define cmd_ret_mesh_access 0x8090 | ||
107 | |||
108 | /* For the IEEE Power Save */ | ||
109 | #define cmd_subcmd_enter_ps 0x0030 | ||
110 | #define cmd_subcmd_exit_ps 0x0031 | ||
111 | #define cmd_subcmd_sleep_confirmed 0x0034 | ||
112 | #define cmd_subcmd_full_powerdown 0x0035 | ||
113 | #define cmd_subcmd_full_powerup 0x0036 | ||
114 | |||
115 | /* command RET code, MSB is set to 1 */ | ||
116 | #define cmd_ret_hw_spec_info 0x8003 | ||
117 | #define cmd_ret_eeprom_update 0x8004 | ||
118 | #define cmd_ret_802_11_reset 0x8005 | ||
119 | #define cmd_ret_802_11_scan 0x8006 | ||
120 | #define cmd_ret_802_11_get_log 0x800b | ||
121 | #define cmd_ret_mac_control 0x8028 | ||
122 | #define cmd_ret_mac_multicast_adr 0x8010 | ||
123 | #define cmd_ret_802_11_authenticate 0x8011 | ||
124 | #define cmd_ret_802_11_deauthenticate 0x8024 | ||
125 | #define cmd_ret_802_11_associate 0x8012 | ||
126 | #define cmd_ret_802_11_reassociate 0x8025 | ||
127 | #define cmd_ret_802_11_disassociate 0x8026 | ||
128 | #define cmd_ret_802_11_set_wep 0x8013 | ||
129 | #define cmd_ret_802_11_stat 0x8014 | ||
130 | #define cmd_ret_802_3_stat 0x8015 | ||
131 | #define cmd_ret_802_11_snmp_mib 0x8016 | ||
132 | #define cmd_ret_mac_reg_map 0x8017 | ||
133 | #define cmd_ret_bbp_reg_map 0x8018 | ||
134 | #define cmd_ret_rf_reg_map 0x8023 | ||
135 | #define cmd_ret_mac_reg_access 0x8019 | ||
136 | #define cmd_ret_bbp_reg_access 0x801a | ||
137 | #define cmd_ret_rf_reg_access 0x801b | ||
138 | #define cmd_ret_802_11_radio_control 0x801c | ||
139 | #define cmd_ret_802_11_rf_channel 0x801d | ||
140 | #define cmd_ret_802_11_rssi 0x801f | ||
141 | #define cmd_ret_802_11_rf_tx_power 0x801e | ||
142 | #define cmd_ret_802_11_rf_antenna 0x8020 | ||
143 | #define cmd_ret_802_11_ps_mode 0x8021 | ||
144 | #define cmd_ret_802_11_data_rate 0x8022 | ||
145 | |||
146 | #define cmd_ret_802_11_ad_hoc_start 0x802B | ||
147 | #define cmd_ret_802_11_ad_hoc_join 0x802C | ||
148 | |||
149 | #define cmd_ret_802_11_query_tkip_reply_cntrs 0x802e | ||
150 | #define cmd_ret_802_11_enable_rsn 0x802f | ||
151 | #define cmd_ret_802_11_pairwise_tsc 0x8036 | ||
152 | #define cmd_ret_802_11_group_tsc 0x8037 | ||
153 | #define cmd_ret_802_11_key_material 0x805e | ||
154 | |||
155 | #define cmd_enable_rsn 0x0001 | ||
156 | #define cmd_disable_rsn 0x0000 | ||
157 | |||
158 | #define cmd_act_set 0x0001 | ||
159 | #define cmd_act_get 0x0000 | ||
160 | |||
161 | #define cmd_act_get_AES (cmd_act_get + 2) | ||
162 | #define cmd_act_set_AES (cmd_act_set + 2) | ||
163 | #define cmd_act_remove_aes (cmd_act_set + 3) | ||
164 | |||
165 | #define cmd_ret_802_11_set_afc 0x803c | ||
166 | #define cmd_ret_802_11_get_afc 0x803d | ||
167 | |||
168 | #define cmd_ret_802_11_ad_hoc_stop 0x8040 | ||
169 | |||
170 | #define cmd_ret_802_11_beacon_stop 0x8049 | ||
171 | |||
172 | #define cmd_ret_802_11_mac_address 0x804D | ||
173 | #define cmd_ret_802_11_eeprom_access 0x8059 | ||
174 | |||
175 | #define cmd_ret_802_11_band_config 0x8058 | ||
176 | |||
177 | #define cmd_ret_802_11_sleep_params 0x8066 | ||
178 | |||
179 | #define cmd_ret_802_11_inactivity_timeout 0x8067 | ||
180 | |||
181 | #define cmd_ret_802_11d_domain_info (0x8000 | \ | ||
182 | cmd_802_11d_domain_info) | ||
183 | |||
184 | #define cmd_ret_802_11_tpc_cfg (cmd_802_11_tpc_cfg | 0x8000) | ||
185 | #define cmd_ret_802_11_pwr_cfg (cmd_802_11_pwr_cfg | 0x8000) | ||
186 | |||
187 | #define cmd_ret_802_11_led_gpio_ctrl 0x804e | ||
188 | |||
189 | #define cmd_ret_802_11_subscribe_event (cmd_802_11_subscribe_event | 0x8000) | ||
190 | |||
191 | #define cmd_ret_802_11_rate_adapt_rateset (cmd_802_11_rate_adapt_rateset | 0x8000) | ||
192 | |||
193 | #define cmd_rte_802_11_tx_rate_query (cmd_802_11_tx_rate_query | 0x8000) | ||
194 | |||
195 | #define cmd_ret_get_tsf 0x8080 | ||
196 | |||
197 | /* Define action or option for cmd_802_11_set_wep */ | ||
198 | #define cmd_act_add 0x0002 | ||
199 | #define cmd_act_remove 0x0004 | ||
200 | #define cmd_act_use_default 0x0008 | ||
201 | |||
202 | #define cmd_type_wep_40_bit 0x0001 | ||
203 | #define cmd_type_wep_104_bit 0x0002 | ||
204 | |||
205 | #define cmd_NUM_OF_WEP_KEYS 4 | ||
206 | |||
207 | #define cmd_WEP_KEY_INDEX_MASK 0x3fff | ||
208 | |||
209 | /* Define action or option for cmd_802_11_reset */ | ||
210 | #define cmd_act_halt 0x0003 | ||
211 | |||
212 | /* Define action or option for cmd_802_11_scan */ | ||
213 | #define cmd_bss_type_bss 0x0001 | ||
214 | #define cmd_bss_type_ibss 0x0002 | ||
215 | #define cmd_bss_type_any 0x0003 | ||
216 | |||
217 | /* Define action or option for cmd_802_11_scan */ | ||
218 | #define cmd_scan_type_active 0x0000 | ||
219 | #define cmd_scan_type_passive 0x0001 | ||
220 | |||
221 | #define cmd_scan_radio_type_bg 0 | ||
222 | |||
223 | #define cmd_scan_probe_delay_time 0 | ||
224 | |||
225 | /* Define action or option for cmd_mac_control */ | ||
226 | #define cmd_act_mac_rx_on 0x0001 | ||
227 | #define cmd_act_mac_tx_on 0x0002 | ||
228 | #define cmd_act_mac_loopback_on 0x0004 | ||
229 | #define cmd_act_mac_wep_enable 0x0008 | ||
230 | #define cmd_act_mac_int_enable 0x0010 | ||
231 | #define cmd_act_mac_multicast_enable 0x0020 | ||
232 | #define cmd_act_mac_broadcast_enable 0x0040 | ||
233 | #define cmd_act_mac_promiscuous_enable 0x0080 | ||
234 | #define cmd_act_mac_all_multicast_enable 0x0100 | ||
235 | #define cmd_act_mac_strict_protection_enable 0x0400 | ||
236 | |||
237 | /* Define action or option for cmd_802_11_radio_control */ | ||
238 | #define cmd_type_auto_preamble 0x0001 | ||
239 | #define cmd_type_short_preamble 0x0002 | ||
240 | #define cmd_type_long_preamble 0x0003 | ||
241 | |||
242 | #define TURN_ON_RF 0x01 | ||
243 | #define RADIO_ON 0x01 | ||
244 | #define RADIO_OFF 0x00 | ||
245 | |||
246 | #define SET_AUTO_PREAMBLE 0x05 | ||
247 | #define SET_SHORT_PREAMBLE 0x03 | ||
248 | #define SET_LONG_PREAMBLE 0x01 | ||
249 | |||
250 | /* Define action or option for CMD_802_11_RF_CHANNEL */ | ||
251 | #define cmd_opt_802_11_rf_channel_get 0x00 | ||
252 | #define cmd_opt_802_11_rf_channel_set 0x01 | ||
253 | |||
254 | /* Define action or option for cmd_802_11_rf_tx_power */ | ||
255 | #define cmd_act_tx_power_opt_get 0x0000 | ||
256 | #define cmd_act_tx_power_opt_set_high 0x8007 | ||
257 | #define cmd_act_tx_power_opt_set_mid 0x8004 | ||
258 | #define cmd_act_tx_power_opt_set_low 0x8000 | ||
259 | |||
260 | #define cmd_act_tx_power_index_high 0x0007 | ||
261 | #define cmd_act_tx_power_index_mid 0x0004 | ||
262 | #define cmd_act_tx_power_index_low 0x0000 | ||
263 | |||
264 | /* Define action or option for cmd_802_11_data_rate */ | ||
265 | #define cmd_act_set_tx_auto 0x0000 | ||
266 | #define cmd_act_set_tx_fix_rate 0x0001 | ||
267 | #define cmd_act_get_tx_rate 0x0002 | ||
268 | |||
269 | #define cmd_act_set_rx 0x0001 | ||
270 | #define cmd_act_set_tx 0x0002 | ||
271 | #define cmd_act_set_both 0x0003 | ||
272 | #define cmd_act_get_rx 0x0004 | ||
273 | #define cmd_act_get_tx 0x0008 | ||
274 | #define cmd_act_get_both 0x000c | ||
275 | |||
276 | /* Define action or option for cmd_802_11_ps_mode */ | ||
277 | #define cmd_type_cam 0x0000 | ||
278 | #define cmd_type_max_psp 0x0001 | ||
279 | #define cmd_type_fast_psp 0x0002 | ||
280 | |||
281 | /* Define action or option for cmd_bt_access */ | ||
282 | enum cmd_bt_access_opts { | ||
283 | /* The bt commands start at 5 instead of 1 because the old dft commands | ||
284 | * are mapped to 1-4. These old commands are no longer maintained and | ||
285 | * should not be called. | ||
286 | */ | ||
287 | cmd_act_bt_access_add = 5, | ||
288 | cmd_act_bt_access_del, | ||
289 | cmd_act_bt_access_list, | ||
290 | cmd_act_bt_access_reset | ||
291 | }; | ||
292 | |||
293 | /* Define action or option for cmd_fwt_access */ | ||
294 | enum cmd_fwt_access_opts { | ||
295 | cmd_act_fwt_access_add = 1, | ||
296 | cmd_act_fwt_access_del, | ||
297 | cmd_act_fwt_access_lookup, | ||
298 | cmd_act_fwt_access_list, | ||
299 | cmd_act_fwt_access_list_route, | ||
300 | cmd_act_fwt_access_list_neighbor, | ||
301 | cmd_act_fwt_access_reset, | ||
302 | cmd_act_fwt_access_cleanup, | ||
303 | cmd_act_fwt_access_time, | ||
304 | }; | ||
305 | |||
306 | /* Define action or option for cmd_mesh_access */ | ||
307 | enum cmd_mesh_access_opts { | ||
308 | cmd_act_mesh_get_ttl = 1, | ||
309 | cmd_act_mesh_set_ttl, | ||
310 | cmd_act_mesh_get_stats, | ||
311 | cmd_act_mesh_get_mpp, | ||
312 | cmd_act_mesh_set_mpp, | ||
313 | }; | ||
314 | |||
315 | /** Card Event definition */ | ||
316 | #define MACREG_INT_CODE_TX_PPA_FREE 0x00000000 | ||
317 | #define MACREG_INT_CODE_TX_DMA_DONE 0x00000001 | ||
318 | #define MACREG_INT_CODE_LINK_LOSE_W_SCAN 0x00000002 | ||
319 | #define MACREG_INT_CODE_LINK_LOSE_NO_SCAN 0x00000003 | ||
320 | #define MACREG_INT_CODE_LINK_SENSED 0x00000004 | ||
321 | #define MACREG_INT_CODE_CMD_FINISHED 0x00000005 | ||
322 | #define MACREG_INT_CODE_MIB_CHANGED 0x00000006 | ||
323 | #define MACREG_INT_CODE_INIT_DONE 0x00000007 | ||
324 | #define MACREG_INT_CODE_DEAUTHENTICATED 0x00000008 | ||
325 | #define MACREG_INT_CODE_DISASSOCIATED 0x00000009 | ||
326 | #define MACREG_INT_CODE_PS_AWAKE 0x0000000a | ||
327 | #define MACREG_INT_CODE_PS_SLEEP 0x0000000b | ||
328 | #define MACREG_INT_CODE_MIC_ERR_MULTICAST 0x0000000d | ||
329 | #define MACREG_INT_CODE_MIC_ERR_UNICAST 0x0000000e | ||
330 | #define MACREG_INT_CODE_WM_AWAKE 0x0000000f | ||
331 | #define MACREG_INT_CODE_ADHOC_BCN_LOST 0x00000011 | ||
332 | #define MACREG_INT_CODE_RSSI_LOW 0x00000019 | ||
333 | #define MACREG_INT_CODE_SNR_LOW 0x0000001a | ||
334 | #define MACREG_INT_CODE_MAX_FAIL 0x0000001b | ||
335 | #define MACREG_INT_CODE_RSSI_HIGH 0x0000001c | ||
336 | #define MACREG_INT_CODE_SNR_HIGH 0x0000001d | ||
337 | |||
338 | #endif /* _HOST_H_ */ | ||
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h new file mode 100644 index 000000000000..f239e5d2435b --- /dev/null +++ b/drivers/net/wireless/libertas/hostcmd.h | |||
@@ -0,0 +1,693 @@ | |||
1 | /* | ||
2 | * This file contains the function prototypes, data structure | ||
3 | * and defines for all the host/station commands | ||
4 | */ | ||
5 | #ifndef __HOSTCMD__H | ||
6 | #define __HOSTCMD__H | ||
7 | |||
8 | #include <linux/wireless.h> | ||
9 | #include "11d.h" | ||
10 | #include "types.h" | ||
11 | |||
12 | /* 802.11-related definitions */ | ||
13 | |||
14 | /* TxPD descriptor */ | ||
15 | struct txpd { | ||
16 | /* Current Tx packet status */ | ||
17 | u32 tx_status; | ||
18 | /* Tx control */ | ||
19 | u32 tx_control; | ||
20 | u32 tx_packet_location; | ||
21 | /* Tx packet length */ | ||
22 | u16 tx_packet_length; | ||
23 | /* First 2 byte of destination MAC address */ | ||
24 | u8 tx_dest_addr_high[2]; | ||
25 | /* Last 4 byte of destination MAC address */ | ||
26 | u8 tx_dest_addr_low[4]; | ||
27 | /* Pkt Priority */ | ||
28 | u8 priority; | ||
29 | /* Pkt Trasnit Power control */ | ||
30 | u8 powermgmt; | ||
31 | /* Amount of time the packet has been queued in the driver (units = 2ms) */ | ||
32 | u8 pktdelay_2ms; | ||
33 | /* reserved */ | ||
34 | u8 reserved1; | ||
35 | }; | ||
36 | |||
37 | /* RxPD Descriptor */ | ||
38 | struct rxpd { | ||
39 | /* Current Rx packet status */ | ||
40 | u16 status; | ||
41 | |||
42 | /* SNR */ | ||
43 | u8 snr; | ||
44 | |||
45 | /* Tx control */ | ||
46 | u8 rx_control; | ||
47 | |||
48 | /* Pkt length */ | ||
49 | u16 pkt_len; | ||
50 | |||
51 | /* Noise Floor */ | ||
52 | u8 nf; | ||
53 | |||
54 | /* Rx Packet Rate */ | ||
55 | u8 rx_rate; | ||
56 | |||
57 | /* Pkt addr */ | ||
58 | u32 pkt_ptr; | ||
59 | |||
60 | /* Next Rx RxPD addr */ | ||
61 | u32 next_rxpd_ptr; | ||
62 | |||
63 | /* Pkt Priority */ | ||
64 | u8 priority; | ||
65 | u8 reserved[3]; | ||
66 | }; | ||
67 | |||
68 | struct cmd_ctrl_node { | ||
69 | /* CMD link list */ | ||
70 | struct list_head list; | ||
71 | u32 status; | ||
72 | /* CMD ID */ | ||
73 | u32 cmd_oid; | ||
74 | /*CMD wait option: wait for finish or no wait */ | ||
75 | u16 wait_option; | ||
76 | /* command parameter */ | ||
77 | void *pdata_buf; | ||
78 | /*command data */ | ||
79 | u8 *bufvirtualaddr; | ||
80 | u16 cmdflags; | ||
81 | /* wait queue */ | ||
82 | u16 cmdwaitqwoken; | ||
83 | wait_queue_head_t cmdwait_q; | ||
84 | }; | ||
85 | |||
86 | /* WLAN_802_11_KEY | ||
87 | * | ||
88 | * Generic structure to hold all key types. key type (WEP40, WEP104, TKIP, AES) | ||
89 | * is determined from the keylength field. | ||
90 | */ | ||
91 | struct WLAN_802_11_KEY { | ||
92 | u32 len; | ||
93 | u32 flags; /* KEY_INFO_* from wlan_defs.h */ | ||
94 | u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH]; | ||
95 | u16 type; /* KEY_TYPE_* from wlan_defs.h */ | ||
96 | }; | ||
97 | |||
98 | struct IE_WPA { | ||
99 | u8 elementid; | ||
100 | u8 len; | ||
101 | u8 oui[4]; | ||
102 | u16 version; | ||
103 | }; | ||
104 | |||
105 | struct WLAN_802_11_SSID { | ||
106 | /* SSID length */ | ||
107 | u32 ssidlength; | ||
108 | |||
109 | /* SSID information field */ | ||
110 | u8 ssid[IW_ESSID_MAX_SIZE]; | ||
111 | }; | ||
112 | |||
113 | struct WPA_SUPPLICANT { | ||
114 | u8 wpa_ie[256]; | ||
115 | u8 wpa_ie_len; | ||
116 | }; | ||
117 | |||
118 | /* wlan_offset_value */ | ||
119 | struct wlan_offset_value { | ||
120 | u32 offset; | ||
121 | u32 value; | ||
122 | }; | ||
123 | |||
124 | struct WLAN_802_11_FIXED_IEs { | ||
125 | u8 timestamp[8]; | ||
126 | u16 beaconinterval; | ||
127 | u16 capabilities; | ||
128 | }; | ||
129 | |||
130 | struct WLAN_802_11_VARIABLE_IEs { | ||
131 | u8 elementid; | ||
132 | u8 length; | ||
133 | u8 data[1]; | ||
134 | }; | ||
135 | |||
136 | /* Define general data structure */ | ||
137 | /* cmd_DS_GEN */ | ||
138 | struct cmd_ds_gen { | ||
139 | u16 command; | ||
140 | u16 size; | ||
141 | u16 seqnum; | ||
142 | u16 result; | ||
143 | }; | ||
144 | |||
145 | #define S_DS_GEN sizeof(struct cmd_ds_gen) | ||
146 | /* | ||
147 | * Define data structure for cmd_get_hw_spec | ||
148 | * This structure defines the response for the GET_HW_SPEC command | ||
149 | */ | ||
150 | struct cmd_ds_get_hw_spec { | ||
151 | /* HW Interface version number */ | ||
152 | u16 hwifversion; | ||
153 | /* HW version number */ | ||
154 | u16 version; | ||
155 | /* Max number of TxPD FW can handle */ | ||
156 | u16 nr_txpd; | ||
157 | /* Max no of Multicast address */ | ||
158 | u16 nr_mcast_adr; | ||
159 | /* MAC address */ | ||
160 | u8 permanentaddr[6]; | ||
161 | |||
162 | /* region Code */ | ||
163 | u16 regioncode; | ||
164 | |||
165 | /* Number of antenna used */ | ||
166 | u16 nr_antenna; | ||
167 | |||
168 | /* FW release number, example 0x1234=1.2.3.4 */ | ||
169 | u32 fwreleasenumber; | ||
170 | |||
171 | /* Base Address of TxPD queue */ | ||
172 | u32 wcb_base; | ||
173 | /* Read Pointer of RxPd queue */ | ||
174 | u32 rxpd_rdptr; | ||
175 | |||
176 | /* Write Pointer of RxPd queue */ | ||
177 | u32 rxpd_wrptr; | ||
178 | |||
179 | /*FW/HW capability */ | ||
180 | u32 fwcapinfo; | ||
181 | } __attribute__ ((packed)); | ||
182 | |||
183 | struct cmd_ds_802_11_reset { | ||
184 | u16 action; | ||
185 | }; | ||
186 | |||
187 | struct cmd_ds_802_11_subscribe_event { | ||
188 | u16 action; | ||
189 | u16 events; | ||
190 | }; | ||
191 | |||
192 | /* | ||
193 | * This scan handle Country Information IE(802.11d compliant) | ||
194 | * Define data structure for cmd_802_11_scan | ||
195 | */ | ||
196 | struct cmd_ds_802_11_scan { | ||
197 | u8 bsstype; | ||
198 | u8 BSSID[ETH_ALEN]; | ||
199 | u8 tlvbuffer[1]; | ||
200 | #if 0 | ||
201 | mrvlietypes_ssidparamset_t ssidParamSet; | ||
202 | mrvlietypes_chanlistparamset_t ChanListParamSet; | ||
203 | mrvlietypes_ratesparamset_t OpRateSet; | ||
204 | #endif | ||
205 | }; | ||
206 | |||
207 | struct cmd_ds_802_11_scan_rsp { | ||
208 | u16 bssdescriptsize; | ||
209 | u8 nr_sets; | ||
210 | u8 bssdesc_and_tlvbuffer[1]; | ||
211 | }; | ||
212 | |||
213 | struct cmd_ds_802_11_get_log { | ||
214 | u32 mcasttxframe; | ||
215 | u32 failed; | ||
216 | u32 retry; | ||
217 | u32 multiretry; | ||
218 | u32 framedup; | ||
219 | u32 rtssuccess; | ||
220 | u32 rtsfailure; | ||
221 | u32 ackfailure; | ||
222 | u32 rxfrag; | ||
223 | u32 mcastrxframe; | ||
224 | u32 fcserror; | ||
225 | u32 txframe; | ||
226 | u32 wepundecryptable; | ||
227 | }; | ||
228 | |||
229 | struct cmd_ds_mac_control { | ||
230 | u16 action; | ||
231 | u16 reserved; | ||
232 | }; | ||
233 | |||
234 | struct cmd_ds_mac_multicast_adr { | ||
235 | u16 action; | ||
236 | u16 nr_of_adrs; | ||
237 | u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; | ||
238 | }; | ||
239 | |||
240 | struct cmd_ds_802_11_authenticate { | ||
241 | u8 macaddr[ETH_ALEN]; | ||
242 | u8 authtype; | ||
243 | u8 reserved[10]; | ||
244 | }; | ||
245 | |||
246 | struct cmd_ds_802_11_deauthenticate { | ||
247 | u8 macaddr[6]; | ||
248 | u16 reasoncode; | ||
249 | }; | ||
250 | |||
251 | struct cmd_ds_802_11_associate { | ||
252 | u8 peerstaaddr[6]; | ||
253 | struct ieeetypes_capinfo capinfo; | ||
254 | u16 listeninterval; | ||
255 | u16 bcnperiod; | ||
256 | u8 dtimperiod; | ||
257 | |||
258 | #if 0 | ||
259 | mrvlietypes_ssidparamset_t ssidParamSet; | ||
260 | mrvlietypes_phyparamset_t phyparamset; | ||
261 | mrvlietypes_ssparamset_t ssparamset; | ||
262 | mrvlietypes_ratesparamset_t ratesParamSet; | ||
263 | #endif | ||
264 | } __attribute__ ((packed)); | ||
265 | |||
266 | struct cmd_ds_802_11_disassociate { | ||
267 | u8 destmacaddr[6]; | ||
268 | u16 reasoncode; | ||
269 | }; | ||
270 | |||
271 | struct cmd_ds_802_11_associate_rsp { | ||
272 | struct ieeetypes_assocrsp assocRsp; | ||
273 | }; | ||
274 | |||
275 | struct cmd_ds_802_11_ad_hoc_result { | ||
276 | u8 PAD[3]; | ||
277 | u8 BSSID[ETH_ALEN]; | ||
278 | }; | ||
279 | |||
280 | struct cmd_ds_802_11_set_wep { | ||
281 | /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ | ||
282 | u16 action; | ||
283 | |||
284 | /* key Index selected for Tx */ | ||
285 | u16 keyindex; | ||
286 | |||
287 | /* 40, 128bit or TXWEP */ | ||
288 | u8 keytype[4]; | ||
289 | u8 keymaterial[4][16]; | ||
290 | }; | ||
291 | |||
292 | struct cmd_ds_802_3_get_stat { | ||
293 | u32 xmitok; | ||
294 | u32 rcvok; | ||
295 | u32 xmiterror; | ||
296 | u32 rcverror; | ||
297 | u32 rcvnobuffer; | ||
298 | u32 rcvcrcerror; | ||
299 | }; | ||
300 | |||
301 | struct cmd_ds_802_11_get_stat { | ||
302 | u32 txfragmentcnt; | ||
303 | u32 mcasttxframecnt; | ||
304 | u32 failedcnt; | ||
305 | u32 retrycnt; | ||
306 | u32 Multipleretrycnt; | ||
307 | u32 rtssuccesscnt; | ||
308 | u32 rtsfailurecnt; | ||
309 | u32 ackfailurecnt; | ||
310 | u32 frameduplicatecnt; | ||
311 | u32 rxfragmentcnt; | ||
312 | u32 mcastrxframecnt; | ||
313 | u32 fcserrorcnt; | ||
314 | u32 bcasttxframecnt; | ||
315 | u32 bcastrxframecnt; | ||
316 | u32 txbeacon; | ||
317 | u32 rxbeacon; | ||
318 | u32 wepundecryptable; | ||
319 | }; | ||
320 | |||
321 | struct cmd_ds_802_11_snmp_mib { | ||
322 | u16 querytype; | ||
323 | u16 oid; | ||
324 | u16 bufsize; | ||
325 | u8 value[128]; | ||
326 | }; | ||
327 | |||
328 | struct cmd_ds_mac_reg_map { | ||
329 | u16 buffersize; | ||
330 | u8 regmap[128]; | ||
331 | u16 reserved; | ||
332 | }; | ||
333 | |||
334 | struct cmd_ds_bbp_reg_map { | ||
335 | u16 buffersize; | ||
336 | u8 regmap[128]; | ||
337 | u16 reserved; | ||
338 | }; | ||
339 | |||
340 | struct cmd_ds_rf_reg_map { | ||
341 | u16 buffersize; | ||
342 | u8 regmap[64]; | ||
343 | u16 reserved; | ||
344 | }; | ||
345 | |||
346 | struct cmd_ds_mac_reg_access { | ||
347 | u16 action; | ||
348 | u16 offset; | ||
349 | u32 value; | ||
350 | }; | ||
351 | |||
352 | struct cmd_ds_bbp_reg_access { | ||
353 | u16 action; | ||
354 | u16 offset; | ||
355 | u8 value; | ||
356 | u8 reserved[3]; | ||
357 | }; | ||
358 | |||
359 | struct cmd_ds_rf_reg_access { | ||
360 | u16 action; | ||
361 | u16 offset; | ||
362 | u8 value; | ||
363 | u8 reserved[3]; | ||
364 | }; | ||
365 | |||
366 | struct cmd_ds_802_11_radio_control { | ||
367 | u16 action; | ||
368 | u16 control; | ||
369 | }; | ||
370 | |||
371 | struct cmd_ds_802_11_sleep_params { | ||
372 | /* ACT_GET/ACT_SET */ | ||
373 | u16 action; | ||
374 | |||
375 | /* Sleep clock error in ppm */ | ||
376 | u16 error; | ||
377 | |||
378 | /* Wakeup offset in usec */ | ||
379 | u16 offset; | ||
380 | |||
381 | /* Clock stabilization time in usec */ | ||
382 | u16 stabletime; | ||
383 | |||
384 | /* control periodic calibration */ | ||
385 | u8 calcontrol; | ||
386 | |||
387 | /* control the use of external sleep clock */ | ||
388 | u8 externalsleepclk; | ||
389 | |||
390 | /* reserved field, should be set to zero */ | ||
391 | u16 reserved; | ||
392 | }; | ||
393 | |||
394 | struct cmd_ds_802_11_inactivity_timeout { | ||
395 | /* ACT_GET/ACT_SET */ | ||
396 | u16 action; | ||
397 | |||
398 | /* Inactivity timeout in msec */ | ||
399 | u16 timeout; | ||
400 | }; | ||
401 | |||
402 | struct cmd_ds_802_11_rf_channel { | ||
403 | u16 action; | ||
404 | u16 currentchannel; | ||
405 | u16 rftype; | ||
406 | u16 reserved; | ||
407 | u8 channellist[32]; | ||
408 | }; | ||
409 | |||
410 | struct cmd_ds_802_11_rssi { | ||
411 | /* weighting factor */ | ||
412 | u16 N; | ||
413 | |||
414 | u16 reserved_0; | ||
415 | u16 reserved_1; | ||
416 | u16 reserved_2; | ||
417 | }; | ||
418 | |||
419 | struct cmd_ds_802_11_rssi_rsp { | ||
420 | u16 SNR; | ||
421 | u16 noisefloor; | ||
422 | u16 avgSNR; | ||
423 | u16 avgnoisefloor; | ||
424 | }; | ||
425 | |||
426 | struct cmd_ds_802_11_mac_address { | ||
427 | u16 action; | ||
428 | u8 macadd[ETH_ALEN]; | ||
429 | }; | ||
430 | |||
431 | struct cmd_ds_802_11_rf_tx_power { | ||
432 | u16 action; | ||
433 | u16 currentlevel; | ||
434 | }; | ||
435 | |||
436 | struct cmd_ds_802_11_rf_antenna { | ||
437 | u16 action; | ||
438 | |||
439 | /* Number of antennas or 0xffff(diversity) */ | ||
440 | u16 antennamode; | ||
441 | |||
442 | }; | ||
443 | |||
444 | struct cmd_ds_802_11_ps_mode { | ||
445 | u16 action; | ||
446 | u16 nullpktinterval; | ||
447 | u16 multipledtim; | ||
448 | u16 reserved; | ||
449 | u16 locallisteninterval; | ||
450 | }; | ||
451 | |||
452 | struct PS_CMD_ConfirmSleep { | ||
453 | u16 command; | ||
454 | u16 size; | ||
455 | u16 seqnum; | ||
456 | u16 result; | ||
457 | |||
458 | u16 action; | ||
459 | u16 reserved1; | ||
460 | u16 multipledtim; | ||
461 | u16 reserved; | ||
462 | u16 locallisteninterval; | ||
463 | }; | ||
464 | |||
465 | struct cmd_ds_802_11_data_rate { | ||
466 | u16 action; | ||
467 | u16 reserverd; | ||
468 | u8 datarate[G_SUPPORTED_RATES]; | ||
469 | }; | ||
470 | |||
471 | struct cmd_ds_802_11_rate_adapt_rateset { | ||
472 | u16 action; | ||
473 | u16 enablehwauto; | ||
474 | u16 bitmap; | ||
475 | }; | ||
476 | |||
477 | struct cmd_ds_802_11_ad_hoc_start { | ||
478 | u8 SSID[IW_ESSID_MAX_SIZE]; | ||
479 | u8 bsstype; | ||
480 | u16 beaconperiod; | ||
481 | u8 dtimperiod; | ||
482 | union IEEEtypes_ssparamset ssparamset; | ||
483 | union ieeetypes_phyparamset phyparamset; | ||
484 | u16 probedelay; | ||
485 | struct ieeetypes_capinfo cap; | ||
486 | u8 datarate[G_SUPPORTED_RATES]; | ||
487 | u8 tlv_memory_size_pad[100]; | ||
488 | } __attribute__ ((packed)); | ||
489 | |||
490 | struct adhoc_bssdesc { | ||
491 | u8 BSSID[6]; | ||
492 | u8 SSID[32]; | ||
493 | u8 bsstype; | ||
494 | u16 beaconperiod; | ||
495 | u8 dtimperiod; | ||
496 | u8 timestamp[8]; | ||
497 | u8 localtime[8]; | ||
498 | union ieeetypes_phyparamset phyparamset; | ||
499 | union IEEEtypes_ssparamset ssparamset; | ||
500 | struct ieeetypes_capinfo cap; | ||
501 | u8 datarates[G_SUPPORTED_RATES]; | ||
502 | |||
503 | /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the | ||
504 | * Adhoc join command and will cause a binary layout mismatch with | ||
505 | * the firmware | ||
506 | */ | ||
507 | } __attribute__ ((packed)); | ||
508 | |||
509 | struct cmd_ds_802_11_ad_hoc_join { | ||
510 | struct adhoc_bssdesc bssdescriptor; | ||
511 | u16 failtimeout; | ||
512 | u16 probedelay; | ||
513 | |||
514 | } __attribute__ ((packed)); | ||
515 | |||
516 | struct cmd_ds_802_11_enable_rsn { | ||
517 | u16 action; | ||
518 | u16 enable; | ||
519 | }; | ||
520 | |||
521 | struct MrvlIEtype_keyParamSet { | ||
522 | /* type ID */ | ||
523 | u16 type; | ||
524 | |||
525 | /* length of Payload */ | ||
526 | u16 length; | ||
527 | |||
528 | /* type of key: WEP=0, TKIP=1, AES=2 */ | ||
529 | u16 keytypeid; | ||
530 | |||
531 | /* key control Info specific to a keytypeid */ | ||
532 | u16 keyinfo; | ||
533 | |||
534 | /* length of key */ | ||
535 | u16 keylen; | ||
536 | |||
537 | /* key material of size keylen */ | ||
538 | u8 key[32]; | ||
539 | }; | ||
540 | |||
541 | struct cmd_ds_802_11_key_material { | ||
542 | u16 action; | ||
543 | struct MrvlIEtype_keyParamSet keyParamSet[2]; | ||
544 | } __attribute__ ((packed)); | ||
545 | |||
546 | struct cmd_ds_802_11_eeprom_access { | ||
547 | u16 action; | ||
548 | |||
549 | /* multiple 4 */ | ||
550 | u16 offset; | ||
551 | u16 bytecount; | ||
552 | u8 value; | ||
553 | } __attribute__ ((packed)); | ||
554 | |||
555 | struct cmd_ds_802_11_tpc_cfg { | ||
556 | u16 action; | ||
557 | u8 enable; | ||
558 | s8 P0; | ||
559 | s8 P1; | ||
560 | s8 P2; | ||
561 | u8 usesnr; | ||
562 | } __attribute__ ((packed)); | ||
563 | |||
564 | struct cmd_ds_802_11_led_ctrl { | ||
565 | u16 action; | ||
566 | u16 numled; | ||
567 | u8 data[256]; | ||
568 | } __attribute__ ((packed)); | ||
569 | |||
570 | struct cmd_ds_802_11_pwr_cfg { | ||
571 | u16 action; | ||
572 | u8 enable; | ||
573 | s8 PA_P0; | ||
574 | s8 PA_P1; | ||
575 | s8 PA_P2; | ||
576 | } __attribute__ ((packed)); | ||
577 | |||
578 | struct cmd_ds_802_11_afc { | ||
579 | u16 afc_auto; | ||
580 | union { | ||
581 | struct { | ||
582 | u16 threshold; | ||
583 | u16 period; | ||
584 | }; | ||
585 | struct { | ||
586 | s16 timing_offset; | ||
587 | s16 carrier_offset; | ||
588 | }; | ||
589 | }; | ||
590 | } __attribute__ ((packed)); | ||
591 | |||
592 | struct cmd_tx_rate_query { | ||
593 | u16 txrate; | ||
594 | } __attribute__ ((packed)); | ||
595 | |||
596 | struct cmd_ds_get_tsf { | ||
597 | __le64 tsfvalue; | ||
598 | } __attribute__ ((packed)); | ||
599 | |||
600 | struct cmd_ds_bt_access { | ||
601 | u16 action; | ||
602 | u32 id; | ||
603 | u8 addr1[ETH_ALEN]; | ||
604 | u8 addr2[ETH_ALEN]; | ||
605 | } __attribute__ ((packed)); | ||
606 | |||
607 | struct cmd_ds_fwt_access { | ||
608 | u16 action; | ||
609 | u32 id; | ||
610 | u8 da[ETH_ALEN]; | ||
611 | u8 dir; | ||
612 | u8 ra[ETH_ALEN]; | ||
613 | u32 ssn; | ||
614 | u32 dsn; | ||
615 | u32 metric; | ||
616 | u8 hopcount; | ||
617 | u8 ttl; | ||
618 | u32 expiration; | ||
619 | u8 sleepmode; | ||
620 | u32 snr; | ||
621 | u32 references; | ||
622 | } __attribute__ ((packed)); | ||
623 | |||
624 | #define MESH_STATS_NUM 7 | ||
625 | struct cmd_ds_mesh_access { | ||
626 | u16 action; | ||
627 | u32 data[MESH_STATS_NUM + 1]; /* last position reserved */ | ||
628 | } __attribute__ ((packed)); | ||
629 | |||
630 | struct cmd_ds_command { | ||
631 | /* command header */ | ||
632 | u16 command; | ||
633 | u16 size; | ||
634 | u16 seqnum; | ||
635 | u16 result; | ||
636 | |||
637 | /* command Body */ | ||
638 | union { | ||
639 | struct cmd_ds_get_hw_spec hwspec; | ||
640 | struct cmd_ds_802_11_ps_mode psmode; | ||
641 | struct cmd_ds_802_11_scan scan; | ||
642 | struct cmd_ds_802_11_scan_rsp scanresp; | ||
643 | struct cmd_ds_mac_control macctrl; | ||
644 | struct cmd_ds_802_11_associate associate; | ||
645 | struct cmd_ds_802_11_deauthenticate deauth; | ||
646 | struct cmd_ds_802_11_set_wep wep; | ||
647 | struct cmd_ds_802_11_ad_hoc_start ads; | ||
648 | struct cmd_ds_802_11_reset reset; | ||
649 | struct cmd_ds_802_11_ad_hoc_result result; | ||
650 | struct cmd_ds_802_11_get_log glog; | ||
651 | struct cmd_ds_802_11_authenticate auth; | ||
652 | struct cmd_ds_802_11_get_stat gstat; | ||
653 | struct cmd_ds_802_3_get_stat gstat_8023; | ||
654 | struct cmd_ds_802_11_snmp_mib smib; | ||
655 | struct cmd_ds_802_11_rf_tx_power txp; | ||
656 | struct cmd_ds_802_11_rf_antenna rant; | ||
657 | struct cmd_ds_802_11_data_rate drate; | ||
658 | struct cmd_ds_802_11_rate_adapt_rateset rateset; | ||
659 | struct cmd_ds_mac_multicast_adr madr; | ||
660 | struct cmd_ds_802_11_ad_hoc_join adj; | ||
661 | struct cmd_ds_802_11_radio_control radio; | ||
662 | struct cmd_ds_802_11_rf_channel rfchannel; | ||
663 | struct cmd_ds_802_11_rssi rssi; | ||
664 | struct cmd_ds_802_11_rssi_rsp rssirsp; | ||
665 | struct cmd_ds_802_11_disassociate dassociate; | ||
666 | struct cmd_ds_802_11_mac_address macadd; | ||
667 | struct cmd_ds_802_11_enable_rsn enbrsn; | ||
668 | struct cmd_ds_802_11_key_material keymaterial; | ||
669 | struct cmd_ds_mac_reg_access macreg; | ||
670 | struct cmd_ds_bbp_reg_access bbpreg; | ||
671 | struct cmd_ds_rf_reg_access rfreg; | ||
672 | struct cmd_ds_802_11_eeprom_access rdeeprom; | ||
673 | |||
674 | struct cmd_ds_802_11d_domain_info domaininfo; | ||
675 | struct cmd_ds_802_11d_domain_info domaininforesp; | ||
676 | |||
677 | struct cmd_ds_802_11_sleep_params sleep_params; | ||
678 | struct cmd_ds_802_11_inactivity_timeout inactivity_timeout; | ||
679 | struct cmd_ds_802_11_tpc_cfg tpccfg; | ||
680 | struct cmd_ds_802_11_pwr_cfg pwrcfg; | ||
681 | struct cmd_ds_802_11_afc afc; | ||
682 | struct cmd_ds_802_11_led_ctrl ledgpio; | ||
683 | |||
684 | struct cmd_tx_rate_query txrate; | ||
685 | struct cmd_ds_bt_access bt; | ||
686 | struct cmd_ds_fwt_access fwt; | ||
687 | struct cmd_ds_mesh_access mesh; | ||
688 | struct cmd_ds_get_tsf gettsf; | ||
689 | struct cmd_ds_802_11_subscribe_event subscribe_event; | ||
690 | } params; | ||
691 | } __attribute__ ((packed)); | ||
692 | |||
693 | #endif | ||
diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c new file mode 100644 index 000000000000..567000c3e87b --- /dev/null +++ b/drivers/net/wireless/libertas/if_bootcmd.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /** | ||
2 | * This file contains functions used in USB Boot command | ||
3 | * and Boot2/FW update | ||
4 | */ | ||
5 | |||
6 | #include <linux/delay.h> | ||
7 | #include <linux/firmware.h> | ||
8 | #include <linux/netdevice.h> | ||
9 | #include <linux/usb.h> | ||
10 | |||
11 | #include "defs.h" | ||
12 | #include "dev.h" | ||
13 | #include "if_usb.h" | ||
14 | |||
15 | /** | ||
16 | * @brief This function issues Boot command to the Boot2 code | ||
17 | * @param ivalue 1:Boot from FW by USB-Download | ||
18 | * 2:Boot from FW in EEPROM | ||
19 | * @return 0 | ||
20 | */ | ||
21 | int if_usb_issue_boot_command(wlan_private *priv, int ivalue) | ||
22 | { | ||
23 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
24 | struct bootcmdstr sbootcmd; | ||
25 | int i; | ||
26 | |||
27 | /* Prepare command */ | ||
28 | sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER; | ||
29 | sbootcmd.u8cmd_tag = ivalue; | ||
30 | for (i=0; i<11; i++) | ||
31 | sbootcmd.au8dumy[i]=0x00; | ||
32 | memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr)); | ||
33 | |||
34 | /* Issue command */ | ||
35 | usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr)); | ||
36 | |||
37 | return 0; | ||
38 | } | ||
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c new file mode 100644 index 000000000000..695fb6a66ffe --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.c | |||
@@ -0,0 +1,952 @@ | |||
1 | /** | ||
2 | * This file contains functions used in USB interface module. | ||
3 | */ | ||
4 | #include <linux/delay.h> | ||
5 | #include <linux/firmware.h> | ||
6 | #include <linux/netdevice.h> | ||
7 | #include <linux/usb.h> | ||
8 | |||
9 | #include "host.h" | ||
10 | #include "sbi.h" | ||
11 | #include "decl.h" | ||
12 | #include "defs.h" | ||
13 | #include "dev.h" | ||
14 | #include "if_usb.h" | ||
15 | |||
16 | #define MESSAGE_HEADER_LEN 4 | ||
17 | |||
18 | static const char usbdriver_name[] = "usb8xxx"; | ||
19 | |||
20 | static struct usb_device_id if_usb_table[] = { | ||
21 | /* Enter the device signature inside */ | ||
22 | { | ||
23 | USB_DEVICE(USB8388_VID_1, USB8388_PID_1), | ||
24 | }, | ||
25 | { | ||
26 | USB_DEVICE(USB8388_VID_2, USB8388_PID_2), | ||
27 | }, | ||
28 | {} /* Terminating entry */ | ||
29 | }; | ||
30 | |||
31 | MODULE_DEVICE_TABLE(usb, if_usb_table); | ||
32 | |||
33 | static void if_usb_receive(struct urb *urb); | ||
34 | static void if_usb_receive_fwload(struct urb *urb); | ||
35 | |||
36 | /** | ||
37 | * @brief call back function to handle the status of the URB | ||
38 | * @param urb pointer to urb structure | ||
39 | * @return N/A | ||
40 | */ | ||
41 | static void if_usb_write_bulk_callback(struct urb *urb) | ||
42 | { | ||
43 | wlan_private *priv = (wlan_private *) (urb->context); | ||
44 | wlan_adapter *adapter = priv->adapter; | ||
45 | struct net_device *dev = priv->wlan_dev.netdev; | ||
46 | |||
47 | /* handle the transmission complete validations */ | ||
48 | |||
49 | if (urb->status != 0) { | ||
50 | /* print the failure status number for debug */ | ||
51 | lbs_pr_info("URB in failure status\n"); | ||
52 | } else { | ||
53 | lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n"); | ||
54 | lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n", | ||
55 | urb->actual_length); | ||
56 | priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; | ||
57 | /* Wake main thread if commands are pending */ | ||
58 | if (!adapter->cur_cmd) | ||
59 | wake_up_interruptible(&priv->mainthread.waitq); | ||
60 | if ((adapter->connect_status == libertas_connected)) | ||
61 | netif_wake_queue(dev); | ||
62 | } | ||
63 | |||
64 | return; | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * @brief free tx/rx urb, skb and rx buffer | ||
69 | * @param cardp pointer usb_card_rec | ||
70 | * @return N/A | ||
71 | */ | ||
72 | void if_usb_free(struct usb_card_rec *cardp) | ||
73 | { | ||
74 | ENTER(); | ||
75 | |||
76 | /* Unlink tx & rx urb */ | ||
77 | usb_kill_urb(cardp->tx_urb); | ||
78 | usb_kill_urb(cardp->rx_urb); | ||
79 | |||
80 | usb_free_urb(cardp->tx_urb); | ||
81 | cardp->tx_urb = NULL; | ||
82 | |||
83 | usb_free_urb(cardp->rx_urb); | ||
84 | cardp->rx_urb = NULL; | ||
85 | |||
86 | kfree(cardp->bulk_out_buffer); | ||
87 | cardp->bulk_out_buffer = NULL; | ||
88 | |||
89 | LEAVE(); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * @brief sets the configuration values | ||
95 | * @param ifnum interface number | ||
96 | * @param id pointer to usb_device_id | ||
97 | * @return 0 on success, error code on failure | ||
98 | */ | ||
99 | static int if_usb_probe(struct usb_interface *intf, | ||
100 | const struct usb_device_id *id) | ||
101 | { | ||
102 | struct usb_device *udev; | ||
103 | struct usb_host_interface *iface_desc; | ||
104 | struct usb_endpoint_descriptor *endpoint; | ||
105 | wlan_private *pwlanpriv; | ||
106 | struct usb_card_rec *usb_cardp; | ||
107 | int i; | ||
108 | |||
109 | udev = interface_to_usbdev(intf); | ||
110 | |||
111 | usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); | ||
112 | if (!usb_cardp) { | ||
113 | lbs_pr_err("Out of memory allocating private data.\n"); | ||
114 | goto error; | ||
115 | } | ||
116 | |||
117 | usb_cardp->udev = udev; | ||
118 | iface_desc = intf->cur_altsetting; | ||
119 | |||
120 | lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" | ||
121 | " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", | ||
122 | udev->descriptor.bcdUSB, | ||
123 | udev->descriptor.bDeviceClass, | ||
124 | udev->descriptor.bDeviceSubClass, | ||
125 | udev->descriptor.bDeviceProtocol); | ||
126 | |||
127 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
128 | endpoint = &iface_desc->endpoint[i].desc; | ||
129 | if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) | ||
130 | && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | ||
131 | USB_ENDPOINT_XFER_BULK)) { | ||
132 | /* we found a bulk in endpoint */ | ||
133 | lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n", | ||
134 | endpoint->wMaxPacketSize); | ||
135 | if (! | ||
136 | (usb_cardp->rx_urb = | ||
137 | usb_alloc_urb(0, GFP_KERNEL))) { | ||
138 | lbs_dev_dbg(1, &udev->dev, | ||
139 | "Rx URB allocation failed\n"); | ||
140 | goto dealloc; | ||
141 | } | ||
142 | usb_cardp->rx_urb_recall = 0; | ||
143 | |||
144 | usb_cardp->bulk_in_size = | ||
145 | endpoint->wMaxPacketSize; | ||
146 | usb_cardp->bulk_in_endpointAddr = | ||
147 | (endpoint-> | ||
148 | bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | ||
149 | lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n", | ||
150 | endpoint->bEndpointAddress); | ||
151 | } | ||
152 | |||
153 | if (((endpoint-> | ||
154 | bEndpointAddress & USB_ENDPOINT_DIR_MASK) == | ||
155 | USB_DIR_OUT) | ||
156 | && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | ||
157 | USB_ENDPOINT_XFER_BULK)) { | ||
158 | /* We found bulk out endpoint */ | ||
159 | if (! | ||
160 | (usb_cardp->tx_urb = | ||
161 | usb_alloc_urb(0, GFP_KERNEL))) { | ||
162 | lbs_dev_dbg(1,&udev->dev, | ||
163 | "Tx URB allocation failed\n"); | ||
164 | goto dealloc; | ||
165 | } | ||
166 | |||
167 | usb_cardp->bulk_out_size = | ||
168 | endpoint->wMaxPacketSize; | ||
169 | lbs_dev_dbg(1, &udev->dev, | ||
170 | "Bulk out size is %d\n", | ||
171 | endpoint->wMaxPacketSize); | ||
172 | usb_cardp->bulk_out_endpointAddr = | ||
173 | endpoint->bEndpointAddress; | ||
174 | lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n", | ||
175 | endpoint->bEndpointAddress); | ||
176 | usb_cardp->bulk_out_buffer = | ||
177 | kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, | ||
178 | GFP_KERNEL); | ||
179 | |||
180 | if (!usb_cardp->bulk_out_buffer) { | ||
181 | lbs_dev_dbg(1, &udev->dev, | ||
182 | "Could not allocate buffer\n"); | ||
183 | goto dealloc; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | |||
189 | /* At this point wlan_add_card() will be called. Don't worry | ||
190 | * about keeping pwlanpriv around since it will be set on our | ||
191 | * usb device data in -> add() -> libertas_sbi_register_dev(). | ||
192 | */ | ||
193 | if (!(pwlanpriv = wlan_add_card(usb_cardp))) | ||
194 | goto dealloc; | ||
195 | |||
196 | usb_get_dev(udev); | ||
197 | usb_set_intfdata(intf, usb_cardp); | ||
198 | |||
199 | /* | ||
200 | * return card structure, which can be got back in the | ||
201 | * diconnect function as the ptr | ||
202 | * argument. | ||
203 | */ | ||
204 | return 0; | ||
205 | |||
206 | dealloc: | ||
207 | if_usb_free(usb_cardp); | ||
208 | |||
209 | error: | ||
210 | return -ENOMEM; | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * @brief free resource and cleanup | ||
215 | * @param udev pointer to usb_device | ||
216 | * @param ptr pointer to usb_cardp | ||
217 | * @return N/A | ||
218 | */ | ||
219 | static void if_usb_disconnect(struct usb_interface *intf) | ||
220 | { | ||
221 | struct usb_card_rec *cardp = usb_get_intfdata(intf); | ||
222 | wlan_private *priv = (wlan_private *) cardp->priv; | ||
223 | wlan_adapter *adapter = NULL; | ||
224 | |||
225 | adapter = priv->adapter; | ||
226 | |||
227 | /* | ||
228 | * Update Surprise removed to TRUE | ||
229 | */ | ||
230 | adapter->surpriseremoved = 1; | ||
231 | |||
232 | /* card is removed and we can call wlan_remove_card */ | ||
233 | lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n"); | ||
234 | wlan_remove_card(cardp); | ||
235 | |||
236 | /* Unlink and free urb */ | ||
237 | if_usb_free(cardp); | ||
238 | |||
239 | usb_set_intfdata(intf, NULL); | ||
240 | usb_put_dev(interface_to_usbdev(intf)); | ||
241 | |||
242 | return; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * @brief This function download FW | ||
247 | * @param priv pointer to wlan_private | ||
248 | * @return 0 | ||
249 | */ | ||
250 | static int if_prog_firmware(wlan_private * priv) | ||
251 | { | ||
252 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
253 | struct FWData *fwdata; | ||
254 | struct fwheader *fwheader; | ||
255 | u8 *firmware = priv->firmware->data; | ||
256 | |||
257 | fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC); | ||
258 | |||
259 | if (!fwdata) | ||
260 | return -1; | ||
261 | |||
262 | fwheader = &fwdata->fwheader; | ||
263 | |||
264 | if (!cardp->CRC_OK) { | ||
265 | cardp->totalbytes = cardp->fwlastblksent; | ||
266 | cardp->fwseqnum = cardp->lastseqnum - 1; | ||
267 | } | ||
268 | |||
269 | lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n", | ||
270 | cardp->totalbytes); | ||
271 | |||
272 | memcpy(fwheader, &firmware[cardp->totalbytes], | ||
273 | sizeof(struct fwheader)); | ||
274 | |||
275 | cardp->fwlastblksent = cardp->totalbytes; | ||
276 | cardp->totalbytes += sizeof(struct fwheader); | ||
277 | |||
278 | lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n"); | ||
279 | memcpy(fwdata->data, &firmware[cardp->totalbytes], | ||
280 | fwdata->fwheader.datalength); | ||
281 | |||
282 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
283 | "Data length = %d\n", fwdata->fwheader.datalength); | ||
284 | |||
285 | cardp->fwseqnum = cardp->fwseqnum + 1; | ||
286 | |||
287 | fwdata->seqnum = cardp->fwseqnum; | ||
288 | cardp->lastseqnum = fwdata->seqnum; | ||
289 | cardp->totalbytes += fwdata->fwheader.datalength; | ||
290 | |||
291 | if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) { | ||
292 | lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n"); | ||
293 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
294 | "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, | ||
295 | cardp->totalbytes); | ||
296 | memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); | ||
297 | usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); | ||
298 | |||
299 | } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) { | ||
300 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
301 | "Host has finished FW downloading\n"); | ||
302 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
303 | "Donwloading FW JUMP BLOCK\n"); | ||
304 | memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); | ||
305 | usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); | ||
306 | cardp->fwfinalblk = 1; | ||
307 | } | ||
308 | |||
309 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
310 | "The firmware download is done size is %d\n", | ||
311 | cardp->totalbytes); | ||
312 | |||
313 | kfree(fwdata); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static int libertas_do_reset(wlan_private *priv) | ||
319 | { | ||
320 | int ret; | ||
321 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
322 | |||
323 | ret = usb_reset_device(cardp->udev); | ||
324 | if (!ret) { | ||
325 | msleep(10); | ||
326 | reset_device(priv); | ||
327 | msleep(10); | ||
328 | } | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * @brief This function transfer the data to the device. | ||
334 | * @param priv pointer to wlan_private | ||
335 | * @param payload pointer to payload data | ||
336 | * @param nb data length | ||
337 | * @return 0 or -1 | ||
338 | */ | ||
339 | int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb) | ||
340 | { | ||
341 | /* pointer to card structure */ | ||
342 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
343 | int ret = -1; | ||
344 | |||
345 | /* check if device is removed */ | ||
346 | if (priv->adapter->surpriseremoved) { | ||
347 | lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n"); | ||
348 | goto tx_ret; | ||
349 | } | ||
350 | |||
351 | usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, | ||
352 | usb_sndbulkpipe(cardp->udev, | ||
353 | cardp->bulk_out_endpointAddr), | ||
354 | payload, nb, if_usb_write_bulk_callback, priv); | ||
355 | |||
356 | cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; | ||
357 | |||
358 | if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { | ||
359 | /* transfer failed */ | ||
360 | lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n"); | ||
361 | ret = -1; | ||
362 | } else { | ||
363 | lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n"); | ||
364 | ret = 0; | ||
365 | } | ||
366 | |||
367 | tx_ret: | ||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | static int __if_usb_submit_rx_urb(wlan_private * priv, | ||
372 | void (*callbackfn) | ||
373 | (struct urb *urb)) | ||
374 | { | ||
375 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
376 | struct sk_buff *skb; | ||
377 | struct read_cb_info *rinfo = &cardp->rinfo; | ||
378 | int ret = -1; | ||
379 | |||
380 | if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { | ||
381 | lbs_pr_err("No free skb\n"); | ||
382 | goto rx_ret; | ||
383 | } | ||
384 | |||
385 | rinfo->skb = skb; | ||
386 | |||
387 | /* Fill the receive configuration URB and initialise the Rx call back */ | ||
388 | usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, | ||
389 | usb_rcvbulkpipe(cardp->udev, | ||
390 | cardp->bulk_in_endpointAddr), | ||
391 | skb->tail + IPFIELD_ALIGN_OFFSET, | ||
392 | MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, | ||
393 | rinfo); | ||
394 | |||
395 | cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; | ||
396 | |||
397 | lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); | ||
398 | if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { | ||
399 | /* handle failure conditions */ | ||
400 | lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n"); | ||
401 | ret = -1; | ||
402 | } else { | ||
403 | lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n"); | ||
404 | ret = 0; | ||
405 | } | ||
406 | |||
407 | rx_ret: | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv) | ||
412 | { | ||
413 | return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload); | ||
414 | } | ||
415 | |||
416 | static inline int if_usb_submit_rx_urb(wlan_private * priv) | ||
417 | { | ||
418 | return __if_usb_submit_rx_urb(priv, &if_usb_receive); | ||
419 | } | ||
420 | |||
421 | static void if_usb_receive_fwload(struct urb *urb) | ||
422 | { | ||
423 | struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; | ||
424 | wlan_private *priv = rinfo->priv; | ||
425 | struct sk_buff *skb = rinfo->skb; | ||
426 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
427 | struct fwsyncheader *syncfwheader; | ||
428 | struct bootcmdrespStr bootcmdresp; | ||
429 | |||
430 | if (urb->status) { | ||
431 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
432 | "URB status is failed during fw load\n"); | ||
433 | kfree_skb(skb); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | if (cardp->bootcmdresp == 0) { | ||
438 | memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, | ||
439 | sizeof(bootcmdresp)); | ||
440 | if (cardp->udev->descriptor.bcdDevice < 0x3106) { | ||
441 | kfree_skb(skb); | ||
442 | if_usb_submit_rx_urb_fwload(priv); | ||
443 | cardp->bootcmdresp = 1; | ||
444 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
445 | "Received valid boot command response\n"); | ||
446 | return; | ||
447 | } | ||
448 | if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) { | ||
449 | lbs_pr_info( | ||
450 | "boot cmd response wrong magic number (0x%x)\n", | ||
451 | bootcmdresp.u32magicnumber); | ||
452 | } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) { | ||
453 | lbs_pr_info( | ||
454 | "boot cmd response cmd_tag error (%d)\n", | ||
455 | bootcmdresp.u8cmd_tag); | ||
456 | } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) { | ||
457 | lbs_pr_info( | ||
458 | "boot cmd response result error (%d)\n", | ||
459 | bootcmdresp.u8result); | ||
460 | } else { | ||
461 | cardp->bootcmdresp = 1; | ||
462 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
463 | "Received valid boot command response\n"); | ||
464 | } | ||
465 | kfree_skb(skb); | ||
466 | if_usb_submit_rx_urb_fwload(priv); | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); | ||
471 | if (!syncfwheader) { | ||
472 | lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n"); | ||
473 | kfree_skb(skb); | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET, | ||
478 | sizeof(struct fwsyncheader)); | ||
479 | |||
480 | if (!syncfwheader->cmd) { | ||
481 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
482 | "FW received Blk with correct CRC\n"); | ||
483 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
484 | "FW received Blk seqnum = %d\n", | ||
485 | syncfwheader->seqnum); | ||
486 | cardp->CRC_OK = 1; | ||
487 | } else { | ||
488 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
489 | "FW received Blk with CRC error\n"); | ||
490 | cardp->CRC_OK = 0; | ||
491 | } | ||
492 | |||
493 | kfree_skb(skb); | ||
494 | |||
495 | if (cardp->fwfinalblk) { | ||
496 | cardp->fwdnldover = 1; | ||
497 | goto exit; | ||
498 | } | ||
499 | |||
500 | if_prog_firmware(priv); | ||
501 | |||
502 | if_usb_submit_rx_urb_fwload(priv); | ||
503 | exit: | ||
504 | kfree(syncfwheader); | ||
505 | |||
506 | return; | ||
507 | |||
508 | } | ||
509 | |||
510 | #define MRVDRV_MIN_PKT_LEN 30 | ||
511 | |||
512 | static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, | ||
513 | struct usb_card_rec *cardp, | ||
514 | wlan_private *priv) | ||
515 | { | ||
516 | if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + | ||
517 | MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { | ||
518 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
519 | "Packet length is Invalid\n"); | ||
520 | kfree_skb(skb); | ||
521 | return; | ||
522 | } | ||
523 | |||
524 | skb_reserve(skb, IPFIELD_ALIGN_OFFSET); | ||
525 | skb_put(skb, recvlength); | ||
526 | skb_pull(skb, MESSAGE_HEADER_LEN); | ||
527 | libertas_process_rxed_packet(priv, skb); | ||
528 | priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); | ||
529 | } | ||
530 | |||
531 | static inline void process_cmdrequest(int recvlength, u8 *recvbuff, | ||
532 | struct sk_buff *skb, | ||
533 | struct usb_card_rec *cardp, | ||
534 | wlan_private *priv) | ||
535 | { | ||
536 | u8 *cmdbuf; | ||
537 | if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) { | ||
538 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
539 | "The receive buffer is too large\n"); | ||
540 | kfree_skb(skb); | ||
541 | return; | ||
542 | } | ||
543 | |||
544 | if (!in_interrupt()) | ||
545 | BUG(); | ||
546 | |||
547 | spin_lock(&priv->adapter->driver_lock); | ||
548 | /* take care of cur_cmd = NULL case by reading the | ||
549 | * data to clear the interrupt */ | ||
550 | if (!priv->adapter->cur_cmd) { | ||
551 | cmdbuf = priv->wlan_dev.upld_buf; | ||
552 | priv->adapter->hisregcpy &= ~his_cmdupldrdy; | ||
553 | } else | ||
554 | cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; | ||
555 | |||
556 | cardp->usb_int_cause |= his_cmdupldrdy; | ||
557 | priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); | ||
558 | memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN, | ||
559 | priv->wlan_dev.upld_len); | ||
560 | |||
561 | kfree_skb(skb); | ||
562 | libertas_interrupt(priv->wlan_dev.netdev); | ||
563 | spin_unlock(&priv->adapter->driver_lock); | ||
564 | |||
565 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
566 | "Wake up main thread to handle cmd response\n"); | ||
567 | |||
568 | return; | ||
569 | } | ||
570 | |||
571 | /** | ||
572 | * @brief This function reads of the packet into the upload buff, | ||
573 | * wake up the main thread and initialise the Rx callack. | ||
574 | * | ||
575 | * @param urb pointer to struct urb | ||
576 | * @return N/A | ||
577 | */ | ||
578 | static void if_usb_receive(struct urb *urb) | ||
579 | { | ||
580 | struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; | ||
581 | wlan_private *priv = rinfo->priv; | ||
582 | struct sk_buff *skb = rinfo->skb; | ||
583 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
584 | |||
585 | int recvlength = urb->actual_length; | ||
586 | u8 *recvbuff = NULL; | ||
587 | u32 recvtype; | ||
588 | |||
589 | ENTER(); | ||
590 | |||
591 | if (recvlength) { | ||
592 | if (urb->status) { | ||
593 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
594 | "URB status is failed\n"); | ||
595 | kfree_skb(skb); | ||
596 | goto setup_for_next; | ||
597 | } | ||
598 | |||
599 | recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; | ||
600 | memcpy(&recvtype, recvbuff, sizeof(u32)); | ||
601 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
602 | "Recv length = 0x%x\n", recvlength); | ||
603 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
604 | "Receive type = 0x%X\n", recvtype); | ||
605 | recvtype = le32_to_cpu(recvtype); | ||
606 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
607 | "Receive type after = 0x%X\n", recvtype); | ||
608 | } else if (urb->status) | ||
609 | goto rx_exit; | ||
610 | |||
611 | |||
612 | switch (recvtype) { | ||
613 | case CMD_TYPE_DATA: | ||
614 | process_cmdtypedata(recvlength, skb, cardp, priv); | ||
615 | break; | ||
616 | |||
617 | case CMD_TYPE_REQUEST: | ||
618 | process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); | ||
619 | break; | ||
620 | |||
621 | case CMD_TYPE_INDICATION: | ||
622 | /* Event cause handling */ | ||
623 | spin_lock(&priv->adapter->driver_lock); | ||
624 | cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN); | ||
625 | lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n", | ||
626 | cardp->usb_event_cause); | ||
627 | if (cardp->usb_event_cause & 0xffff0000) { | ||
628 | libertas_send_tx_feedback(priv); | ||
629 | break; | ||
630 | } | ||
631 | cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3; | ||
632 | cardp->usb_int_cause |= his_cardevent; | ||
633 | kfree_skb(skb); | ||
634 | libertas_interrupt(priv->wlan_dev.netdev); | ||
635 | spin_unlock(&priv->adapter->driver_lock); | ||
636 | goto rx_exit; | ||
637 | default: | ||
638 | kfree_skb(skb); | ||
639 | break; | ||
640 | } | ||
641 | |||
642 | setup_for_next: | ||
643 | if_usb_submit_rx_urb(priv); | ||
644 | rx_exit: | ||
645 | LEAVE(); | ||
646 | return; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * @brief This function downloads data to FW | ||
651 | * @param priv pointer to wlan_private structure | ||
652 | * @param type type of data | ||
653 | * @param buf pointer to data buffer | ||
654 | * @param len number of bytes | ||
655 | * @return 0 or -1 | ||
656 | */ | ||
657 | int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb) | ||
658 | { | ||
659 | int ret = -1; | ||
660 | u32 tmp; | ||
661 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
662 | |||
663 | lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type); | ||
664 | lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb); | ||
665 | |||
666 | if (type == MVMS_CMD) { | ||
667 | tmp = cpu_to_le32(CMD_TYPE_REQUEST); | ||
668 | priv->wlan_dev.dnld_sent = DNLD_CMD_SENT; | ||
669 | memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, | ||
670 | MESSAGE_HEADER_LEN); | ||
671 | |||
672 | } else { | ||
673 | tmp = cpu_to_le32(CMD_TYPE_DATA); | ||
674 | priv->wlan_dev.dnld_sent = DNLD_DATA_SENT; | ||
675 | memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, | ||
676 | MESSAGE_HEADER_LEN); | ||
677 | } | ||
678 | |||
679 | memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb); | ||
680 | |||
681 | ret = | ||
682 | usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN); | ||
683 | |||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | /* called with adapter->driver_lock held */ | ||
688 | int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg) | ||
689 | { | ||
690 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
691 | |||
692 | *ireg = cardp->usb_int_cause; | ||
693 | cardp->usb_int_cause = 0; | ||
694 | |||
695 | lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg); | ||
696 | |||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | int libertas_sbi_read_event_cause(wlan_private * priv) | ||
701 | { | ||
702 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
703 | priv->adapter->eventcause = cardp->usb_event_cause; | ||
704 | /* Re-submit rx urb here to avoid event lost issue */ | ||
705 | if_usb_submit_rx_urb(priv); | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | int reset_device(wlan_private *priv) | ||
710 | { | ||
711 | int ret; | ||
712 | |||
713 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset, | ||
714 | cmd_act_halt, 0, 0, NULL); | ||
715 | msleep_interruptible(10); | ||
716 | |||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | int libertas_sbi_unregister_dev(wlan_private * priv) | ||
721 | { | ||
722 | int ret = 0; | ||
723 | |||
724 | /* Need to send a Reset command to device before USB resources freed | ||
725 | * and wlan_remove_card() called, then device can handle FW download | ||
726 | * again. | ||
727 | */ | ||
728 | if (priv) | ||
729 | reset_device(priv); | ||
730 | |||
731 | return ret; | ||
732 | } | ||
733 | |||
734 | |||
735 | /** | ||
736 | * @brief This function register usb device and initialize parameter | ||
737 | * @param priv pointer to wlan_private | ||
738 | * @return 0 or -1 | ||
739 | */ | ||
740 | int libertas_sbi_register_dev(wlan_private * priv) | ||
741 | { | ||
742 | |||
743 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
744 | ENTER(); | ||
745 | |||
746 | cardp->priv = priv; | ||
747 | cardp->eth_dev = priv->wlan_dev.netdev; | ||
748 | priv->hotplug_device = &(cardp->udev->dev); | ||
749 | |||
750 | SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev)); | ||
751 | |||
752 | lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n", | ||
753 | cardp->udev); | ||
754 | |||
755 | LEAVE(); | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | |||
760 | |||
761 | int libertas_sbi_prog_firmware(wlan_private * priv) | ||
762 | { | ||
763 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
764 | int i = 0; | ||
765 | static int reset_count = 10; | ||
766 | |||
767 | ENTER(); | ||
768 | |||
769 | cardp->rinfo.priv = priv; | ||
770 | |||
771 | restart: | ||
772 | if (if_usb_submit_rx_urb_fwload(priv) < 0) { | ||
773 | lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n"); | ||
774 | LEAVE(); | ||
775 | return -1; | ||
776 | } | ||
777 | |||
778 | #ifdef SUPPORT_BOOT_COMMAND | ||
779 | cardp->bootcmdresp = 0; | ||
780 | do { | ||
781 | int j = 0; | ||
782 | i++; | ||
783 | /* Issue Boot command = 1, Boot from Download-FW */ | ||
784 | if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB); | ||
785 | /* wait for command response */ | ||
786 | do { | ||
787 | j++; | ||
788 | msleep_interruptible(100); | ||
789 | } while (cardp->bootcmdresp == 0 && j < 10); | ||
790 | } while (cardp->bootcmdresp == 0 && i < 5); | ||
791 | |||
792 | if (cardp->bootcmdresp == 0) { | ||
793 | if (--reset_count >= 0) { | ||
794 | libertas_do_reset(priv); | ||
795 | goto restart; | ||
796 | } | ||
797 | return -1; | ||
798 | } | ||
799 | #endif | ||
800 | |||
801 | i = 0; | ||
802 | priv->adapter->fw_ready = 0; | ||
803 | |||
804 | cardp->totalbytes = 0; | ||
805 | cardp->fwlastblksent = 0; | ||
806 | cardp->CRC_OK = 1; | ||
807 | cardp->fwdnldover = 0; | ||
808 | cardp->fwseqnum = -1; | ||
809 | cardp->totalbytes = 0; | ||
810 | cardp->fwfinalblk = 0; | ||
811 | |||
812 | if_prog_firmware(priv); | ||
813 | |||
814 | do { | ||
815 | lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n"); | ||
816 | i++; | ||
817 | msleep_interruptible(100); | ||
818 | if (priv->adapter->surpriseremoved || i >= 20) | ||
819 | break; | ||
820 | } while (!cardp->fwdnldover); | ||
821 | |||
822 | if (!cardp->fwdnldover) { | ||
823 | lbs_pr_info("failed to load fw, resetting device!\n"); | ||
824 | if (--reset_count >= 0) { | ||
825 | libertas_do_reset(priv); | ||
826 | goto restart; | ||
827 | } | ||
828 | |||
829 | lbs_pr_info("FW download failure, time = %d ms\n", i * 100); | ||
830 | LEAVE(); | ||
831 | return -1; | ||
832 | } | ||
833 | |||
834 | if_usb_submit_rx_urb(priv); | ||
835 | |||
836 | /* Delay 200 ms to waiting for the FW ready */ | ||
837 | msleep_interruptible(200); | ||
838 | |||
839 | priv->adapter->fw_ready = 1; | ||
840 | |||
841 | LEAVE(); | ||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | /** | ||
846 | * @brief Given a usb_card_rec return its wlan_private | ||
847 | * @param card pointer to a usb_card_rec | ||
848 | * @return pointer to wlan_private | ||
849 | */ | ||
850 | wlan_private *libertas_sbi_get_priv(void *card) | ||
851 | { | ||
852 | struct usb_card_rec *cardp = card; | ||
853 | return cardp->priv; | ||
854 | } | ||
855 | |||
856 | #ifdef ENABLE_PM | ||
857 | int libertas_sbi_suspend(wlan_private * priv) | ||
858 | { | ||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | int libertas_sbi_resume(wlan_private * priv) | ||
863 | { | ||
864 | return 0; | ||
865 | } | ||
866 | #endif | ||
867 | |||
868 | #ifdef CONFIG_PM | ||
869 | static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) | ||
870 | { | ||
871 | struct usb_card_rec *cardp = usb_get_intfdata(intf); | ||
872 | wlan_private *priv = cardp->priv; | ||
873 | |||
874 | ENTER(); | ||
875 | |||
876 | if (priv->adapter->psstate != PS_STATE_FULL_POWER) | ||
877 | return -1; | ||
878 | |||
879 | netif_device_detach(cardp->eth_dev); | ||
880 | |||
881 | /* Unlink tx & rx urb */ | ||
882 | usb_kill_urb(cardp->tx_urb); | ||
883 | usb_kill_urb(cardp->rx_urb); | ||
884 | |||
885 | cardp->rx_urb_recall = 1; | ||
886 | |||
887 | LEAVE(); | ||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | static int if_usb_resume(struct usb_interface *intf) | ||
892 | { | ||
893 | struct usb_card_rec *cardp = usb_get_intfdata(intf); | ||
894 | |||
895 | ENTER(); | ||
896 | |||
897 | cardp->rx_urb_recall = 0; | ||
898 | |||
899 | if_usb_submit_rx_urb(cardp->priv); | ||
900 | |||
901 | netif_device_attach(cardp->eth_dev); | ||
902 | |||
903 | LEAVE(); | ||
904 | return 0; | ||
905 | } | ||
906 | #else | ||
907 | #define if_usb_suspend NULL | ||
908 | #define if_usb_resume NULL | ||
909 | #endif | ||
910 | |||
911 | static struct usb_driver if_usb_driver = { | ||
912 | /* driver name */ | ||
913 | .name = usbdriver_name, | ||
914 | /* probe function name */ | ||
915 | .probe = if_usb_probe, | ||
916 | /* disconnect function name */ | ||
917 | .disconnect = if_usb_disconnect, | ||
918 | /* device signature table */ | ||
919 | .id_table = if_usb_table, | ||
920 | .suspend = if_usb_suspend, | ||
921 | .resume = if_usb_resume, | ||
922 | }; | ||
923 | |||
924 | /** | ||
925 | * @brief This function registers driver. | ||
926 | * @param add pointer to add_card callback function | ||
927 | * @param remove pointer to remove card callback function | ||
928 | * @param arg pointer to call back function parameter | ||
929 | * @return dummy success variable | ||
930 | */ | ||
931 | int libertas_sbi_register(void) | ||
932 | { | ||
933 | /* | ||
934 | * API registers the Marvell USB driver | ||
935 | * to the USB system | ||
936 | */ | ||
937 | usb_register(&if_usb_driver); | ||
938 | |||
939 | /* Return success to wlan layer */ | ||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | /** | ||
944 | * @brief This function removes usb driver. | ||
945 | * @return N/A | ||
946 | */ | ||
947 | void libertas_sbi_unregister(void) | ||
948 | { | ||
949 | /* API unregisters the driver from USB subsystem */ | ||
950 | usb_deregister(&if_usb_driver); | ||
951 | return; | ||
952 | } | ||
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h new file mode 100644 index 000000000000..785116720bc6 --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.h | |||
@@ -0,0 +1,109 @@ | |||
1 | /** | ||
2 | * This file contains definition for USB interface. | ||
3 | */ | ||
4 | #define CMD_TYPE_REQUEST 0xF00DFACE | ||
5 | #define CMD_TYPE_DATA 0xBEADC0DE | ||
6 | #define CMD_TYPE_INDICATION 0xBEEFFACE | ||
7 | |||
8 | #define IPFIELD_ALIGN_OFFSET 2 | ||
9 | |||
10 | #define USB8388_VID_1 0x1286 | ||
11 | #define USB8388_PID_1 0x2001 | ||
12 | #define USB8388_VID_2 0x05a3 | ||
13 | #define USB8388_PID_2 0x8388 | ||
14 | |||
15 | #ifdef SUPPORT_BOOT_COMMAND | ||
16 | #define BOOT_CMD_FW_BY_USB 0x01 | ||
17 | #define BOOT_CMD_FW_IN_EEPROM 0x02 | ||
18 | #define BOOT_CMD_UPDATE_BOOT2 0x03 | ||
19 | #define BOOT_CMD_UPDATE_FW 0x04 | ||
20 | #define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */ | ||
21 | |||
22 | struct bootcmdstr | ||
23 | { | ||
24 | u32 u32magicnumber; | ||
25 | u8 u8cmd_tag; | ||
26 | u8 au8dumy[11]; | ||
27 | }; | ||
28 | |||
29 | #define BOOT_CMD_RESP_OK 0x0001 | ||
30 | #define BOOT_CMD_RESP_FAIL 0x0000 | ||
31 | |||
32 | struct bootcmdrespStr | ||
33 | { | ||
34 | u32 u32magicnumber; | ||
35 | u8 u8cmd_tag; | ||
36 | u8 u8result; | ||
37 | u8 au8dumy[2]; | ||
38 | }; | ||
39 | #endif /* SUPPORT_BOOT_COMMAND */ | ||
40 | |||
41 | /* read callback private data */ | ||
42 | struct read_cb_info { | ||
43 | wlan_private *priv; | ||
44 | struct sk_buff *skb; | ||
45 | }; | ||
46 | |||
47 | /** USB card description structure*/ | ||
48 | struct usb_card_rec { | ||
49 | struct net_device *eth_dev; | ||
50 | struct usb_device *udev; | ||
51 | struct urb *rx_urb, *tx_urb; | ||
52 | void *priv; | ||
53 | struct read_cb_info rinfo; | ||
54 | |||
55 | int bulk_in_size; | ||
56 | u8 bulk_in_endpointAddr; | ||
57 | |||
58 | u8 *bulk_out_buffer; | ||
59 | int bulk_out_size; | ||
60 | u8 bulk_out_endpointAddr; | ||
61 | |||
62 | u8 CRC_OK; | ||
63 | u32 fwseqnum; | ||
64 | u32 lastseqnum; | ||
65 | u32 totalbytes; | ||
66 | u32 fwlastblksent; | ||
67 | u8 fwdnldover; | ||
68 | u8 fwfinalblk; | ||
69 | |||
70 | u32 usb_event_cause; | ||
71 | u8 usb_int_cause; | ||
72 | |||
73 | u8 rx_urb_recall; | ||
74 | |||
75 | u8 bootcmdresp; | ||
76 | }; | ||
77 | |||
78 | /** fwheader */ | ||
79 | struct fwheader { | ||
80 | u32 dnldcmd; | ||
81 | u32 baseaddr; | ||
82 | u32 datalength; | ||
83 | u32 CRC; | ||
84 | }; | ||
85 | |||
86 | #define FW_MAX_DATA_BLK_SIZE 600 | ||
87 | /** FWData */ | ||
88 | struct FWData { | ||
89 | struct fwheader fwheader; | ||
90 | u32 seqnum; | ||
91 | u8 data[FW_MAX_DATA_BLK_SIZE]; | ||
92 | }; | ||
93 | |||
94 | /** fwsyncheader */ | ||
95 | struct fwsyncheader { | ||
96 | u32 cmd; | ||
97 | u32 seqnum; | ||
98 | }; | ||
99 | |||
100 | #define FW_HAS_DATA_TO_RECV 0x00000001 | ||
101 | #define FW_HAS_LAST_BLOCK 0x00000004 | ||
102 | |||
103 | #define FW_DATA_XMIT_SIZE \ | ||
104 | sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32) | ||
105 | |||
106 | int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb); | ||
107 | void if_usb_free(struct usb_card_rec *cardp); | ||
108 | int if_usb_issue_boot_command(wlan_private *priv, int ivalue); | ||
109 | |||
diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c new file mode 100644 index 000000000000..82b39642423a --- /dev/null +++ b/drivers/net/wireless/libertas/ioctl.c | |||
@@ -0,0 +1,2500 @@ | |||
1 | /** | ||
2 | * This file contains ioctl functions | ||
3 | */ | ||
4 | |||
5 | #include <linux/ctype.h> | ||
6 | #include <linux/delay.h> | ||
7 | #include <linux/if.h> | ||
8 | #include <linux/if_arp.h> | ||
9 | #include <linux/wireless.h> | ||
10 | |||
11 | #include <net/iw_handler.h> | ||
12 | #include <net/ieee80211.h> | ||
13 | |||
14 | #include "host.h" | ||
15 | #include "radiotap.h" | ||
16 | #include "decl.h" | ||
17 | #include "defs.h" | ||
18 | #include "dev.h" | ||
19 | #include "join.h" | ||
20 | #include "wext.h" | ||
21 | |||
22 | #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \ | ||
23 | IW_ESSID_MAX_SIZE + \ | ||
24 | IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \ | ||
25 | IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \ | ||
26 | IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */ | ||
27 | |||
28 | #define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ) | ||
29 | |||
30 | static int setrxantenna(wlan_private * priv, int mode) | ||
31 | { | ||
32 | int ret = 0; | ||
33 | wlan_adapter *adapter = priv->adapter; | ||
34 | |||
35 | if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2 | ||
36 | && mode != RF_ANTENNA_AUTO) { | ||
37 | return -EINVAL; | ||
38 | } | ||
39 | |||
40 | adapter->rxantennamode = mode; | ||
41 | |||
42 | lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode); | ||
43 | |||
44 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, | ||
45 | cmd_act_set_rx, | ||
46 | cmd_option_waitforrsp, 0, | ||
47 | &adapter->rxantennamode); | ||
48 | return ret; | ||
49 | } | ||
50 | |||
51 | static int settxantenna(wlan_private * priv, int mode) | ||
52 | { | ||
53 | int ret = 0; | ||
54 | wlan_adapter *adapter = priv->adapter; | ||
55 | |||
56 | if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2) | ||
57 | && (mode != RF_ANTENNA_AUTO)) { | ||
58 | return -EINVAL; | ||
59 | } | ||
60 | |||
61 | adapter->txantennamode = mode; | ||
62 | |||
63 | lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode); | ||
64 | |||
65 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, | ||
66 | cmd_act_set_tx, | ||
67 | cmd_option_waitforrsp, 0, | ||
68 | &adapter->txantennamode); | ||
69 | |||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | static int getrxantenna(wlan_private * priv, char *buf) | ||
74 | { | ||
75 | int ret = 0; | ||
76 | wlan_adapter *adapter = priv->adapter; | ||
77 | |||
78 | // clear it, so we will know if the value | ||
79 | // returned below is correct or not. | ||
80 | adapter->rxantennamode = 0; | ||
81 | |||
82 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, | ||
83 | cmd_act_get_rx, | ||
84 | cmd_option_waitforrsp, 0, NULL); | ||
85 | |||
86 | if (ret) { | ||
87 | LEAVE(); | ||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode); | ||
92 | |||
93 | return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1; | ||
94 | } | ||
95 | |||
96 | static int gettxantenna(wlan_private * priv, char *buf) | ||
97 | { | ||
98 | int ret = 0; | ||
99 | wlan_adapter *adapter = priv->adapter; | ||
100 | |||
101 | // clear it, so we will know if the value | ||
102 | // returned below is correct or not. | ||
103 | adapter->txantennamode = 0; | ||
104 | |||
105 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, | ||
106 | cmd_act_get_tx, | ||
107 | cmd_option_waitforrsp, 0, NULL); | ||
108 | |||
109 | if (ret) { | ||
110 | LEAVE(); | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode); | ||
115 | |||
116 | return sprintf(buf, "0x%04x", adapter->txantennamode) + 1; | ||
117 | } | ||
118 | |||
119 | static int wlan_set_region(wlan_private * priv, u16 region_code) | ||
120 | { | ||
121 | int i; | ||
122 | |||
123 | for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { | ||
124 | // use the region code to search for the index | ||
125 | if (region_code == libertas_region_code_to_index[i]) { | ||
126 | priv->adapter->regiontableindex = (u16) i; | ||
127 | priv->adapter->regioncode = region_code; | ||
128 | break; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | // if it's unidentified region code | ||
133 | if (i >= MRVDRV_MAX_REGION_CODE) { | ||
134 | lbs_pr_debug(1, "region Code not identified\n"); | ||
135 | LEAVE(); | ||
136 | return -1; | ||
137 | } | ||
138 | |||
139 | if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) { | ||
140 | LEAVE(); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * @brief Get/Set Firmware wakeup method | ||
149 | * | ||
150 | * @param priv A pointer to wlan_private structure | ||
151 | * @param wrq A pointer to user data | ||
152 | * @return 0--success, otherwise fail | ||
153 | */ | ||
154 | static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq) | ||
155 | { | ||
156 | wlan_adapter *adapter = priv->adapter; | ||
157 | int data; | ||
158 | ENTER(); | ||
159 | |||
160 | if ((int)wrq->u.data.length == 0) { | ||
161 | if (copy_to_user | ||
162 | (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) { | ||
163 | lbs_pr_alert("copy_to_user failed!\n"); | ||
164 | return -EFAULT; | ||
165 | } | ||
166 | } else { | ||
167 | if ((int)wrq->u.data.length > 1) { | ||
168 | lbs_pr_alert("ioctl too many args!\n"); | ||
169 | return -EFAULT; | ||
170 | } | ||
171 | if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { | ||
172 | lbs_pr_alert("Copy from user failed\n"); | ||
173 | return -EFAULT; | ||
174 | } | ||
175 | |||
176 | adapter->pkttxctrl = (u32) data; | ||
177 | } | ||
178 | |||
179 | wrq->u.data.length = 1; | ||
180 | |||
181 | LEAVE(); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * @brief Get/Set NULL Package generation interval | ||
187 | * | ||
188 | * @param priv A pointer to wlan_private structure | ||
189 | * @param wrq A pointer to user data | ||
190 | * @return 0--success, otherwise fail | ||
191 | */ | ||
192 | static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq) | ||
193 | { | ||
194 | wlan_adapter *adapter = priv->adapter; | ||
195 | int data; | ||
196 | ENTER(); | ||
197 | |||
198 | if ((int)wrq->u.data.length == 0) { | ||
199 | data = adapter->nullpktinterval; | ||
200 | |||
201 | if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { | ||
202 | lbs_pr_alert( "copy_to_user failed!\n"); | ||
203 | return -EFAULT; | ||
204 | } | ||
205 | } else { | ||
206 | if ((int)wrq->u.data.length > 1) { | ||
207 | lbs_pr_alert( "ioctl too many args!\n"); | ||
208 | return -EFAULT; | ||
209 | } | ||
210 | if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { | ||
211 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
212 | return -EFAULT; | ||
213 | } | ||
214 | |||
215 | adapter->nullpktinterval = data; | ||
216 | } | ||
217 | |||
218 | wrq->u.data.length = 1; | ||
219 | |||
220 | LEAVE(); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq) | ||
225 | { | ||
226 | wlan_adapter *adapter = priv->adapter; | ||
227 | int data[2]; | ||
228 | ENTER(); | ||
229 | data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; | ||
230 | data[1] = adapter->rxpd_rate; | ||
231 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { | ||
232 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
233 | return -EFAULT; | ||
234 | } | ||
235 | wrq->u.data.length = 2; | ||
236 | LEAVE(); | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq) | ||
241 | { | ||
242 | int ret = 0; | ||
243 | wlan_adapter *adapter = priv->adapter; | ||
244 | int data[4]; | ||
245 | |||
246 | ENTER(); | ||
247 | memset(data, 0, sizeof(data)); | ||
248 | if (wrq->u.data.length) { | ||
249 | if (copy_from_user(data, wrq->u.data.pointer, | ||
250 | min_t(size_t, wrq->u.data.length, 4) * sizeof(int))) | ||
251 | return -EFAULT; | ||
252 | } | ||
253 | if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) { | ||
254 | if (adapter->connect_status == libertas_connected) { | ||
255 | ret = libertas_prepare_and_send_command(priv, | ||
256 | cmd_802_11_rssi, | ||
257 | 0, | ||
258 | cmd_option_waitforrsp, | ||
259 | 0, NULL); | ||
260 | |||
261 | if (ret) { | ||
262 | LEAVE(); | ||
263 | return ret; | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | if (wrq->u.data.length == 0) { | ||
269 | data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; | ||
270 | data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; | ||
271 | data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; | ||
272 | data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; | ||
273 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4)) | ||
274 | return -EFAULT; | ||
275 | wrq->u.data.length = 4; | ||
276 | } else if (data[0] == 0) { | ||
277 | data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; | ||
278 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) | ||
279 | return -EFAULT; | ||
280 | wrq->u.data.length = 1; | ||
281 | } else if (data[0] == 1) { | ||
282 | data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; | ||
283 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) | ||
284 | return -EFAULT; | ||
285 | wrq->u.data.length = 1; | ||
286 | } else if (data[0] == 2) { | ||
287 | data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; | ||
288 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) | ||
289 | return -EFAULT; | ||
290 | wrq->u.data.length = 1; | ||
291 | } else if (data[0] == 3) { | ||
292 | data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; | ||
293 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) | ||
294 | return -EFAULT; | ||
295 | wrq->u.data.length = 1; | ||
296 | } else | ||
297 | return -ENOTSUPP; | ||
298 | |||
299 | LEAVE(); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq) | ||
304 | { | ||
305 | int data; | ||
306 | wlan_adapter *adapter = priv->adapter; | ||
307 | |||
308 | if (wrq->u.data.length > 0) { | ||
309 | if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) | ||
310 | return -EFAULT; | ||
311 | |||
312 | lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data); | ||
313 | if ((data > MRVDRV_MAX_BEACON_INTERVAL) | ||
314 | || (data < MRVDRV_MIN_BEACON_INTERVAL)) | ||
315 | return -ENOTSUPP; | ||
316 | adapter->beaconperiod = data; | ||
317 | } | ||
318 | data = adapter->beaconperiod; | ||
319 | if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) | ||
320 | return -EFAULT; | ||
321 | |||
322 | wrq->u.data.length = 1; | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq) | ||
328 | { | ||
329 | int ret = 0; | ||
330 | wlan_adapter *adapter = priv->adapter; | ||
331 | int temp; | ||
332 | int data = 0; | ||
333 | int *val; | ||
334 | |||
335 | ENTER(); | ||
336 | data = SUBCMD_DATA(wrq); | ||
337 | if ((data == 0) || (data == 1)) { | ||
338 | ret = libertas_prepare_and_send_command(priv, | ||
339 | cmd_802_11_rssi, | ||
340 | 0, cmd_option_waitforrsp, | ||
341 | 0, NULL); | ||
342 | if (ret) { | ||
343 | LEAVE(); | ||
344 | return ret; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | switch (data) { | ||
349 | case 0: | ||
350 | |||
351 | temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], | ||
352 | adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | ||
353 | break; | ||
354 | case 1: | ||
355 | temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG], | ||
356 | adapter->NF[TYPE_BEACON][TYPE_AVG]); | ||
357 | break; | ||
358 | case 2: | ||
359 | temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], | ||
360 | adapter->NF[TYPE_RXPD][TYPE_NOAVG]); | ||
361 | break; | ||
362 | case 3: | ||
363 | temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
364 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
365 | break; | ||
366 | default: | ||
367 | return -ENOTSUPP; | ||
368 | } | ||
369 | val = (int *)wrq->u.name; | ||
370 | *val = temp; | ||
371 | |||
372 | LEAVE(); | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq) | ||
377 | { | ||
378 | int ret = 0; | ||
379 | wlan_adapter *adapter = priv->adapter; | ||
380 | int temp; | ||
381 | int data = 0; | ||
382 | int *val; | ||
383 | |||
384 | data = SUBCMD_DATA(wrq); | ||
385 | if ((data == 0) || (data == 1)) { | ||
386 | ret = libertas_prepare_and_send_command(priv, | ||
387 | cmd_802_11_rssi, | ||
388 | 0, cmd_option_waitforrsp, | ||
389 | 0, NULL); | ||
390 | |||
391 | if (ret) { | ||
392 | LEAVE(); | ||
393 | return ret; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | switch (data) { | ||
398 | case 0: | ||
399 | temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG]; | ||
400 | break; | ||
401 | case 1: | ||
402 | temp = adapter->NF[TYPE_BEACON][TYPE_AVG]; | ||
403 | break; | ||
404 | case 2: | ||
405 | temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG]; | ||
406 | break; | ||
407 | case 3: | ||
408 | temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; | ||
409 | break; | ||
410 | default: | ||
411 | return -ENOTSUPP; | ||
412 | } | ||
413 | |||
414 | temp = CAL_NF(temp); | ||
415 | |||
416 | lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp); | ||
417 | val = (int *)wrq->u.name; | ||
418 | *val = temp; | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req) | ||
423 | { | ||
424 | wlan_adapter *adapter = priv->adapter; | ||
425 | int *pdata; | ||
426 | struct iwreq *wrq = (struct iwreq *)req; | ||
427 | int ret = 0; | ||
428 | adapter->txrate = 0; | ||
429 | lbs_pr_debug(1, "wlan_get_txrate_ioctl\n"); | ||
430 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query, | ||
431 | cmd_act_get, cmd_option_waitforrsp, | ||
432 | 0, NULL); | ||
433 | if (ret) | ||
434 | return ret; | ||
435 | |||
436 | pdata = (int *)wrq->u.name; | ||
437 | *pdata = (int)adapter->txrate; | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
442 | { | ||
443 | char status[64]; | ||
444 | wlan_adapter *adapter = priv->adapter; | ||
445 | |||
446 | memset(status, 0, sizeof(status)); | ||
447 | |||
448 | switch (adapter->inframode) { | ||
449 | case wlan802_11ibss: | ||
450 | if (adapter->connect_status == libertas_connected) { | ||
451 | if (adapter->adhoccreate) | ||
452 | memcpy(&status, "AdhocStarted", sizeof(status)); | ||
453 | else | ||
454 | memcpy(&status, "AdhocJoined", sizeof(status)); | ||
455 | } else { | ||
456 | memcpy(&status, "AdhocIdle", sizeof(status)); | ||
457 | } | ||
458 | break; | ||
459 | case wlan802_11infrastructure: | ||
460 | memcpy(&status, "Inframode", sizeof(status)); | ||
461 | break; | ||
462 | default: | ||
463 | memcpy(&status, "AutoUnknownmode", sizeof(status)); | ||
464 | break; | ||
465 | } | ||
466 | |||
467 | lbs_pr_debug(1, "status = %s\n", status); | ||
468 | wrq->u.data.length = strlen(status) + 1; | ||
469 | |||
470 | if (wrq->u.data.pointer) { | ||
471 | if (copy_to_user(wrq->u.data.pointer, | ||
472 | &status, wrq->u.data.length)) | ||
473 | return -EFAULT; | ||
474 | } | ||
475 | |||
476 | LEAVE(); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * @brief Set/Get WPA IE | ||
482 | * @param priv A pointer to wlan_private structure | ||
483 | * @param req A pointer to ifreq structure | ||
484 | * @return 0 --success, otherwise fail | ||
485 | */ | ||
486 | static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req) | ||
487 | { | ||
488 | struct iwreq *wrq = (struct iwreq *)req; | ||
489 | wlan_adapter *adapter = priv->adapter; | ||
490 | int ret = 0; | ||
491 | |||
492 | ENTER(); | ||
493 | |||
494 | if (wrq->u.data.length) { | ||
495 | if (wrq->u.data.length > sizeof(adapter->wpa_ie)) { | ||
496 | lbs_pr_debug(1, "failed to copy WPA IE, too big \n"); | ||
497 | return -EFAULT; | ||
498 | } | ||
499 | if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer, | ||
500 | wrq->u.data.length)) { | ||
501 | lbs_pr_debug(1, "failed to copy WPA IE \n"); | ||
502 | return -EFAULT; | ||
503 | } | ||
504 | adapter->wpa_ie_len = wrq->u.data.length; | ||
505 | lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len, | ||
506 | adapter->wpa_ie[0]); | ||
507 | lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len); | ||
508 | if (adapter->wpa_ie[0] == WPA_IE) | ||
509 | adapter->secinfo.WPAenabled = 1; | ||
510 | else if (adapter->wpa_ie[0] == WPA2_IE) | ||
511 | adapter->secinfo.WPA2enabled = 1; | ||
512 | else { | ||
513 | adapter->secinfo.WPAenabled = 0; | ||
514 | adapter->secinfo.WPA2enabled = 0; | ||
515 | } | ||
516 | } else { | ||
517 | memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie)); | ||
518 | adapter->wpa_ie_len = wrq->u.data.length; | ||
519 | lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n", | ||
520 | adapter->wpa_ie_len, adapter->wpa_ie[0]); | ||
521 | adapter->secinfo.WPAenabled = 0; | ||
522 | adapter->secinfo.WPA2enabled = 0; | ||
523 | } | ||
524 | |||
525 | // enable/disable RSN in firmware if WPA is enabled/disabled | ||
526 | // depending on variable adapter->secinfo.WPAenabled is set or not | ||
527 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn, | ||
528 | cmd_act_set, cmd_option_waitforrsp, | ||
529 | 0, NULL); | ||
530 | |||
531 | LEAVE(); | ||
532 | return ret; | ||
533 | } | ||
534 | |||
535 | /** | ||
536 | * @brief Set Auto prescan | ||
537 | * @param priv A pointer to wlan_private structure | ||
538 | * @param wrq A pointer to iwreq structure | ||
539 | * @return 0 --success, otherwise fail | ||
540 | */ | ||
541 | static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
542 | { | ||
543 | int data; | ||
544 | wlan_adapter *adapter = priv->adapter; | ||
545 | int *val; | ||
546 | |||
547 | data = SUBCMD_DATA(wrq); | ||
548 | lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data); | ||
549 | adapter->prescan = data; | ||
550 | |||
551 | val = (int *)wrq->u.name; | ||
552 | *val = data; | ||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req) | ||
557 | { | ||
558 | struct iwreq *wrq = (struct iwreq *)req; | ||
559 | u32 mdtim; | ||
560 | int idata; | ||
561 | int ret = -EINVAL; | ||
562 | |||
563 | ENTER(); | ||
564 | |||
565 | idata = SUBCMD_DATA(wrq); | ||
566 | mdtim = (u32) idata; | ||
567 | if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM) | ||
568 | && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM)) | ||
569 | || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) { | ||
570 | priv->adapter->multipledtim = mdtim; | ||
571 | ret = 0; | ||
572 | } | ||
573 | if (ret) | ||
574 | lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n"); | ||
575 | |||
576 | LEAVE(); | ||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | /** | ||
581 | * @brief Set authentication mode | ||
582 | * @param priv A pointer to wlan_private structure | ||
583 | * @param req A pointer to ifreq structure | ||
584 | * @return 0 --success, otherwise fail | ||
585 | */ | ||
586 | static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req) | ||
587 | { | ||
588 | int alg; | ||
589 | struct iwreq *wrq = (struct iwreq *)req; | ||
590 | wlan_adapter *adapter = priv->adapter; | ||
591 | |||
592 | if (wrq->u.data.flags == 0) { | ||
593 | //from iwpriv subcmd | ||
594 | alg = SUBCMD_DATA(wrq); | ||
595 | } else { | ||
596 | //from wpa_supplicant subcmd | ||
597 | if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) { | ||
598 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
599 | return -EFAULT; | ||
600 | } | ||
601 | } | ||
602 | |||
603 | lbs_pr_debug(1, "auth alg is %#x\n", alg); | ||
604 | |||
605 | switch (alg) { | ||
606 | case AUTH_ALG_SHARED_KEY: | ||
607 | adapter->secinfo.authmode = wlan802_11authmodeshared; | ||
608 | break; | ||
609 | case AUTH_ALG_NETWORK_EAP: | ||
610 | adapter->secinfo.authmode = | ||
611 | wlan802_11authmodenetworkEAP; | ||
612 | break; | ||
613 | case AUTH_ALG_OPEN_SYSTEM: | ||
614 | default: | ||
615 | adapter->secinfo.authmode = wlan802_11authmodeopen; | ||
616 | break; | ||
617 | } | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | /** | ||
622 | * @brief Set 802.1x authentication mode | ||
623 | * @param priv A pointer to wlan_private structure | ||
624 | * @param req A pointer to ifreq structure | ||
625 | * @return 0 --success, otherwise fail | ||
626 | */ | ||
627 | static int wlan_set8021xauthalg_ioctl(wlan_private * priv, struct ifreq *req) | ||
628 | { | ||
629 | int alg; | ||
630 | struct iwreq *wrq = (struct iwreq *)req; | ||
631 | |||
632 | if (wrq->u.data.flags == 0) { | ||
633 | //from iwpriv subcmd | ||
634 | alg = SUBCMD_DATA(wrq); | ||
635 | } else { | ||
636 | //from wpa_supplicant subcmd | ||
637 | if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(int))) { | ||
638 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
639 | return -EFAULT; | ||
640 | } | ||
641 | } | ||
642 | lbs_pr_debug(1, "802.1x auth alg is %#x\n", alg); | ||
643 | priv->adapter->secinfo.auth1xalg = alg; | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req) | ||
648 | { | ||
649 | int mode; | ||
650 | struct iwreq *wrq = (struct iwreq *)req; | ||
651 | |||
652 | ENTER(); | ||
653 | |||
654 | if (wrq->u.data.flags == 0) { | ||
655 | //from iwpriv subcmd | ||
656 | mode = SUBCMD_DATA(wrq); | ||
657 | } else { | ||
658 | //from wpa_supplicant subcmd | ||
659 | if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) { | ||
660 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
661 | return -EFAULT; | ||
662 | } | ||
663 | } | ||
664 | lbs_pr_debug(1, "encryption mode is %#x\n", mode); | ||
665 | priv->adapter->secinfo.Encryptionmode = mode; | ||
666 | |||
667 | LEAVE(); | ||
668 | return 0; | ||
669 | } | ||
670 | |||
671 | static void adjust_mtu(wlan_private * priv) | ||
672 | { | ||
673 | int mtu_increment = 0; | ||
674 | |||
675 | if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) | ||
676 | mtu_increment += sizeof(struct ieee80211_hdr_4addr); | ||
677 | |||
678 | if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) | ||
679 | mtu_increment += max(sizeof(struct tx_radiotap_hdr), | ||
680 | sizeof(struct rx_radiotap_hdr)); | ||
681 | priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN | ||
682 | - sizeof(struct ethhdr) | ||
683 | + mtu_increment; | ||
684 | } | ||
685 | |||
686 | /** | ||
687 | * @brief Set Link-Layer Layer mode | ||
688 | * @param priv A pointer to wlan_private structure | ||
689 | * @param req A pointer to ifreq structure | ||
690 | * @return 0 --success, otherwise fail | ||
691 | */ | ||
692 | static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req) | ||
693 | { | ||
694 | int mode; | ||
695 | |||
696 | mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; | ||
697 | |||
698 | switch (mode) { | ||
699 | case WLAN_LINKMODE_802_3: | ||
700 | priv->adapter->linkmode = mode; | ||
701 | break; | ||
702 | case WLAN_LINKMODE_802_11: | ||
703 | priv->adapter->linkmode = mode; | ||
704 | break; | ||
705 | default: | ||
706 | lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n", | ||
707 | mode); | ||
708 | return -EINVAL; | ||
709 | break; | ||
710 | } | ||
711 | lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode); | ||
712 | |||
713 | adjust_mtu(priv); | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | /** | ||
719 | * @brief Set Radio header mode | ||
720 | * @param priv A pointer to wlan_private structure | ||
721 | * @param req A pointer to ifreq structure | ||
722 | * @return 0 --success, otherwise fail | ||
723 | */ | ||
724 | static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req) | ||
725 | { | ||
726 | int mode; | ||
727 | |||
728 | mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; | ||
729 | |||
730 | switch (mode) { | ||
731 | case WLAN_RADIOMODE_NONE: | ||
732 | priv->adapter->radiomode = mode; | ||
733 | break; | ||
734 | case WLAN_RADIOMODE_RADIOTAP: | ||
735 | priv->adapter->radiomode = mode; | ||
736 | break; | ||
737 | default: | ||
738 | lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n", | ||
739 | mode); | ||
740 | return -EINVAL; | ||
741 | } | ||
742 | lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode); | ||
743 | |||
744 | adjust_mtu(priv); | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | /** | ||
749 | * @brief Set Debug header mode | ||
750 | * @param priv A pointer to wlan_private structure | ||
751 | * @param req A pointer to ifreq structure | ||
752 | * @return 0 --success, otherwise fail | ||
753 | */ | ||
754 | static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req) | ||
755 | { | ||
756 | priv->adapter->debugmode = (int)((struct ifreq *) | ||
757 | ((u8 *) req + 4))->ifr_data; | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv, | ||
762 | struct ifreq *req) | ||
763 | { | ||
764 | int len; | ||
765 | char buf[8]; | ||
766 | struct iwreq *wrq = (struct iwreq *)req; | ||
767 | |||
768 | lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n"); | ||
769 | len = getrxantenna(priv, buf); | ||
770 | |||
771 | wrq->u.data.length = len; | ||
772 | if (wrq->u.data.pointer) { | ||
773 | if (copy_to_user(wrq->u.data.pointer, &buf, len)) { | ||
774 | lbs_pr_debug(1, "CopyToUser failed\n"); | ||
775 | return -EFAULT; | ||
776 | } | ||
777 | } | ||
778 | |||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv, | ||
783 | struct ifreq *req) | ||
784 | { | ||
785 | int len; | ||
786 | char buf[8]; | ||
787 | struct iwreq *wrq = (struct iwreq *)req; | ||
788 | |||
789 | lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n"); | ||
790 | len = gettxantenna(priv, buf); | ||
791 | |||
792 | wrq->u.data.length = len; | ||
793 | if (wrq->u.data.pointer) { | ||
794 | if (copy_to_user(wrq->u.data.pointer, &buf, len)) { | ||
795 | lbs_pr_debug(1, "CopyToUser failed\n"); | ||
796 | return -EFAULT; | ||
797 | } | ||
798 | } | ||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * @brief Get the MAC TSF value from the firmware | ||
804 | * | ||
805 | * @param priv A pointer to wlan_private structure | ||
806 | * @param wrq A pointer to iwreq structure containing buffer | ||
807 | * space to store a TSF value retrieved from the firmware | ||
808 | * | ||
809 | * @return 0 if successful; IOCTL error code otherwise | ||
810 | */ | ||
811 | static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
812 | { | ||
813 | u64 tsfval; | ||
814 | int ret; | ||
815 | |||
816 | ret = libertas_prepare_and_send_command(priv, | ||
817 | cmd_get_tsf, | ||
818 | 0, cmd_option_waitforrsp, 0, &tsfval); | ||
819 | |||
820 | lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval); | ||
821 | |||
822 | if (ret != 0) { | ||
823 | lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n"); | ||
824 | ret = -EFAULT; | ||
825 | } else { | ||
826 | if (copy_to_user(wrq->u.data.pointer, | ||
827 | &tsfval, | ||
828 | min_t(size_t, wrq->u.data.length, | ||
829 | sizeof(tsfval))) != 0) { | ||
830 | |||
831 | lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n"); | ||
832 | ret = -EFAULT; | ||
833 | } else { | ||
834 | ret = 0; | ||
835 | } | ||
836 | } | ||
837 | return ret; | ||
838 | } | ||
839 | |||
840 | /** | ||
841 | * @brief Get/Set adapt rate | ||
842 | * @param priv A pointer to wlan_private structure | ||
843 | * @param wrq A pointer to iwreq structure | ||
844 | * @return 0 --success, otherwise fail | ||
845 | */ | ||
846 | static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq) | ||
847 | { | ||
848 | int ret; | ||
849 | wlan_adapter *adapter = priv->adapter; | ||
850 | int data[2]; | ||
851 | |||
852 | memset(data, 0, sizeof(data)); | ||
853 | if (!wrq->u.data.length) { | ||
854 | lbs_pr_debug(1, "Get ADAPT RATE SET\n"); | ||
855 | ret = libertas_prepare_and_send_command(priv, | ||
856 | cmd_802_11_rate_adapt_rateset, | ||
857 | cmd_act_get, | ||
858 | cmd_option_waitforrsp, 0, NULL); | ||
859 | data[0] = adapter->enablehwauto; | ||
860 | data[1] = adapter->ratebitmap; | ||
861 | if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { | ||
862 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
863 | return -EFAULT; | ||
864 | } | ||
865 | #define GET_TWO_INT 2 | ||
866 | wrq->u.data.length = GET_TWO_INT; | ||
867 | } else { | ||
868 | lbs_pr_debug(1, "Set ADAPT RATE SET\n"); | ||
869 | if (wrq->u.data.length > 2) | ||
870 | return -EINVAL; | ||
871 | if (copy_from_user | ||
872 | (data, wrq->u.data.pointer, | ||
873 | sizeof(int) * wrq->u.data.length)) { | ||
874 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
875 | return -EFAULT; | ||
876 | } | ||
877 | |||
878 | adapter->enablehwauto = data[0]; | ||
879 | adapter->ratebitmap = data[1]; | ||
880 | ret = libertas_prepare_and_send_command(priv, | ||
881 | cmd_802_11_rate_adapt_rateset, | ||
882 | cmd_act_set, | ||
883 | cmd_option_waitforrsp, 0, NULL); | ||
884 | } | ||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | /** | ||
889 | * @brief Get/Set inactivity timeout | ||
890 | * @param priv A pointer to wlan_private structure | ||
891 | * @param wrq A pointer to iwreq structure | ||
892 | * @return 0 --success, otherwise fail | ||
893 | */ | ||
894 | static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq) | ||
895 | { | ||
896 | int ret; | ||
897 | int data = 0; | ||
898 | u16 timeout = 0; | ||
899 | |||
900 | ENTER(); | ||
901 | if (wrq->u.data.length > 1) | ||
902 | return -ENOTSUPP; | ||
903 | |||
904 | if (wrq->u.data.length == 0) { | ||
905 | /* Get */ | ||
906 | ret = libertas_prepare_and_send_command(priv, | ||
907 | cmd_802_11_inactivity_timeout, | ||
908 | cmd_act_get, | ||
909 | cmd_option_waitforrsp, 0, | ||
910 | &timeout); | ||
911 | data = timeout; | ||
912 | if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { | ||
913 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
914 | return -EFAULT; | ||
915 | } | ||
916 | } else { | ||
917 | /* Set */ | ||
918 | if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { | ||
919 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
920 | return -EFAULT; | ||
921 | } | ||
922 | |||
923 | timeout = data; | ||
924 | ret = libertas_prepare_and_send_command(priv, | ||
925 | cmd_802_11_inactivity_timeout, | ||
926 | cmd_act_set, | ||
927 | cmd_option_waitforrsp, 0, | ||
928 | &timeout); | ||
929 | } | ||
930 | |||
931 | wrq->u.data.length = 1; | ||
932 | |||
933 | LEAVE(); | ||
934 | return ret; | ||
935 | } | ||
936 | |||
937 | static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
938 | { | ||
939 | int ret; | ||
940 | char buf[GETLOG_BUFSIZE - 1]; | ||
941 | wlan_adapter *adapter = priv->adapter; | ||
942 | |||
943 | lbs_pr_debug(1, " GET STATS\n"); | ||
944 | |||
945 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log, | ||
946 | 0, cmd_option_waitforrsp, 0, NULL); | ||
947 | |||
948 | if (ret) { | ||
949 | return ret; | ||
950 | } | ||
951 | |||
952 | if (wrq->u.data.pointer) { | ||
953 | sprintf(buf, "\n mcasttxframe %u failed %u retry %u " | ||
954 | "multiretry %u framedup %u " | ||
955 | "rtssuccess %u rtsfailure %u ackfailure %u\n" | ||
956 | "rxfrag %u mcastrxframe %u fcserror %u " | ||
957 | "txframe %u wepundecryptable %u ", | ||
958 | adapter->logmsg.mcasttxframe, | ||
959 | adapter->logmsg.failed, | ||
960 | adapter->logmsg.retry, | ||
961 | adapter->logmsg.multiretry, | ||
962 | adapter->logmsg.framedup, | ||
963 | adapter->logmsg.rtssuccess, | ||
964 | adapter->logmsg.rtsfailure, | ||
965 | adapter->logmsg.ackfailure, | ||
966 | adapter->logmsg.rxfrag, | ||
967 | adapter->logmsg.mcastrxframe, | ||
968 | adapter->logmsg.fcserror, | ||
969 | adapter->logmsg.txframe, | ||
970 | adapter->logmsg.wepundecryptable); | ||
971 | wrq->u.data.length = strlen(buf) + 1; | ||
972 | if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { | ||
973 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
974 | return -EFAULT; | ||
975 | } | ||
976 | } | ||
977 | |||
978 | return 0; | ||
979 | } | ||
980 | |||
981 | static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
982 | { | ||
983 | u8 buf[12]; | ||
984 | u8 *option[] = { "active", "passive", "get", }; | ||
985 | int i, max_options = (sizeof(option) / sizeof(option[0])); | ||
986 | int ret = 0; | ||
987 | wlan_adapter *adapter = priv->adapter; | ||
988 | |||
989 | if (priv->adapter->enable11d) { | ||
990 | lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n"); | ||
991 | return -EFAULT; | ||
992 | } | ||
993 | |||
994 | memset(buf, 0, sizeof(buf)); | ||
995 | |||
996 | if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), | ||
997 | wrq->u.data.length))) | ||
998 | return -EFAULT; | ||
999 | |||
1000 | lbs_pr_debug(1, "Scan type Option = %s\n", buf); | ||
1001 | |||
1002 | buf[sizeof(buf) - 1] = '\0'; | ||
1003 | |||
1004 | for (i = 0; i < max_options; i++) { | ||
1005 | if (!strcmp(buf, option[i])) | ||
1006 | break; | ||
1007 | } | ||
1008 | |||
1009 | switch (i) { | ||
1010 | case 0: | ||
1011 | adapter->scantype = cmd_scan_type_active; | ||
1012 | break; | ||
1013 | case 1: | ||
1014 | adapter->scantype = cmd_scan_type_passive; | ||
1015 | break; | ||
1016 | case 2: | ||
1017 | wrq->u.data.length = strlen(option[adapter->scantype]) + 1; | ||
1018 | |||
1019 | if (copy_to_user(wrq->u.data.pointer, | ||
1020 | option[adapter->scantype], | ||
1021 | wrq->u.data.length)) { | ||
1022 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
1023 | ret = -EFAULT; | ||
1024 | } | ||
1025 | |||
1026 | break; | ||
1027 | default: | ||
1028 | lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n"); | ||
1029 | ret = -EINVAL; | ||
1030 | break; | ||
1031 | } | ||
1032 | |||
1033 | return ret; | ||
1034 | } | ||
1035 | |||
1036 | static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
1037 | { | ||
1038 | wlan_adapter *adapter = priv->adapter; | ||
1039 | u8 buf[12]; | ||
1040 | u8 *option[] = { "bss", "ibss", "any", "get" }; | ||
1041 | int i, max_options = (sizeof(option) / sizeof(option[0])); | ||
1042 | int ret = 0; | ||
1043 | |||
1044 | ENTER(); | ||
1045 | |||
1046 | memset(buf, 0, sizeof(buf)); | ||
1047 | |||
1048 | if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), | ||
1049 | wrq->u.data.length))) { | ||
1050 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
1051 | return -EFAULT; | ||
1052 | } | ||
1053 | |||
1054 | lbs_pr_debug(1, "Scan mode Option = %s\n", buf); | ||
1055 | |||
1056 | buf[sizeof(buf) - 1] = '\0'; | ||
1057 | |||
1058 | for (i = 0; i < max_options; i++) { | ||
1059 | if (!strcmp(buf, option[i])) | ||
1060 | break; | ||
1061 | } | ||
1062 | |||
1063 | switch (i) { | ||
1064 | |||
1065 | case 0: | ||
1066 | adapter->scanmode = cmd_bss_type_bss; | ||
1067 | break; | ||
1068 | case 1: | ||
1069 | adapter->scanmode = cmd_bss_type_ibss; | ||
1070 | break; | ||
1071 | case 2: | ||
1072 | adapter->scanmode = cmd_bss_type_any; | ||
1073 | break; | ||
1074 | case 3: | ||
1075 | |||
1076 | wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1; | ||
1077 | |||
1078 | lbs_pr_debug(1, "Get Scan mode Option = %s\n", | ||
1079 | option[adapter->scanmode - 1]); | ||
1080 | |||
1081 | lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length); | ||
1082 | |||
1083 | if (copy_to_user(wrq->u.data.pointer, | ||
1084 | option[adapter->scanmode - 1], | ||
1085 | wrq->u.data.length)) { | ||
1086 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
1087 | ret = -EFAULT; | ||
1088 | } | ||
1089 | lbs_pr_debug(1, "GET Scan type Option after copy = %s\n", | ||
1090 | (char *)wrq->u.data.pointer); | ||
1091 | |||
1092 | break; | ||
1093 | |||
1094 | default: | ||
1095 | lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n"); | ||
1096 | ret = -EINVAL; | ||
1097 | break; | ||
1098 | } | ||
1099 | |||
1100 | LEAVE(); | ||
1101 | return ret; | ||
1102 | } | ||
1103 | |||
1104 | /** | ||
1105 | * @brief Get/Set Adhoc G Rate | ||
1106 | * | ||
1107 | * @param priv A pointer to wlan_private structure | ||
1108 | * @param wrq A pointer to user data | ||
1109 | * @return 0--success, otherwise fail | ||
1110 | */ | ||
1111 | static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq) | ||
1112 | { | ||
1113 | wlan_adapter *adapter = priv->adapter; | ||
1114 | int data, data1; | ||
1115 | int *val; | ||
1116 | |||
1117 | ENTER(); | ||
1118 | |||
1119 | data1 = SUBCMD_DATA(wrq); | ||
1120 | switch (data1) { | ||
1121 | case 0: | ||
1122 | adapter->adhoc_grate_enabled = 0; | ||
1123 | break; | ||
1124 | case 1: | ||
1125 | adapter->adhoc_grate_enabled = 1; | ||
1126 | break; | ||
1127 | case 2: | ||
1128 | break; | ||
1129 | default: | ||
1130 | return -EINVAL; | ||
1131 | } | ||
1132 | data = adapter->adhoc_grate_enabled; | ||
1133 | val = (int *)wrq->u.name; | ||
1134 | *val = data; | ||
1135 | LEAVE(); | ||
1136 | return 0; | ||
1137 | } | ||
1138 | |||
1139 | static inline int hex2int(char c) | ||
1140 | { | ||
1141 | if (c >= '0' && c <= '9') | ||
1142 | return (c - '0'); | ||
1143 | if (c >= 'a' && c <= 'f') | ||
1144 | return (c - 'a' + 10); | ||
1145 | if (c >= 'A' && c <= 'F') | ||
1146 | return (c - 'A' + 10); | ||
1147 | return -1; | ||
1148 | } | ||
1149 | |||
1150 | /* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx") | ||
1151 | into binary format (6 bytes). | ||
1152 | |||
1153 | This function expects that each byte is represented with 2 characters | ||
1154 | (e.g., 11:2:11:11:11:11 is invalid) | ||
1155 | |||
1156 | */ | ||
1157 | static char *eth_str2addr(char *ethstr, u8 * addr) | ||
1158 | { | ||
1159 | int i, val, val2; | ||
1160 | char *pos = ethstr; | ||
1161 | |||
1162 | /* get rid of initial blanks */ | ||
1163 | while (*pos == ' ' || *pos == '\t') | ||
1164 | ++pos; | ||
1165 | |||
1166 | for (i = 0; i < 6; i++) { | ||
1167 | val = hex2int(*pos++); | ||
1168 | if (val < 0) | ||
1169 | return NULL; | ||
1170 | val2 = hex2int(*pos++); | ||
1171 | if (val2 < 0) | ||
1172 | return NULL; | ||
1173 | addr[i] = (val * 16 + val2) & 0xff; | ||
1174 | |||
1175 | if (i < 5 && *pos++ != ':') | ||
1176 | return NULL; | ||
1177 | } | ||
1178 | return pos; | ||
1179 | } | ||
1180 | |||
1181 | /* this writes xx:xx:xx:xx:xx:xx into ethstr | ||
1182 | (ethstr must have space for 18 chars) */ | ||
1183 | static int eth_addr2str(u8 * addr, char *ethstr) | ||
1184 | { | ||
1185 | int i; | ||
1186 | char *pos = ethstr; | ||
1187 | |||
1188 | for (i = 0; i < 6; i++) { | ||
1189 | sprintf(pos, "%02x", addr[i] & 0xff); | ||
1190 | pos += 2; | ||
1191 | if (i < 5) | ||
1192 | *pos++ = ':'; | ||
1193 | } | ||
1194 | return 17; | ||
1195 | } | ||
1196 | |||
1197 | /** | ||
1198 | * @brief Add an entry to the BT table | ||
1199 | * @param priv A pointer to wlan_private structure | ||
1200 | * @param req A pointer to ifreq structure | ||
1201 | * @return 0 --success, otherwise fail | ||
1202 | */ | ||
1203 | static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req) | ||
1204 | { | ||
1205 | struct iwreq *wrq = (struct iwreq *)req; | ||
1206 | char ethaddrs_str[18]; | ||
1207 | char *pos; | ||
1208 | u8 ethaddr[ETH_ALEN]; | ||
1209 | |||
1210 | ENTER(); | ||
1211 | if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, | ||
1212 | sizeof(ethaddrs_str))) | ||
1213 | return -EFAULT; | ||
1214 | |||
1215 | if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { | ||
1216 | lbs_pr_info("BT_ADD: Invalid MAC address\n"); | ||
1217 | return -EINVAL; | ||
1218 | } | ||
1219 | |||
1220 | lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str); | ||
1221 | LEAVE(); | ||
1222 | return (libertas_prepare_and_send_command(priv, cmd_bt_access, | ||
1223 | cmd_act_bt_access_add, | ||
1224 | cmd_option_waitforrsp, 0, ethaddr)); | ||
1225 | } | ||
1226 | |||
1227 | /** | ||
1228 | * @brief Delete an entry from the BT table | ||
1229 | * @param priv A pointer to wlan_private structure | ||
1230 | * @param req A pointer to ifreq structure | ||
1231 | * @return 0 --success, otherwise fail | ||
1232 | */ | ||
1233 | static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req) | ||
1234 | { | ||
1235 | struct iwreq *wrq = (struct iwreq *)req; | ||
1236 | char ethaddrs_str[18]; | ||
1237 | u8 ethaddr[ETH_ALEN]; | ||
1238 | char *pos; | ||
1239 | |||
1240 | ENTER(); | ||
1241 | if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, | ||
1242 | sizeof(ethaddrs_str))) | ||
1243 | return -EFAULT; | ||
1244 | |||
1245 | if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { | ||
1246 | lbs_pr_info("Invalid MAC address\n"); | ||
1247 | return -EINVAL; | ||
1248 | } | ||
1249 | |||
1250 | lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str); | ||
1251 | |||
1252 | return (libertas_prepare_and_send_command(priv, | ||
1253 | cmd_bt_access, | ||
1254 | cmd_act_bt_access_del, | ||
1255 | cmd_option_waitforrsp, 0, ethaddr)); | ||
1256 | LEAVE(); | ||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | /** | ||
1261 | * @brief Reset all entries from the BT table | ||
1262 | * @param priv A pointer to wlan_private structure | ||
1263 | * @return 0 --success, otherwise fail | ||
1264 | */ | ||
1265 | static int wlan_bt_reset_ioctl(wlan_private * priv) | ||
1266 | { | ||
1267 | ENTER(); | ||
1268 | |||
1269 | lbs_pr_alert( "BT: resetting\n"); | ||
1270 | |||
1271 | return (libertas_prepare_and_send_command(priv, | ||
1272 | cmd_bt_access, | ||
1273 | cmd_act_bt_access_reset, | ||
1274 | cmd_option_waitforrsp, 0, NULL)); | ||
1275 | |||
1276 | LEAVE(); | ||
1277 | return 0; | ||
1278 | } | ||
1279 | |||
1280 | /** | ||
1281 | * @brief List an entry from the BT table | ||
1282 | * @param priv A pointer to wlan_private structure | ||
1283 | * @param req A pointer to ifreq structure | ||
1284 | * @return 0 --success, otherwise fail | ||
1285 | */ | ||
1286 | static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req) | ||
1287 | { | ||
1288 | int pos; | ||
1289 | char *addr1; | ||
1290 | struct iwreq *wrq = (struct iwreq *)req; | ||
1291 | /* used to pass id and store the bt entry returned by the FW */ | ||
1292 | union { | ||
1293 | int id; | ||
1294 | char addr1addr2[2 * ETH_ALEN]; | ||
1295 | } param; | ||
1296 | static char outstr[64]; | ||
1297 | char *pbuf = outstr; | ||
1298 | int ret; | ||
1299 | |||
1300 | ENTER(); | ||
1301 | |||
1302 | if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) { | ||
1303 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
1304 | return -1; | ||
1305 | } | ||
1306 | param.id = simple_strtoul(outstr, NULL, 10); | ||
1307 | pos = sprintf(pbuf, "%d: ", param.id); | ||
1308 | pbuf += pos; | ||
1309 | |||
1310 | ret = libertas_prepare_and_send_command(priv, cmd_bt_access, | ||
1311 | cmd_act_bt_access_list, | ||
1312 | cmd_option_waitforrsp, 0, | ||
1313 | (char *)¶m); | ||
1314 | |||
1315 | if (ret == 0) { | ||
1316 | addr1 = param.addr1addr2; | ||
1317 | |||
1318 | pos = sprintf(pbuf, "ignoring traffic from "); | ||
1319 | pbuf += pos; | ||
1320 | pos = eth_addr2str(addr1, pbuf); | ||
1321 | pbuf += pos; | ||
1322 | } else { | ||
1323 | sprintf(pbuf, "(null)"); | ||
1324 | pbuf += pos; | ||
1325 | } | ||
1326 | |||
1327 | wrq->u.data.length = strlen(outstr); | ||
1328 | if (copy_to_user(wrq->u.data.pointer, (char *)outstr, | ||
1329 | wrq->u.data.length)) { | ||
1330 | lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n"); | ||
1331 | return -EFAULT; | ||
1332 | } | ||
1333 | |||
1334 | LEAVE(); | ||
1335 | return 0; | ||
1336 | } | ||
1337 | |||
1338 | /** | ||
1339 | * @brief Find the next parameter in an input string | ||
1340 | * @param ptr A pointer to the input parameter string | ||
1341 | * @return A pointer to the next parameter, or 0 if no parameters left. | ||
1342 | */ | ||
1343 | static char * next_param(char * ptr) | ||
1344 | { | ||
1345 | if (!ptr) return NULL; | ||
1346 | while (*ptr == ' ' || *ptr == '\t') ++ptr; | ||
1347 | return (*ptr == '\0') ? NULL : ptr; | ||
1348 | } | ||
1349 | |||
1350 | /** | ||
1351 | * @brief Add an entry to the FWT table | ||
1352 | * @param priv A pointer to wlan_private structure | ||
1353 | * @param req A pointer to ifreq structure | ||
1354 | * @return 0 --success, otherwise fail | ||
1355 | */ | ||
1356 | static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req) | ||
1357 | { | ||
1358 | struct iwreq *wrq = (struct iwreq *)req; | ||
1359 | char in_str[128]; | ||
1360 | static struct cmd_ds_fwt_access fwt_access; | ||
1361 | char *ptr; | ||
1362 | |||
1363 | ENTER(); | ||
1364 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) | ||
1365 | return -EFAULT; | ||
1366 | |||
1367 | if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { | ||
1368 | lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n"); | ||
1369 | return -EINVAL; | ||
1370 | } | ||
1371 | |||
1372 | if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { | ||
1373 | lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n"); | ||
1374 | return -EINVAL; | ||
1375 | } | ||
1376 | |||
1377 | if ((ptr = next_param(ptr))) | ||
1378 | fwt_access.metric = | ||
1379 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1380 | else | ||
1381 | fwt_access.metric = FWT_DEFAULT_METRIC; | ||
1382 | |||
1383 | if ((ptr = next_param(ptr))) | ||
1384 | fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); | ||
1385 | else | ||
1386 | fwt_access.dir = FWT_DEFAULT_DIR; | ||
1387 | |||
1388 | if ((ptr = next_param(ptr))) | ||
1389 | fwt_access.ssn = | ||
1390 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1391 | else | ||
1392 | fwt_access.ssn = FWT_DEFAULT_SSN; | ||
1393 | |||
1394 | if ((ptr = next_param(ptr))) | ||
1395 | fwt_access.dsn = | ||
1396 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1397 | else | ||
1398 | fwt_access.dsn = FWT_DEFAULT_DSN; | ||
1399 | |||
1400 | if ((ptr = next_param(ptr))) | ||
1401 | fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10); | ||
1402 | else | ||
1403 | fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT; | ||
1404 | |||
1405 | if ((ptr = next_param(ptr))) | ||
1406 | fwt_access.ttl = simple_strtoul(ptr, &ptr, 10); | ||
1407 | else | ||
1408 | fwt_access.ttl = FWT_DEFAULT_TTL; | ||
1409 | |||
1410 | if ((ptr = next_param(ptr))) | ||
1411 | fwt_access.expiration = | ||
1412 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1413 | else | ||
1414 | fwt_access.expiration = FWT_DEFAULT_EXPIRATION; | ||
1415 | |||
1416 | if ((ptr = next_param(ptr))) | ||
1417 | fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10); | ||
1418 | else | ||
1419 | fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE; | ||
1420 | |||
1421 | if ((ptr = next_param(ptr))) | ||
1422 | fwt_access.snr = | ||
1423 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1424 | else | ||
1425 | fwt_access.snr = FWT_DEFAULT_SNR; | ||
1426 | |||
1427 | #ifdef DEBUG | ||
1428 | { | ||
1429 | char ethaddr1_str[18], ethaddr2_str[18]; | ||
1430 | eth_addr2str(fwt_access.da, ethaddr1_str); | ||
1431 | eth_addr2str(fwt_access.ra, ethaddr2_str); | ||
1432 | lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str, | ||
1433 | fwt_access.dir, ethaddr2_str); | ||
1434 | lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n", | ||
1435 | fwt_access.ssn, fwt_access.dsn, fwt_access.metric, | ||
1436 | fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration, | ||
1437 | fwt_access.sleepmode, fwt_access.snr); | ||
1438 | } | ||
1439 | #endif | ||
1440 | |||
1441 | LEAVE(); | ||
1442 | return (libertas_prepare_and_send_command(priv, cmd_fwt_access, | ||
1443 | cmd_act_fwt_access_add, | ||
1444 | cmd_option_waitforrsp, 0, | ||
1445 | (void *)&fwt_access)); | ||
1446 | } | ||
1447 | |||
1448 | /** | ||
1449 | * @brief Delete an entry from the FWT table | ||
1450 | * @param priv A pointer to wlan_private structure | ||
1451 | * @param req A pointer to ifreq structure | ||
1452 | * @return 0 --success, otherwise fail | ||
1453 | */ | ||
1454 | static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req) | ||
1455 | { | ||
1456 | struct iwreq *wrq = (struct iwreq *)req; | ||
1457 | char in_str[64]; | ||
1458 | static struct cmd_ds_fwt_access fwt_access; | ||
1459 | char *ptr; | ||
1460 | |||
1461 | ENTER(); | ||
1462 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) | ||
1463 | return -EFAULT; | ||
1464 | |||
1465 | if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { | ||
1466 | lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n"); | ||
1467 | return -EINVAL; | ||
1468 | } | ||
1469 | |||
1470 | if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { | ||
1471 | lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n"); | ||
1472 | return -EINVAL; | ||
1473 | } | ||
1474 | |||
1475 | if ((ptr = next_param(ptr))) | ||
1476 | fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); | ||
1477 | else | ||
1478 | fwt_access.dir = FWT_DEFAULT_DIR; | ||
1479 | |||
1480 | #ifdef DEBUG | ||
1481 | { | ||
1482 | char ethaddr1_str[18], ethaddr2_str[18]; | ||
1483 | lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str); | ||
1484 | eth_addr2str(fwt_access.da, ethaddr1_str); | ||
1485 | eth_addr2str(fwt_access.ra, ethaddr2_str); | ||
1486 | lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str, | ||
1487 | ethaddr2_str, fwt_access.dir); | ||
1488 | } | ||
1489 | #endif | ||
1490 | |||
1491 | LEAVE(); | ||
1492 | return (libertas_prepare_and_send_command(priv, | ||
1493 | cmd_fwt_access, | ||
1494 | cmd_act_fwt_access_del, | ||
1495 | cmd_option_waitforrsp, 0, | ||
1496 | (void *)&fwt_access)); | ||
1497 | } | ||
1498 | |||
1499 | |||
1500 | /** | ||
1501 | * @brief Print route parameters | ||
1502 | * @param fwt_access struct cmd_ds_fwt_access with route info | ||
1503 | * @param buf destination buffer for route info | ||
1504 | */ | ||
1505 | static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf) | ||
1506 | { | ||
1507 | buf += sprintf(buf, " "); | ||
1508 | buf += eth_addr2str(fwt_access.da, buf); | ||
1509 | buf += sprintf(buf, " "); | ||
1510 | buf += eth_addr2str(fwt_access.ra, buf); | ||
1511 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric)); | ||
1512 | buf += sprintf(buf, " %u", fwt_access.dir); | ||
1513 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn)); | ||
1514 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn)); | ||
1515 | buf += sprintf(buf, " %u", fwt_access.hopcount); | ||
1516 | buf += sprintf(buf, " %u", fwt_access.ttl); | ||
1517 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration)); | ||
1518 | buf += sprintf(buf, " %u", fwt_access.sleepmode); | ||
1519 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr)); | ||
1520 | } | ||
1521 | |||
1522 | /** | ||
1523 | * @brief Lookup an entry in the FWT table | ||
1524 | * @param priv A pointer to wlan_private structure | ||
1525 | * @param req A pointer to ifreq structure | ||
1526 | * @return 0 --success, otherwise fail | ||
1527 | */ | ||
1528 | static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req) | ||
1529 | { | ||
1530 | struct iwreq *wrq = (struct iwreq *)req; | ||
1531 | char in_str[64]; | ||
1532 | char *ptr; | ||
1533 | static struct cmd_ds_fwt_access fwt_access; | ||
1534 | static char out_str[128]; | ||
1535 | int ret; | ||
1536 | |||
1537 | ENTER(); | ||
1538 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) | ||
1539 | return -EFAULT; | ||
1540 | |||
1541 | if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { | ||
1542 | lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n"); | ||
1543 | return -EINVAL; | ||
1544 | } | ||
1545 | |||
1546 | #ifdef DEBUG | ||
1547 | { | ||
1548 | char ethaddr1_str[18]; | ||
1549 | lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str); | ||
1550 | eth_addr2str(fwt_access.da, ethaddr1_str); | ||
1551 | lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str); | ||
1552 | } | ||
1553 | #endif | ||
1554 | |||
1555 | ret = libertas_prepare_and_send_command(priv, | ||
1556 | cmd_fwt_access, | ||
1557 | cmd_act_fwt_access_lookup, | ||
1558 | cmd_option_waitforrsp, 0, | ||
1559 | (void *)&fwt_access); | ||
1560 | |||
1561 | if (ret == 0) | ||
1562 | print_route(fwt_access, out_str); | ||
1563 | else | ||
1564 | sprintf(out_str, "(null)"); | ||
1565 | |||
1566 | wrq->u.data.length = strlen(out_str); | ||
1567 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | ||
1568 | wrq->u.data.length)) { | ||
1569 | lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n"); | ||
1570 | return -EFAULT; | ||
1571 | } | ||
1572 | |||
1573 | LEAVE(); | ||
1574 | return 0; | ||
1575 | } | ||
1576 | |||
1577 | /** | ||
1578 | * @brief Reset all entries from the FWT table | ||
1579 | * @param priv A pointer to wlan_private structure | ||
1580 | * @return 0 --success, otherwise fail | ||
1581 | */ | ||
1582 | static int wlan_fwt_reset_ioctl(wlan_private * priv) | ||
1583 | { | ||
1584 | lbs_pr_debug(1, "FWT: resetting\n"); | ||
1585 | |||
1586 | return (libertas_prepare_and_send_command(priv, | ||
1587 | cmd_fwt_access, | ||
1588 | cmd_act_fwt_access_reset, | ||
1589 | cmd_option_waitforrsp, 0, NULL)); | ||
1590 | } | ||
1591 | |||
1592 | /** | ||
1593 | * @brief List an entry from the FWT table | ||
1594 | * @param priv A pointer to wlan_private structure | ||
1595 | * @param req A pointer to ifreq structure | ||
1596 | * @return 0 --success, otherwise fail | ||
1597 | */ | ||
1598 | static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req) | ||
1599 | { | ||
1600 | struct iwreq *wrq = (struct iwreq *)req; | ||
1601 | char in_str[8]; | ||
1602 | static struct cmd_ds_fwt_access fwt_access; | ||
1603 | char *ptr = in_str; | ||
1604 | static char out_str[128]; | ||
1605 | char *pbuf = out_str; | ||
1606 | int ret; | ||
1607 | |||
1608 | ENTER(); | ||
1609 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) | ||
1610 | return -EFAULT; | ||
1611 | |||
1612 | fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1613 | |||
1614 | #ifdef DEBUG | ||
1615 | { | ||
1616 | lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str); | ||
1617 | lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id)); | ||
1618 | } | ||
1619 | #endif | ||
1620 | |||
1621 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | ||
1622 | cmd_act_fwt_access_list, | ||
1623 | cmd_option_waitforrsp, 0, (void *)&fwt_access); | ||
1624 | |||
1625 | if (ret == 0) | ||
1626 | print_route(fwt_access, pbuf); | ||
1627 | else | ||
1628 | pbuf += sprintf(pbuf, " (null)"); | ||
1629 | |||
1630 | wrq->u.data.length = strlen(out_str); | ||
1631 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | ||
1632 | wrq->u.data.length)) { | ||
1633 | lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n"); | ||
1634 | return -EFAULT; | ||
1635 | } | ||
1636 | |||
1637 | LEAVE(); | ||
1638 | return 0; | ||
1639 | } | ||
1640 | |||
1641 | /** | ||
1642 | * @brief List an entry from the FRT table | ||
1643 | * @param priv A pointer to wlan_private structure | ||
1644 | * @param req A pointer to ifreq structure | ||
1645 | * @return 0 --success, otherwise fail | ||
1646 | */ | ||
1647 | static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req) | ||
1648 | { | ||
1649 | struct iwreq *wrq = (struct iwreq *)req; | ||
1650 | char in_str[64]; | ||
1651 | static struct cmd_ds_fwt_access fwt_access; | ||
1652 | char *ptr = in_str; | ||
1653 | static char out_str[128]; | ||
1654 | char *pbuf = out_str; | ||
1655 | int ret; | ||
1656 | |||
1657 | ENTER(); | ||
1658 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) | ||
1659 | return -EFAULT; | ||
1660 | |||
1661 | fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1662 | |||
1663 | #ifdef DEBUG | ||
1664 | { | ||
1665 | lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str); | ||
1666 | lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id)); | ||
1667 | } | ||
1668 | #endif | ||
1669 | |||
1670 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | ||
1671 | cmd_act_fwt_access_list_route, | ||
1672 | cmd_option_waitforrsp, 0, (void *)&fwt_access); | ||
1673 | |||
1674 | if (ret == 0) { | ||
1675 | pbuf += sprintf(pbuf, " "); | ||
1676 | pbuf += eth_addr2str(fwt_access.da, pbuf); | ||
1677 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric)); | ||
1678 | pbuf += sprintf(pbuf, " %u", fwt_access.dir); | ||
1679 | /* note that the firmware returns the nid in the id field */ | ||
1680 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id)); | ||
1681 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn)); | ||
1682 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn)); | ||
1683 | pbuf += sprintf(pbuf, " hop %u", fwt_access.hopcount); | ||
1684 | pbuf += sprintf(pbuf, " ttl %u", fwt_access.ttl); | ||
1685 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration)); | ||
1686 | } else | ||
1687 | pbuf += sprintf(pbuf, " (null)"); | ||
1688 | |||
1689 | wrq->u.data.length = strlen(out_str); | ||
1690 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | ||
1691 | wrq->u.data.length)) { | ||
1692 | lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n"); | ||
1693 | return -EFAULT; | ||
1694 | } | ||
1695 | |||
1696 | LEAVE(); | ||
1697 | return 0; | ||
1698 | } | ||
1699 | |||
1700 | /** | ||
1701 | * @brief List an entry from the FNT table | ||
1702 | * @param priv A pointer to wlan_private structure | ||
1703 | * @param req A pointer to ifreq structure | ||
1704 | * @return 0 --success, otherwise fail | ||
1705 | */ | ||
1706 | static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req) | ||
1707 | { | ||
1708 | struct iwreq *wrq = (struct iwreq *)req; | ||
1709 | char in_str[8]; | ||
1710 | static struct cmd_ds_fwt_access fwt_access; | ||
1711 | char *ptr = in_str; | ||
1712 | static char out_str[128]; | ||
1713 | char *pbuf = out_str; | ||
1714 | int ret; | ||
1715 | |||
1716 | ENTER(); | ||
1717 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) | ||
1718 | return -EFAULT; | ||
1719 | |||
1720 | memset(&fwt_access, 0, sizeof(fwt_access)); | ||
1721 | fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | ||
1722 | |||
1723 | #ifdef DEBUG | ||
1724 | { | ||
1725 | lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str); | ||
1726 | lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id)); | ||
1727 | } | ||
1728 | #endif | ||
1729 | |||
1730 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | ||
1731 | cmd_act_fwt_access_list_neighbor, | ||
1732 | cmd_option_waitforrsp, 0, | ||
1733 | (void *)&fwt_access); | ||
1734 | |||
1735 | if (ret == 0) { | ||
1736 | pbuf += sprintf(pbuf, " ra "); | ||
1737 | pbuf += eth_addr2str(fwt_access.ra, pbuf); | ||
1738 | pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode); | ||
1739 | pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr)); | ||
1740 | pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references)); | ||
1741 | } else | ||
1742 | pbuf += sprintf(pbuf, " (null)"); | ||
1743 | |||
1744 | wrq->u.data.length = strlen(out_str); | ||
1745 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | ||
1746 | wrq->u.data.length)) { | ||
1747 | lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n"); | ||
1748 | return -EFAULT; | ||
1749 | } | ||
1750 | |||
1751 | LEAVE(); | ||
1752 | return 0; | ||
1753 | } | ||
1754 | |||
1755 | /** | ||
1756 | * @brief Cleans up the route (FRT) and neighbor (FNT) tables | ||
1757 | * (Garbage Collection) | ||
1758 | * @param priv A pointer to wlan_private structure | ||
1759 | * @param req A pointer to ifreq structure | ||
1760 | * @return 0 --success, otherwise fail | ||
1761 | */ | ||
1762 | static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req) | ||
1763 | { | ||
1764 | static struct cmd_ds_fwt_access fwt_access; | ||
1765 | int ret; | ||
1766 | |||
1767 | ENTER(); | ||
1768 | |||
1769 | lbs_pr_debug(1, "FWT: cleaning up\n"); | ||
1770 | |||
1771 | memset(&fwt_access, 0, sizeof(fwt_access)); | ||
1772 | |||
1773 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | ||
1774 | cmd_act_fwt_access_cleanup, | ||
1775 | cmd_option_waitforrsp, 0, | ||
1776 | (void *)&fwt_access); | ||
1777 | |||
1778 | if (ret == 0) | ||
1779 | req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); | ||
1780 | else | ||
1781 | return -EFAULT; | ||
1782 | |||
1783 | LEAVE(); | ||
1784 | return 0; | ||
1785 | } | ||
1786 | |||
1787 | /** | ||
1788 | * @brief Gets firmware internal time (debug purposes) | ||
1789 | * @param priv A pointer to wlan_private structure | ||
1790 | * @param req A pointer to ifreq structure | ||
1791 | * @return 0 --success, otherwise fail | ||
1792 | */ | ||
1793 | static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req) | ||
1794 | { | ||
1795 | static struct cmd_ds_fwt_access fwt_access; | ||
1796 | int ret; | ||
1797 | |||
1798 | ENTER(); | ||
1799 | |||
1800 | lbs_pr_debug(1, "FWT: getting time\n"); | ||
1801 | |||
1802 | memset(&fwt_access, 0, sizeof(fwt_access)); | ||
1803 | |||
1804 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | ||
1805 | cmd_act_fwt_access_time, | ||
1806 | cmd_option_waitforrsp, 0, | ||
1807 | (void *)&fwt_access); | ||
1808 | |||
1809 | if (ret == 0) | ||
1810 | req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); | ||
1811 | else | ||
1812 | return -EFAULT; | ||
1813 | |||
1814 | LEAVE(); | ||
1815 | return 0; | ||
1816 | } | ||
1817 | |||
1818 | /** | ||
1819 | * @brief Gets mesh ttl from firmware | ||
1820 | * @param priv A pointer to wlan_private structure | ||
1821 | * @param req A pointer to ifreq structure | ||
1822 | * @return 0 --success, otherwise fail | ||
1823 | */ | ||
1824 | static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req) | ||
1825 | { | ||
1826 | struct cmd_ds_mesh_access mesh_access; | ||
1827 | int ret; | ||
1828 | |||
1829 | ENTER(); | ||
1830 | |||
1831 | memset(&mesh_access, 0, sizeof(mesh_access)); | ||
1832 | |||
1833 | ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, | ||
1834 | cmd_act_mesh_get_ttl, | ||
1835 | cmd_option_waitforrsp, 0, | ||
1836 | (void *)&mesh_access); | ||
1837 | |||
1838 | if (ret == 0) { | ||
1839 | req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0])); | ||
1840 | } | ||
1841 | else | ||
1842 | return -EFAULT; | ||
1843 | |||
1844 | LEAVE(); | ||
1845 | return 0; | ||
1846 | } | ||
1847 | |||
1848 | /** | ||
1849 | * @brief Gets mesh ttl from firmware | ||
1850 | * @param priv A pointer to wlan_private structure | ||
1851 | * @param ttl New ttl value | ||
1852 | * @return 0 --success, otherwise fail | ||
1853 | */ | ||
1854 | static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl) | ||
1855 | { | ||
1856 | struct cmd_ds_mesh_access mesh_access; | ||
1857 | int ret; | ||
1858 | |||
1859 | ENTER(); | ||
1860 | |||
1861 | if( (ttl > 0xff) || (ttl < 0) ) | ||
1862 | return -EINVAL; | ||
1863 | |||
1864 | memset(&mesh_access, 0, sizeof(mesh_access)); | ||
1865 | mesh_access.data[0] = ttl; | ||
1866 | |||
1867 | ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, | ||
1868 | cmd_act_mesh_set_ttl, | ||
1869 | cmd_option_waitforrsp, 0, | ||
1870 | (void *)&mesh_access); | ||
1871 | |||
1872 | if (ret != 0) | ||
1873 | ret = -EFAULT; | ||
1874 | |||
1875 | LEAVE(); | ||
1876 | return ret; | ||
1877 | } | ||
1878 | |||
1879 | /** | ||
1880 | * @brief ioctl function - entry point | ||
1881 | * | ||
1882 | * @param dev A pointer to net_device structure | ||
1883 | * @param req A pointer to ifreq structure | ||
1884 | * @param cmd command | ||
1885 | * @return 0--success, otherwise fail | ||
1886 | */ | ||
1887 | int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) | ||
1888 | { | ||
1889 | int subcmd = 0; | ||
1890 | int idata = 0; | ||
1891 | int *pdata; | ||
1892 | int ret = 0; | ||
1893 | wlan_private *priv = dev->priv; | ||
1894 | wlan_adapter *adapter = priv->adapter; | ||
1895 | struct iwreq *wrq = (struct iwreq *)req; | ||
1896 | |||
1897 | ENTER(); | ||
1898 | |||
1899 | lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd); | ||
1900 | switch (cmd) { | ||
1901 | case WLANSCAN_TYPE: | ||
1902 | lbs_pr_debug(1, "Scan type Ioctl\n"); | ||
1903 | ret = wlan_scan_type_ioctl(priv, wrq); | ||
1904 | break; | ||
1905 | |||
1906 | case WLAN_SETNONE_GETNONE: /* set WPA mode on/off ioctl #20 */ | ||
1907 | switch (wrq->u.data.flags) { | ||
1908 | case WLANDEAUTH: | ||
1909 | lbs_pr_debug(1, "Deauth\n"); | ||
1910 | libertas_send_deauth(priv); | ||
1911 | break; | ||
1912 | |||
1913 | case WLANADHOCSTOP: | ||
1914 | lbs_pr_debug(1, "Adhoc stop\n"); | ||
1915 | ret = libertas_do_adhocstop_ioctl(priv); | ||
1916 | break; | ||
1917 | |||
1918 | case WLANRADIOON: | ||
1919 | wlan_radio_ioctl(priv, 1); | ||
1920 | break; | ||
1921 | |||
1922 | case WLANRADIOOFF: | ||
1923 | wlan_radio_ioctl(priv, 0); | ||
1924 | break; | ||
1925 | case WLANWLANIDLEON: | ||
1926 | libertas_idle_on(priv); | ||
1927 | break; | ||
1928 | case WLANWLANIDLEOFF: | ||
1929 | libertas_idle_off(priv); | ||
1930 | break; | ||
1931 | case WLAN_SUBCMD_BT_RESET: /* bt_reset */ | ||
1932 | wlan_bt_reset_ioctl(priv); | ||
1933 | break; | ||
1934 | case WLAN_SUBCMD_FWT_RESET: /* fwt_reset */ | ||
1935 | wlan_fwt_reset_ioctl(priv); | ||
1936 | break; | ||
1937 | } /* End of switch */ | ||
1938 | break; | ||
1939 | |||
1940 | case WLANSETWPAIE: | ||
1941 | ret = wlan_setwpaie_ioctl(priv, req); | ||
1942 | break; | ||
1943 | case WLAN_SETINT_GETINT: | ||
1944 | /* The first 4 bytes of req->ifr_data is sub-ioctl number | ||
1945 | * after 4 bytes sits the payload. | ||
1946 | */ | ||
1947 | subcmd = (int)req->ifr_data; //from iwpriv subcmd | ||
1948 | switch (subcmd) { | ||
1949 | case WLANNF: | ||
1950 | ret = wlan_get_nf(priv, wrq); | ||
1951 | break; | ||
1952 | case WLANRSSI: | ||
1953 | ret = wlan_get_rssi(priv, wrq); | ||
1954 | break; | ||
1955 | case WLANENABLE11D: | ||
1956 | ret = libertas_cmd_enable_11d(priv, wrq); | ||
1957 | break; | ||
1958 | case WLANADHOCGRATE: | ||
1959 | ret = wlan_do_set_grate_ioctl(priv, wrq); | ||
1960 | break; | ||
1961 | case WLAN_SUBCMD_SET_PRESCAN: | ||
1962 | ret = wlan_subcmd_setprescan_ioctl(priv, wrq); | ||
1963 | break; | ||
1964 | } | ||
1965 | break; | ||
1966 | |||
1967 | case WLAN_SETONEINT_GETONEINT: | ||
1968 | switch (wrq->u.data.flags) { | ||
1969 | case WLAN_BEACON_INTERVAL: | ||
1970 | ret = wlan_beacon_interval(priv, wrq); | ||
1971 | break; | ||
1972 | |||
1973 | case WLAN_LISTENINTRVL: | ||
1974 | if (!wrq->u.data.length) { | ||
1975 | int data; | ||
1976 | lbs_pr_debug(1, "Get locallisteninterval value\n"); | ||
1977 | #define GET_ONE_INT 1 | ||
1978 | data = adapter->locallisteninterval; | ||
1979 | if (copy_to_user(wrq->u.data.pointer, | ||
1980 | &data, sizeof(int))) { | ||
1981 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
1982 | return -EFAULT; | ||
1983 | } | ||
1984 | |||
1985 | wrq->u.data.length = GET_ONE_INT; | ||
1986 | } else { | ||
1987 | int data; | ||
1988 | if (copy_from_user | ||
1989 | (&data, wrq->u.data.pointer, sizeof(int))) { | ||
1990 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
1991 | return -EFAULT; | ||
1992 | } | ||
1993 | |||
1994 | lbs_pr_debug(1, "Set locallisteninterval = %d\n", | ||
1995 | data); | ||
1996 | #define MAX_U16_VAL 65535 | ||
1997 | if (data > MAX_U16_VAL) { | ||
1998 | lbs_pr_debug(1, "Exceeds U16 value\n"); | ||
1999 | return -EINVAL; | ||
2000 | } | ||
2001 | adapter->locallisteninterval = data; | ||
2002 | } | ||
2003 | break; | ||
2004 | case WLAN_TXCONTROL: | ||
2005 | ret = wlan_txcontrol(priv, wrq); //adds for txcontrol ioctl | ||
2006 | break; | ||
2007 | |||
2008 | case WLAN_NULLPKTINTERVAL: | ||
2009 | ret = wlan_null_pkt_interval(priv, wrq); | ||
2010 | break; | ||
2011 | |||
2012 | default: | ||
2013 | ret = -EOPNOTSUPP; | ||
2014 | break; | ||
2015 | } | ||
2016 | break; | ||
2017 | |||
2018 | case WLAN_SETONEINT_GETNONE: | ||
2019 | /* The first 4 bytes of req->ifr_data is sub-ioctl number | ||
2020 | * after 4 bytes sits the payload. | ||
2021 | */ | ||
2022 | subcmd = wrq->u.data.flags; //from wpa_supplicant subcmd | ||
2023 | |||
2024 | if (!subcmd) | ||
2025 | subcmd = (int)req->ifr_data; //from iwpriv subcmd | ||
2026 | |||
2027 | switch (subcmd) { | ||
2028 | case WLAN_SUBCMD_SETRXANTENNA: /* SETRXANTENNA */ | ||
2029 | idata = SUBCMD_DATA(wrq); | ||
2030 | ret = setrxantenna(priv, idata); | ||
2031 | break; | ||
2032 | case WLAN_SUBCMD_SETTXANTENNA: /* SETTXANTENNA */ | ||
2033 | idata = SUBCMD_DATA(wrq); | ||
2034 | ret = settxantenna(priv, idata); | ||
2035 | break; | ||
2036 | case WLAN_SET_ATIM_WINDOW: | ||
2037 | adapter->atimwindow = SUBCMD_DATA(wrq); | ||
2038 | adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50); | ||
2039 | break; | ||
2040 | case WLANSETBCNAVG: | ||
2041 | adapter->bcn_avg_factor = SUBCMD_DATA(wrq); | ||
2042 | if (adapter->bcn_avg_factor == 0) | ||
2043 | adapter->bcn_avg_factor = | ||
2044 | DEFAULT_BCN_AVG_FACTOR; | ||
2045 | if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR) | ||
2046 | adapter->bcn_avg_factor = | ||
2047 | DEFAULT_BCN_AVG_FACTOR; | ||
2048 | break; | ||
2049 | case WLANSETDATAAVG: | ||
2050 | adapter->data_avg_factor = SUBCMD_DATA(wrq); | ||
2051 | if (adapter->data_avg_factor == 0) | ||
2052 | adapter->data_avg_factor = | ||
2053 | DEFAULT_DATA_AVG_FACTOR; | ||
2054 | if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR) | ||
2055 | adapter->data_avg_factor = | ||
2056 | DEFAULT_DATA_AVG_FACTOR; | ||
2057 | break; | ||
2058 | case WLANSETREGION: | ||
2059 | idata = SUBCMD_DATA(wrq); | ||
2060 | ret = wlan_set_region(priv, (u16) idata); | ||
2061 | break; | ||
2062 | |||
2063 | case WLAN_SET_LISTEN_INTERVAL: | ||
2064 | idata = SUBCMD_DATA(wrq); | ||
2065 | adapter->listeninterval = (u16) idata; | ||
2066 | break; | ||
2067 | |||
2068 | case WLAN_SET_MULTIPLE_DTIM: | ||
2069 | ret = wlan_set_multiple_dtim_ioctl(priv, req); | ||
2070 | break; | ||
2071 | |||
2072 | case WLANSETAUTHALG: | ||
2073 | ret = wlan_setauthalg_ioctl(priv, req); | ||
2074 | break; | ||
2075 | |||
2076 | case WLANSET8021XAUTHALG: | ||
2077 | ret = wlan_set8021xauthalg_ioctl(priv, req); | ||
2078 | break; | ||
2079 | |||
2080 | case WLANSETENCRYPTIONMODE: | ||
2081 | ret = wlan_setencryptionmode_ioctl(priv, req); | ||
2082 | break; | ||
2083 | |||
2084 | case WLAN_SET_LINKMODE: | ||
2085 | ret = wlan_set_linkmode_ioctl(priv, req); | ||
2086 | break; | ||
2087 | |||
2088 | case WLAN_SET_RADIOMODE: | ||
2089 | ret = wlan_set_radiomode_ioctl(priv, req); | ||
2090 | break; | ||
2091 | |||
2092 | case WLAN_SET_DEBUGMODE: | ||
2093 | ret = wlan_set_debugmode_ioctl(priv, req); | ||
2094 | break; | ||
2095 | |||
2096 | case WLAN_SUBCMD_MESH_SET_TTL: | ||
2097 | idata = SUBCMD_DATA(wrq); | ||
2098 | ret = wlan_mesh_set_ttl_ioctl(priv, idata); | ||
2099 | break; | ||
2100 | |||
2101 | default: | ||
2102 | ret = -EOPNOTSUPP; | ||
2103 | break; | ||
2104 | } | ||
2105 | |||
2106 | break; | ||
2107 | |||
2108 | case WLAN_SETNONE_GETTWELVE_CHAR: /* Get Antenna settings */ | ||
2109 | /* | ||
2110 | * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is | ||
2111 | * in flags of iwreq structure, otherwise it will be in | ||
2112 | * mode member of iwreq structure. | ||
2113 | */ | ||
2114 | switch ((int)wrq->u.data.flags) { | ||
2115 | case WLAN_SUBCMD_GETRXANTENNA: /* Get Rx Antenna */ | ||
2116 | ret = wlan_subcmd_getrxantenna_ioctl(priv, req); | ||
2117 | break; | ||
2118 | |||
2119 | case WLAN_SUBCMD_GETTXANTENNA: /* Get Tx Antenna */ | ||
2120 | ret = wlan_subcmd_gettxantenna_ioctl(priv, req); | ||
2121 | break; | ||
2122 | |||
2123 | case WLAN_GET_TSF: | ||
2124 | ret = wlan_get_tsf_ioctl(priv, wrq); | ||
2125 | break; | ||
2126 | } | ||
2127 | break; | ||
2128 | |||
2129 | case WLAN_SET128CHAR_GET128CHAR: | ||
2130 | switch ((int)wrq->u.data.flags) { | ||
2131 | |||
2132 | case WLANSCAN_MODE: | ||
2133 | lbs_pr_debug(1, "Scan mode Ioctl\n"); | ||
2134 | ret = wlan_scan_mode_ioctl(priv, wrq); | ||
2135 | break; | ||
2136 | |||
2137 | case WLAN_GET_ADHOC_STATUS: | ||
2138 | ret = wlan_get_adhoc_status_ioctl(priv, wrq); | ||
2139 | break; | ||
2140 | case WLAN_SUBCMD_BT_ADD: | ||
2141 | ret = wlan_bt_add_ioctl(priv, req); | ||
2142 | break; | ||
2143 | case WLAN_SUBCMD_BT_DEL: | ||
2144 | ret = wlan_bt_del_ioctl(priv, req); | ||
2145 | break; | ||
2146 | case WLAN_SUBCMD_BT_LIST: | ||
2147 | ret = wlan_bt_list_ioctl(priv, req); | ||
2148 | break; | ||
2149 | case WLAN_SUBCMD_FWT_ADD: | ||
2150 | ret = wlan_fwt_add_ioctl(priv, req); | ||
2151 | break; | ||
2152 | case WLAN_SUBCMD_FWT_DEL: | ||
2153 | ret = wlan_fwt_del_ioctl(priv, req); | ||
2154 | break; | ||
2155 | case WLAN_SUBCMD_FWT_LOOKUP: | ||
2156 | ret = wlan_fwt_lookup_ioctl(priv, req); | ||
2157 | break; | ||
2158 | case WLAN_SUBCMD_FWT_LIST_NEIGHBOR: | ||
2159 | ret = wlan_fwt_list_neighbor_ioctl(priv, req); | ||
2160 | break; | ||
2161 | case WLAN_SUBCMD_FWT_LIST: | ||
2162 | ret = wlan_fwt_list_ioctl(priv, req); | ||
2163 | break; | ||
2164 | case WLAN_SUBCMD_FWT_LIST_ROUTE: | ||
2165 | ret = wlan_fwt_list_route_ioctl(priv, req); | ||
2166 | break; | ||
2167 | } | ||
2168 | break; | ||
2169 | |||
2170 | case WLAN_SETNONE_GETONEINT: | ||
2171 | switch ((int)req->ifr_data) { | ||
2172 | case WLANGETBCNAVG: | ||
2173 | pdata = (int *)wrq->u.name; | ||
2174 | *pdata = (int)adapter->bcn_avg_factor; | ||
2175 | break; | ||
2176 | |||
2177 | case WLANGETREGION: | ||
2178 | pdata = (int *)wrq->u.name; | ||
2179 | *pdata = (int)adapter->regioncode; | ||
2180 | break; | ||
2181 | |||
2182 | case WLAN_GET_LISTEN_INTERVAL: | ||
2183 | pdata = (int *)wrq->u.name; | ||
2184 | *pdata = (int)adapter->listeninterval; | ||
2185 | break; | ||
2186 | |||
2187 | case WLAN_GET_LINKMODE: | ||
2188 | req->ifr_data = (char *)((u32) adapter->linkmode); | ||
2189 | break; | ||
2190 | |||
2191 | case WLAN_GET_RADIOMODE: | ||
2192 | req->ifr_data = (char *)((u32) adapter->radiomode); | ||
2193 | break; | ||
2194 | |||
2195 | case WLAN_GET_DEBUGMODE: | ||
2196 | req->ifr_data = (char *)((u32) adapter->debugmode); | ||
2197 | break; | ||
2198 | |||
2199 | case WLAN_GET_MULTIPLE_DTIM: | ||
2200 | pdata = (int *)wrq->u.name; | ||
2201 | *pdata = (int)adapter->multipledtim; | ||
2202 | break; | ||
2203 | case WLAN_GET_TX_RATE: | ||
2204 | ret = wlan_get_txrate_ioctl(priv, req); | ||
2205 | break; | ||
2206 | case WLAN_SUBCMD_FWT_CLEANUP: /* fwt_cleanup */ | ||
2207 | ret = wlan_fwt_cleanup_ioctl(priv, req); | ||
2208 | break; | ||
2209 | |||
2210 | case WLAN_SUBCMD_FWT_TIME: /* fwt_time */ | ||
2211 | ret = wlan_fwt_time_ioctl(priv, req); | ||
2212 | break; | ||
2213 | |||
2214 | case WLAN_SUBCMD_MESH_GET_TTL: | ||
2215 | ret = wlan_mesh_get_ttl_ioctl(priv, req); | ||
2216 | break; | ||
2217 | |||
2218 | default: | ||
2219 | ret = -EOPNOTSUPP; | ||
2220 | |||
2221 | } | ||
2222 | |||
2223 | break; | ||
2224 | |||
2225 | case WLANGETLOG: | ||
2226 | ret = wlan_do_getlog_ioctl(priv, wrq); | ||
2227 | break; | ||
2228 | |||
2229 | case WLAN_SET_GET_SIXTEEN_INT: | ||
2230 | switch ((int)wrq->u.data.flags) { | ||
2231 | case WLAN_TPCCFG: | ||
2232 | { | ||
2233 | int data[5]; | ||
2234 | struct cmd_ds_802_11_tpc_cfg cfg; | ||
2235 | memset(&cfg, 0, sizeof(cfg)); | ||
2236 | if ((wrq->u.data.length > 1) | ||
2237 | && (wrq->u.data.length != 5)) | ||
2238 | return -1; | ||
2239 | |||
2240 | if (wrq->u.data.length == 0) { | ||
2241 | cfg.action = | ||
2242 | cpu_to_le16 | ||
2243 | (cmd_act_get); | ||
2244 | } else { | ||
2245 | if (copy_from_user | ||
2246 | (data, wrq->u.data.pointer, | ||
2247 | sizeof(int) * 5)) { | ||
2248 | lbs_pr_debug(1, | ||
2249 | "Copy from user failed\n"); | ||
2250 | return -EFAULT; | ||
2251 | } | ||
2252 | |||
2253 | cfg.action = | ||
2254 | cpu_to_le16 | ||
2255 | (cmd_act_set); | ||
2256 | cfg.enable = data[0]; | ||
2257 | cfg.usesnr = data[1]; | ||
2258 | cfg.P0 = data[2]; | ||
2259 | cfg.P1 = data[3]; | ||
2260 | cfg.P2 = data[4]; | ||
2261 | } | ||
2262 | |||
2263 | ret = | ||
2264 | libertas_prepare_and_send_command(priv, | ||
2265 | cmd_802_11_tpc_cfg, | ||
2266 | 0, | ||
2267 | cmd_option_waitforrsp, | ||
2268 | 0, (void *)&cfg); | ||
2269 | |||
2270 | data[0] = cfg.enable; | ||
2271 | data[1] = cfg.usesnr; | ||
2272 | data[2] = cfg.P0; | ||
2273 | data[3] = cfg.P1; | ||
2274 | data[4] = cfg.P2; | ||
2275 | if (copy_to_user | ||
2276 | (wrq->u.data.pointer, data, | ||
2277 | sizeof(int) * 5)) { | ||
2278 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
2279 | return -EFAULT; | ||
2280 | } | ||
2281 | |||
2282 | wrq->u.data.length = 5; | ||
2283 | } | ||
2284 | break; | ||
2285 | |||
2286 | case WLAN_POWERCFG: | ||
2287 | { | ||
2288 | int data[4]; | ||
2289 | struct cmd_ds_802_11_pwr_cfg cfg; | ||
2290 | memset(&cfg, 0, sizeof(cfg)); | ||
2291 | if ((wrq->u.data.length > 1) | ||
2292 | && (wrq->u.data.length != 4)) | ||
2293 | return -1; | ||
2294 | if (wrq->u.data.length == 0) { | ||
2295 | cfg.action = | ||
2296 | cpu_to_le16 | ||
2297 | (cmd_act_get); | ||
2298 | } else { | ||
2299 | if (copy_from_user | ||
2300 | (data, wrq->u.data.pointer, | ||
2301 | sizeof(int) * 4)) { | ||
2302 | lbs_pr_debug(1, | ||
2303 | "Copy from user failed\n"); | ||
2304 | return -EFAULT; | ||
2305 | } | ||
2306 | |||
2307 | cfg.action = | ||
2308 | cpu_to_le16 | ||
2309 | (cmd_act_set); | ||
2310 | cfg.enable = data[0]; | ||
2311 | cfg.PA_P0 = data[1]; | ||
2312 | cfg.PA_P1 = data[2]; | ||
2313 | cfg.PA_P2 = data[3]; | ||
2314 | } | ||
2315 | ret = | ||
2316 | libertas_prepare_and_send_command(priv, | ||
2317 | cmd_802_11_pwr_cfg, | ||
2318 | 0, | ||
2319 | cmd_option_waitforrsp, | ||
2320 | 0, (void *)&cfg); | ||
2321 | data[0] = cfg.enable; | ||
2322 | data[1] = cfg.PA_P0; | ||
2323 | data[2] = cfg.PA_P1; | ||
2324 | data[3] = cfg.PA_P2; | ||
2325 | if (copy_to_user | ||
2326 | (wrq->u.data.pointer, data, | ||
2327 | sizeof(int) * 4)) { | ||
2328 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
2329 | return -EFAULT; | ||
2330 | } | ||
2331 | |||
2332 | wrq->u.data.length = 4; | ||
2333 | } | ||
2334 | break; | ||
2335 | case WLAN_AUTO_FREQ_SET: | ||
2336 | { | ||
2337 | int data[3]; | ||
2338 | struct cmd_ds_802_11_afc afc; | ||
2339 | memset(&afc, 0, sizeof(afc)); | ||
2340 | if (wrq->u.data.length != 3) | ||
2341 | return -1; | ||
2342 | if (copy_from_user | ||
2343 | (data, wrq->u.data.pointer, | ||
2344 | sizeof(int) * 3)) { | ||
2345 | lbs_pr_debug(1, "Copy from user failed\n"); | ||
2346 | return -EFAULT; | ||
2347 | } | ||
2348 | afc.afc_auto = data[0]; | ||
2349 | |||
2350 | if (afc.afc_auto != 0) { | ||
2351 | afc.threshold = data[1]; | ||
2352 | afc.period = data[2]; | ||
2353 | } else { | ||
2354 | afc.timing_offset = data[1]; | ||
2355 | afc.carrier_offset = data[2]; | ||
2356 | } | ||
2357 | ret = | ||
2358 | libertas_prepare_and_send_command(priv, | ||
2359 | cmd_802_11_set_afc, | ||
2360 | 0, | ||
2361 | cmd_option_waitforrsp, | ||
2362 | 0, (void *)&afc); | ||
2363 | } | ||
2364 | break; | ||
2365 | case WLAN_AUTO_FREQ_GET: | ||
2366 | { | ||
2367 | int data[3]; | ||
2368 | struct cmd_ds_802_11_afc afc; | ||
2369 | memset(&afc, 0, sizeof(afc)); | ||
2370 | ret = | ||
2371 | libertas_prepare_and_send_command(priv, | ||
2372 | cmd_802_11_get_afc, | ||
2373 | 0, | ||
2374 | cmd_option_waitforrsp, | ||
2375 | 0, (void *)&afc); | ||
2376 | data[0] = afc.afc_auto; | ||
2377 | data[1] = afc.timing_offset; | ||
2378 | data[2] = afc.carrier_offset; | ||
2379 | if (copy_to_user | ||
2380 | (wrq->u.data.pointer, data, | ||
2381 | sizeof(int) * 3)) { | ||
2382 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
2383 | return -EFAULT; | ||
2384 | } | ||
2385 | |||
2386 | wrq->u.data.length = 3; | ||
2387 | } | ||
2388 | break; | ||
2389 | case WLAN_SCANPROBES: | ||
2390 | { | ||
2391 | int data; | ||
2392 | if (wrq->u.data.length > 0) { | ||
2393 | if (copy_from_user | ||
2394 | (&data, wrq->u.data.pointer, | ||
2395 | sizeof(int))) { | ||
2396 | lbs_pr_debug(1, | ||
2397 | "Copy from user failed\n"); | ||
2398 | return -EFAULT; | ||
2399 | } | ||
2400 | |||
2401 | adapter->scanprobes = data; | ||
2402 | } else { | ||
2403 | data = adapter->scanprobes; | ||
2404 | if (copy_to_user | ||
2405 | (wrq->u.data.pointer, &data, | ||
2406 | sizeof(int))) { | ||
2407 | lbs_pr_debug(1, | ||
2408 | "Copy to user failed\n"); | ||
2409 | return -EFAULT; | ||
2410 | } | ||
2411 | } | ||
2412 | wrq->u.data.length = 1; | ||
2413 | } | ||
2414 | break; | ||
2415 | case WLAN_LED_GPIO_CTRL: | ||
2416 | { | ||
2417 | int i; | ||
2418 | int data[16]; | ||
2419 | |||
2420 | struct cmd_ds_802_11_led_ctrl ctrl; | ||
2421 | struct mrvlietypes_ledgpio *gpio = | ||
2422 | (struct mrvlietypes_ledgpio *) ctrl.data; | ||
2423 | |||
2424 | memset(&ctrl, 0, sizeof(ctrl)); | ||
2425 | if (wrq->u.data.length > MAX_LEDS * 2) | ||
2426 | return -ENOTSUPP; | ||
2427 | if ((wrq->u.data.length % 2) != 0) | ||
2428 | return -ENOTSUPP; | ||
2429 | if (wrq->u.data.length == 0) { | ||
2430 | ctrl.action = | ||
2431 | cpu_to_le16 | ||
2432 | (cmd_act_get); | ||
2433 | } else { | ||
2434 | if (copy_from_user | ||
2435 | (data, wrq->u.data.pointer, | ||
2436 | sizeof(int) * | ||
2437 | wrq->u.data.length)) { | ||
2438 | lbs_pr_debug(1, | ||
2439 | "Copy from user failed\n"); | ||
2440 | return -EFAULT; | ||
2441 | } | ||
2442 | |||
2443 | ctrl.action = | ||
2444 | cpu_to_le16 | ||
2445 | (cmd_act_set); | ||
2446 | ctrl.numled = cpu_to_le16(0); | ||
2447 | gpio->header.type = | ||
2448 | cpu_to_le16(TLV_TYPE_LED_GPIO); | ||
2449 | gpio->header.len = wrq->u.data.length; | ||
2450 | for (i = 0; i < wrq->u.data.length; | ||
2451 | i += 2) { | ||
2452 | gpio->ledpin[i / 2].led = | ||
2453 | data[i]; | ||
2454 | gpio->ledpin[i / 2].pin = | ||
2455 | data[i + 1]; | ||
2456 | } | ||
2457 | } | ||
2458 | ret = | ||
2459 | libertas_prepare_and_send_command(priv, | ||
2460 | cmd_802_11_led_gpio_ctrl, | ||
2461 | 0, | ||
2462 | cmd_option_waitforrsp, | ||
2463 | 0, (void *)&ctrl); | ||
2464 | for (i = 0; i < gpio->header.len; i += 2) { | ||
2465 | data[i] = gpio->ledpin[i / 2].led; | ||
2466 | data[i + 1] = gpio->ledpin[i / 2].pin; | ||
2467 | } | ||
2468 | if (copy_to_user(wrq->u.data.pointer, data, | ||
2469 | sizeof(int) * | ||
2470 | gpio->header.len)) { | ||
2471 | lbs_pr_debug(1, "Copy to user failed\n"); | ||
2472 | return -EFAULT; | ||
2473 | } | ||
2474 | |||
2475 | wrq->u.data.length = gpio->header.len; | ||
2476 | } | ||
2477 | break; | ||
2478 | case WLAN_ADAPT_RATESET: | ||
2479 | ret = wlan_adapt_rateset(priv, wrq); | ||
2480 | break; | ||
2481 | case WLAN_INACTIVITY_TIMEOUT: | ||
2482 | ret = wlan_inactivity_timeout(priv, wrq); | ||
2483 | break; | ||
2484 | case WLANSNR: | ||
2485 | ret = wlan_get_snr(priv, wrq); | ||
2486 | break; | ||
2487 | case WLAN_GET_RXINFO: | ||
2488 | ret = wlan_get_rxinfo(priv, wrq); | ||
2489 | } | ||
2490 | break; | ||
2491 | |||
2492 | default: | ||
2493 | ret = -EINVAL; | ||
2494 | break; | ||
2495 | } | ||
2496 | LEAVE(); | ||
2497 | return ret; | ||
2498 | } | ||
2499 | |||
2500 | |||
diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c new file mode 100644 index 000000000000..11682cbe752b --- /dev/null +++ b/drivers/net/wireless/libertas/join.c | |||
@@ -0,0 +1,1055 @@ | |||
1 | /** | ||
2 | * Functions implementing wlan infrastructure and adhoc join routines, | ||
3 | * IOCTL handlers as well as command preperation and response routines | ||
4 | * for sending adhoc start, adhoc join, and association commands | ||
5 | * to the firmware. | ||
6 | */ | ||
7 | #include <linux/netdevice.h> | ||
8 | #include <linux/if_arp.h> | ||
9 | #include <linux/wireless.h> | ||
10 | |||
11 | #include <net/iw_handler.h> | ||
12 | |||
13 | #include "host.h" | ||
14 | #include "decl.h" | ||
15 | #include "join.h" | ||
16 | #include "dev.h" | ||
17 | |||
18 | /** | ||
19 | * @brief This function finds out the common rates between rate1 and rate2. | ||
20 | * | ||
21 | * It will fill common rates in rate1 as output if found. | ||
22 | * | ||
23 | * NOTE: Setting the MSB of the basic rates need to be taken | ||
24 | * care, either before or after calling this function | ||
25 | * | ||
26 | * @param adapter A pointer to wlan_adapter structure | ||
27 | * @param rate1 the buffer which keeps input and output | ||
28 | * @param rate1_size the size of rate1 buffer | ||
29 | * @param rate2 the buffer which keeps rate2 | ||
30 | * @param rate2_size the size of rate2 buffer. | ||
31 | * | ||
32 | * @return 0 or -1 | ||
33 | */ | ||
34 | static int get_common_rates(wlan_adapter * adapter, u8 * rate1, | ||
35 | int rate1_size, u8 * rate2, int rate2_size) | ||
36 | { | ||
37 | u8 *ptr = rate1; | ||
38 | int ret = 0; | ||
39 | u8 tmp[30]; | ||
40 | int i; | ||
41 | |||
42 | memset(&tmp, 0, sizeof(tmp)); | ||
43 | memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp))); | ||
44 | memset(rate1, 0, rate1_size); | ||
45 | |||
46 | /* Mask the top bit of the original values */ | ||
47 | for (i = 0; tmp[i] && i < sizeof(tmp); i++) | ||
48 | tmp[i] &= 0x7F; | ||
49 | |||
50 | for (i = 0; rate2[i] && i < rate2_size; i++) { | ||
51 | /* Check for Card Rate in tmp, excluding the top bit */ | ||
52 | if (strchr(tmp, rate2[i] & 0x7F)) { | ||
53 | /* values match, so copy the Card Rate to rate1 */ | ||
54 | *rate1++ = rate2[i]; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp)); | ||
59 | lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size); | ||
60 | lbs_dbg_hex("Common rates:", ptr, rate1_size); | ||
61 | lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate); | ||
62 | |||
63 | if (!adapter->is_datarate_auto) { | ||
64 | while (*ptr) { | ||
65 | if ((*ptr & 0x7f) == adapter->datarate) { | ||
66 | ret = 0; | ||
67 | goto done; | ||
68 | } | ||
69 | ptr++; | ||
70 | } | ||
71 | lbs_pr_alert( "Previously set fixed data rate %#x isn't " | ||
72 | "compatible with the network.\n", adapter->datarate); | ||
73 | |||
74 | ret = -1; | ||
75 | goto done; | ||
76 | } | ||
77 | |||
78 | ret = 0; | ||
79 | done: | ||
80 | return ret; | ||
81 | } | ||
82 | |||
83 | int libertas_send_deauth(wlan_private * priv) | ||
84 | { | ||
85 | wlan_adapter *adapter = priv->adapter; | ||
86 | int ret = 0; | ||
87 | |||
88 | if (adapter->inframode == wlan802_11infrastructure && | ||
89 | adapter->connect_status == libertas_connected) | ||
90 | ret = libertas_send_deauthentication(priv); | ||
91 | else | ||
92 | ret = -ENOTSUPP; | ||
93 | |||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | int libertas_do_adhocstop_ioctl(wlan_private * priv) | ||
98 | { | ||
99 | wlan_adapter *adapter = priv->adapter; | ||
100 | int ret = 0; | ||
101 | |||
102 | if (adapter->inframode == wlan802_11ibss && | ||
103 | adapter->connect_status == libertas_connected) | ||
104 | ret = libertas_stop_adhoc_network(priv); | ||
105 | else | ||
106 | ret = -ENOTSUPP; | ||
107 | |||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * @brief Associate to a specific BSS discovered in a scan | ||
113 | * | ||
114 | * @param priv A pointer to wlan_private structure | ||
115 | * @param pbssdesc Pointer to the BSS descriptor to associate with. | ||
116 | * | ||
117 | * @return 0-success, otherwise fail | ||
118 | */ | ||
119 | int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc) | ||
120 | { | ||
121 | wlan_adapter *adapter = priv->adapter; | ||
122 | int ret; | ||
123 | |||
124 | ENTER(); | ||
125 | |||
126 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate, | ||
127 | 0, cmd_option_waitforrsp, | ||
128 | 0, pbssdesc->macaddress); | ||
129 | |||
130 | if (ret) { | ||
131 | LEAVE(); | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | /* set preamble to firmware */ | ||
136 | if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble) | ||
137 | adapter->preamble = cmd_type_short_preamble; | ||
138 | else | ||
139 | adapter->preamble = cmd_type_long_preamble; | ||
140 | |||
141 | libertas_set_radio_control(priv); | ||
142 | |||
143 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate, | ||
144 | 0, cmd_option_waitforrsp, 0, pbssdesc); | ||
145 | |||
146 | LEAVE(); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * @brief Start an Adhoc Network | ||
152 | * | ||
153 | * @param priv A pointer to wlan_private structure | ||
154 | * @param adhocssid The ssid of the Adhoc Network | ||
155 | * @return 0--success, -1--fail | ||
156 | */ | ||
157 | int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid) | ||
158 | { | ||
159 | wlan_adapter *adapter = priv->adapter; | ||
160 | int ret = 0; | ||
161 | |||
162 | adapter->adhoccreate = 1; | ||
163 | |||
164 | if (!adapter->capinfo.shortpreamble) { | ||
165 | lbs_pr_debug(1, "AdhocStart: Long preamble\n"); | ||
166 | adapter->preamble = cmd_type_long_preamble; | ||
167 | } else { | ||
168 | lbs_pr_debug(1, "AdhocStart: Short preamble\n"); | ||
169 | adapter->preamble = cmd_type_short_preamble; | ||
170 | } | ||
171 | |||
172 | libertas_set_radio_control(priv); | ||
173 | |||
174 | lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel); | ||
175 | lbs_pr_debug(1, "curbssparams.channel = %d\n", | ||
176 | adapter->curbssparams.channel); | ||
177 | lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band); | ||
178 | |||
179 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start, | ||
180 | 0, cmd_option_waitforrsp, 0, adhocssid); | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * @brief Join an adhoc network found in a previous scan | ||
187 | * | ||
188 | * @param priv A pointer to wlan_private structure | ||
189 | * @param pbssdesc Pointer to a BSS descriptor found in a previous scan | ||
190 | * to attempt to join | ||
191 | * | ||
192 | * @return 0--success, -1--fail | ||
193 | */ | ||
194 | int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc) | ||
195 | { | ||
196 | wlan_adapter *adapter = priv->adapter; | ||
197 | int ret = 0; | ||
198 | |||
199 | lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n", | ||
200 | adapter->curbssparams.ssid.ssid); | ||
201 | lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n", | ||
202 | adapter->curbssparams.ssid.ssidlength); | ||
203 | lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid); | ||
204 | lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n", | ||
205 | pbssdesc->ssid.ssidlength); | ||
206 | |||
207 | /* check if the requested SSID is already joined */ | ||
208 | if (adapter->curbssparams.ssid.ssidlength | ||
209 | && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid) | ||
210 | && (adapter->curbssparams.bssdescriptor.inframode == | ||
211 | wlan802_11ibss)) { | ||
212 | |||
213 | lbs_pr_debug(1, | ||
214 | "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " | ||
215 | "not attempting to re-join"); | ||
216 | |||
217 | return -1; | ||
218 | } | ||
219 | |||
220 | /*Use shortpreamble only when both creator and card supports | ||
221 | short preamble */ | ||
222 | if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) { | ||
223 | lbs_pr_debug(1, "AdhocJoin: Long preamble\n"); | ||
224 | adapter->preamble = cmd_type_long_preamble; | ||
225 | } else { | ||
226 | lbs_pr_debug(1, "AdhocJoin: Short preamble\n"); | ||
227 | adapter->preamble = cmd_type_short_preamble; | ||
228 | } | ||
229 | |||
230 | libertas_set_radio_control(priv); | ||
231 | |||
232 | lbs_pr_debug(1, "curbssparams.channel = %d\n", | ||
233 | adapter->curbssparams.channel); | ||
234 | lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band); | ||
235 | |||
236 | adapter->adhoccreate = 0; | ||
237 | |||
238 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join, | ||
239 | 0, cmd_option_waitforrsp, | ||
240 | OID_802_11_SSID, pbssdesc); | ||
241 | |||
242 | return ret; | ||
243 | } | ||
244 | |||
245 | int libertas_stop_adhoc_network(wlan_private * priv) | ||
246 | { | ||
247 | return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop, | ||
248 | 0, cmd_option_waitforrsp, 0, NULL); | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * @brief Send Deauthentication Request | ||
253 | * | ||
254 | * @param priv A pointer to wlan_private structure | ||
255 | * @return 0--success, -1--fail | ||
256 | */ | ||
257 | int libertas_send_deauthentication(wlan_private * priv) | ||
258 | { | ||
259 | return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate, | ||
260 | 0, cmd_option_waitforrsp, 0, NULL); | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * @brief Set Idle Off | ||
265 | * | ||
266 | * @param priv A pointer to wlan_private structure | ||
267 | * @return 0 --success, otherwise fail | ||
268 | */ | ||
269 | int libertas_idle_off(wlan_private * priv) | ||
270 | { | ||
271 | wlan_adapter *adapter = priv->adapter; | ||
272 | int ret = 0; | ||
273 | const u8 zeromac[] = { 0, 0, 0, 0, 0, 0 }; | ||
274 | int i; | ||
275 | |||
276 | ENTER(); | ||
277 | |||
278 | if (adapter->connect_status == libertas_disconnected) { | ||
279 | if (adapter->inframode == wlan802_11infrastructure) { | ||
280 | if (memcmp(adapter->previousbssid, zeromac, | ||
281 | sizeof(zeromac)) != 0) { | ||
282 | |||
283 | lbs_pr_debug(1, "Previous SSID = %s\n", | ||
284 | adapter->previousssid.ssid); | ||
285 | lbs_pr_debug(1, "Previous BSSID = " | ||
286 | "%02x:%02x:%02x:%02x:%02x:%02x:\n", | ||
287 | adapter->previousbssid[0], | ||
288 | adapter->previousbssid[1], | ||
289 | adapter->previousbssid[2], | ||
290 | adapter->previousbssid[3], | ||
291 | adapter->previousbssid[4], | ||
292 | adapter->previousbssid[5]); | ||
293 | |||
294 | i = libertas_find_SSID_in_list(adapter, | ||
295 | &adapter->previousssid, | ||
296 | adapter->previousbssid, | ||
297 | adapter->inframode); | ||
298 | |||
299 | if (i < 0) { | ||
300 | libertas_send_specific_BSSID_scan(priv, | ||
301 | adapter-> | ||
302 | previousbssid, | ||
303 | 1); | ||
304 | i = libertas_find_SSID_in_list(adapter, | ||
305 | &adapter-> | ||
306 | previousssid, | ||
307 | adapter-> | ||
308 | previousbssid, | ||
309 | adapter-> | ||
310 | inframode); | ||
311 | } | ||
312 | |||
313 | if (i < 0) { | ||
314 | /* If the BSSID could not be found, try just the SSID */ | ||
315 | i = libertas_find_SSID_in_list(adapter, | ||
316 | &adapter-> | ||
317 | previousssid, NULL, | ||
318 | adapter-> | ||
319 | inframode); | ||
320 | } | ||
321 | |||
322 | if (i < 0) { | ||
323 | libertas_send_specific_SSID_scan(priv, | ||
324 | &adapter-> | ||
325 | previousssid, | ||
326 | 1); | ||
327 | i = libertas_find_SSID_in_list(adapter, | ||
328 | &adapter-> | ||
329 | previousssid, NULL, | ||
330 | adapter-> | ||
331 | inframode); | ||
332 | } | ||
333 | |||
334 | if (i >= 0) { | ||
335 | ret = | ||
336 | wlan_associate(priv, | ||
337 | &adapter-> | ||
338 | scantable[i]); | ||
339 | } | ||
340 | } | ||
341 | } else if (adapter->inframode == wlan802_11ibss) { | ||
342 | ret = libertas_prepare_and_send_command(priv, | ||
343 | cmd_802_11_ad_hoc_start, | ||
344 | 0, | ||
345 | cmd_option_waitforrsp, | ||
346 | 0, &adapter->previousssid); | ||
347 | } | ||
348 | } | ||
349 | /* else it is connected */ | ||
350 | |||
351 | lbs_pr_debug(1, "\nwlanidle is off"); | ||
352 | LEAVE(); | ||
353 | return ret; | ||
354 | } | ||
355 | |||
356 | /** | ||
357 | * @brief Set Idle On | ||
358 | * | ||
359 | * @param priv A pointer to wlan_private structure | ||
360 | * @return 0 --success, otherwise fail | ||
361 | */ | ||
362 | int libertas_idle_on(wlan_private * priv) | ||
363 | { | ||
364 | wlan_adapter *adapter = priv->adapter; | ||
365 | int ret = 0; | ||
366 | |||
367 | if (adapter->connect_status == libertas_connected) { | ||
368 | if (adapter->inframode == wlan802_11infrastructure) { | ||
369 | lbs_pr_debug(1, "Previous SSID = %s\n", | ||
370 | adapter->previousssid.ssid); | ||
371 | memmove(&adapter->previousssid, | ||
372 | &adapter->curbssparams.ssid, | ||
373 | sizeof(struct WLAN_802_11_SSID)); | ||
374 | libertas_send_deauth(priv); | ||
375 | |||
376 | } else if (adapter->inframode == wlan802_11ibss) { | ||
377 | ret = libertas_stop_adhoc_network(priv); | ||
378 | } | ||
379 | |||
380 | } | ||
381 | |||
382 | lbs_pr_debug(1, "\nwlanidle is on"); | ||
383 | |||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * @brief This function prepares command of authenticate. | ||
389 | * | ||
390 | * @param priv A pointer to wlan_private structure | ||
391 | * @param cmd A pointer to cmd_ds_command structure | ||
392 | * @param pdata_buf Void cast of pointer to a BSSID to authenticate with | ||
393 | * | ||
394 | * @return 0 or -1 | ||
395 | */ | ||
396 | int libertas_cmd_80211_authenticate(wlan_private * priv, | ||
397 | struct cmd_ds_command *cmd, | ||
398 | void *pdata_buf) | ||
399 | { | ||
400 | wlan_adapter *adapter = priv->adapter; | ||
401 | struct cmd_ds_802_11_authenticate *pauthenticate = | ||
402 | &cmd->params.auth; | ||
403 | u8 *bssid = pdata_buf; | ||
404 | |||
405 | cmd->command = cpu_to_le16(cmd_802_11_authenticate); | ||
406 | cmd->size = | ||
407 | cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) | ||
408 | + S_DS_GEN); | ||
409 | |||
410 | pauthenticate->authtype = adapter->secinfo.authmode; | ||
411 | memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); | ||
412 | |||
413 | lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n", | ||
414 | bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | int libertas_cmd_80211_deauthenticate(wlan_private * priv, | ||
420 | struct cmd_ds_command *cmd) | ||
421 | { | ||
422 | wlan_adapter *adapter = priv->adapter; | ||
423 | struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; | ||
424 | |||
425 | ENTER(); | ||
426 | |||
427 | cmd->command = cpu_to_le16(cmd_802_11_deauthenticate); | ||
428 | cmd->size = | ||
429 | cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + | ||
430 | S_DS_GEN); | ||
431 | |||
432 | /* set AP MAC address */ | ||
433 | memmove(dauth->macaddr, adapter->curbssparams.bssid, | ||
434 | ETH_ALEN); | ||
435 | |||
436 | /* Reason code 3 = Station is leaving */ | ||
437 | #define REASON_CODE_STA_LEAVING 3 | ||
438 | dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); | ||
439 | |||
440 | LEAVE(); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | int libertas_cmd_80211_associate(wlan_private * priv, | ||
445 | struct cmd_ds_command *cmd, void *pdata_buf) | ||
446 | { | ||
447 | wlan_adapter *adapter = priv->adapter; | ||
448 | struct cmd_ds_802_11_associate *passo = &cmd->params.associate; | ||
449 | int ret = 0; | ||
450 | struct bss_descriptor *pbssdesc; | ||
451 | u8 *card_rates; | ||
452 | u8 *pos; | ||
453 | int card_rates_size; | ||
454 | u16 tmpcap; | ||
455 | struct mrvlietypes_ssidparamset *ssid; | ||
456 | struct mrvlietypes_phyparamset *phy; | ||
457 | struct mrvlietypes_ssparamset *ss; | ||
458 | struct mrvlietypes_ratesparamset *rates; | ||
459 | struct mrvlietypes_rsnparamset *rsn; | ||
460 | |||
461 | ENTER(); | ||
462 | |||
463 | pbssdesc = pdata_buf; | ||
464 | pos = (u8 *) passo; | ||
465 | |||
466 | if (!adapter) { | ||
467 | ret = -1; | ||
468 | goto done; | ||
469 | } | ||
470 | |||
471 | cmd->command = cpu_to_le16(cmd_802_11_associate); | ||
472 | |||
473 | /* Save so we know which BSS Desc to use in the response handler */ | ||
474 | adapter->pattemptedbssdesc = pbssdesc; | ||
475 | |||
476 | memcpy(passo->peerstaaddr, | ||
477 | pbssdesc->macaddress, sizeof(passo->peerstaaddr)); | ||
478 | pos += sizeof(passo->peerstaaddr); | ||
479 | |||
480 | /* set the listen interval */ | ||
481 | passo->listeninterval = adapter->listeninterval; | ||
482 | |||
483 | pos += sizeof(passo->capinfo); | ||
484 | pos += sizeof(passo->listeninterval); | ||
485 | pos += sizeof(passo->bcnperiod); | ||
486 | pos += sizeof(passo->dtimperiod); | ||
487 | |||
488 | ssid = (struct mrvlietypes_ssidparamset *) pos; | ||
489 | ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); | ||
490 | ssid->header.len = pbssdesc->ssid.ssidlength; | ||
491 | memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len); | ||
492 | pos += sizeof(ssid->header) + ssid->header.len; | ||
493 | ssid->header.len = cpu_to_le16(ssid->header.len); | ||
494 | |||
495 | phy = (struct mrvlietypes_phyparamset *) pos; | ||
496 | phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); | ||
497 | phy->header.len = sizeof(phy->fh_ds.dsparamset); | ||
498 | memcpy(&phy->fh_ds.dsparamset, | ||
499 | &pbssdesc->phyparamset.dsparamset.currentchan, | ||
500 | sizeof(phy->fh_ds.dsparamset)); | ||
501 | pos += sizeof(phy->header) + phy->header.len; | ||
502 | phy->header.len = cpu_to_le16(phy->header.len); | ||
503 | |||
504 | ss = (struct mrvlietypes_ssparamset *) pos; | ||
505 | ss->header.type = cpu_to_le16(TLV_TYPE_CF); | ||
506 | ss->header.len = sizeof(ss->cf_ibss.cfparamset); | ||
507 | pos += sizeof(ss->header) + ss->header.len; | ||
508 | ss->header.len = cpu_to_le16(ss->header.len); | ||
509 | |||
510 | rates = (struct mrvlietypes_ratesparamset *) pos; | ||
511 | rates->header.type = cpu_to_le16(TLV_TYPE_RATES); | ||
512 | |||
513 | memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES); | ||
514 | |||
515 | card_rates = libertas_supported_rates; | ||
516 | card_rates_size = sizeof(libertas_supported_rates); | ||
517 | |||
518 | if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES, | ||
519 | card_rates, card_rates_size)) { | ||
520 | ret = -1; | ||
521 | goto done; | ||
522 | } | ||
523 | |||
524 | rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES); | ||
525 | adapter->curbssparams.numofrates = rates->header.len; | ||
526 | |||
527 | pos += sizeof(rates->header) + rates->header.len; | ||
528 | rates->header.len = cpu_to_le16(rates->header.len); | ||
529 | |||
530 | if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { | ||
531 | rsn = (struct mrvlietypes_rsnparamset *) pos; | ||
532 | rsn->header.type = (u16) adapter->wpa_ie[0]; /* WPA_IE or WPA2_IE */ | ||
533 | rsn->header.type = cpu_to_le16(rsn->header.type); | ||
534 | rsn->header.len = (u16) adapter->wpa_ie[1]; | ||
535 | memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len); | ||
536 | lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn, | ||
537 | sizeof(rsn->header) + rsn->header.len); | ||
538 | pos += sizeof(rsn->header) + rsn->header.len; | ||
539 | rsn->header.len = cpu_to_le16(rsn->header.len); | ||
540 | } | ||
541 | |||
542 | /* update curbssparams */ | ||
543 | adapter->curbssparams.channel = | ||
544 | (pbssdesc->phyparamset.dsparamset.currentchan); | ||
545 | |||
546 | /* Copy the infra. association rates into Current BSS state structure */ | ||
547 | memcpy(&adapter->curbssparams.datarates, &rates->rates, | ||
548 | min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len)); | ||
549 | |||
550 | lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len); | ||
551 | |||
552 | /* set IBSS field */ | ||
553 | if (pbssdesc->inframode == wlan802_11infrastructure) { | ||
554 | #define CAPINFO_ESS_MODE 1 | ||
555 | passo->capinfo.ess = CAPINFO_ESS_MODE; | ||
556 | } | ||
557 | |||
558 | if (libertas_parse_dnld_countryinfo_11d(priv)) { | ||
559 | ret = -1; | ||
560 | goto done; | ||
561 | } | ||
562 | |||
563 | cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); | ||
564 | |||
565 | /* set the capability info at last */ | ||
566 | memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo)); | ||
567 | tmpcap &= CAPINFO_MASK; | ||
568 | lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", | ||
569 | tmpcap, CAPINFO_MASK); | ||
570 | tmpcap = cpu_to_le16(tmpcap); | ||
571 | memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo)); | ||
572 | |||
573 | done: | ||
574 | LEAVE(); | ||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, | ||
579 | struct cmd_ds_command *cmd, void *pssid) | ||
580 | { | ||
581 | wlan_adapter *adapter = priv->adapter; | ||
582 | struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; | ||
583 | int ret = 0; | ||
584 | int cmdappendsize = 0; | ||
585 | int i; | ||
586 | u16 tmpcap; | ||
587 | struct bss_descriptor *pbssdesc; | ||
588 | struct WLAN_802_11_SSID *ssid = pssid; | ||
589 | |||
590 | ENTER(); | ||
591 | |||
592 | if (!adapter) { | ||
593 | ret = -1; | ||
594 | goto done; | ||
595 | } | ||
596 | |||
597 | cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start); | ||
598 | |||
599 | pbssdesc = &adapter->curbssparams.bssdescriptor; | ||
600 | adapter->pattemptedbssdesc = pbssdesc; | ||
601 | |||
602 | /* | ||
603 | * Fill in the parameters for 2 data structures: | ||
604 | * 1. cmd_ds_802_11_ad_hoc_start command | ||
605 | * 2. adapter->scantable[i] | ||
606 | * | ||
607 | * Driver will fill up SSID, bsstype,IBSS param, Physical Param, | ||
608 | * probe delay, and cap info. | ||
609 | * | ||
610 | * Firmware will fill up beacon period, DTIM, Basic rates | ||
611 | * and operational rates. | ||
612 | */ | ||
613 | |||
614 | memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE); | ||
615 | |||
616 | memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength); | ||
617 | |||
618 | lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID); | ||
619 | |||
620 | memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE); | ||
621 | memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength); | ||
622 | |||
623 | pbssdesc->ssid.ssidlength = ssid->ssidlength; | ||
624 | |||
625 | /* set the BSS type */ | ||
626 | adhs->bsstype = cmd_bss_type_ibss; | ||
627 | pbssdesc->inframode = wlan802_11ibss; | ||
628 | adhs->beaconperiod = adapter->beaconperiod; | ||
629 | |||
630 | /* set Physical param set */ | ||
631 | #define DS_PARA_IE_ID 3 | ||
632 | #define DS_PARA_IE_LEN 1 | ||
633 | |||
634 | adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; | ||
635 | adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; | ||
636 | |||
637 | WARN_ON(!adapter->adhocchannel); | ||
638 | |||
639 | lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n", | ||
640 | adapter->adhocchannel); | ||
641 | |||
642 | adapter->curbssparams.channel = adapter->adhocchannel; | ||
643 | |||
644 | pbssdesc->channel = adapter->adhocchannel; | ||
645 | adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel; | ||
646 | |||
647 | memcpy(&pbssdesc->phyparamset, | ||
648 | &adhs->phyparamset, sizeof(union ieeetypes_phyparamset)); | ||
649 | |||
650 | /* set IBSS param set */ | ||
651 | #define IBSS_PARA_IE_ID 6 | ||
652 | #define IBSS_PARA_IE_LEN 2 | ||
653 | |||
654 | adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; | ||
655 | adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; | ||
656 | adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow; | ||
657 | memcpy(&pbssdesc->ssparamset, | ||
658 | &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset)); | ||
659 | |||
660 | /* set capability info */ | ||
661 | adhs->cap.ess = 0; | ||
662 | adhs->cap.ibss = 1; | ||
663 | pbssdesc->cap.ibss = 1; | ||
664 | |||
665 | /* probedelay */ | ||
666 | adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time); | ||
667 | |||
668 | /* set up privacy in adapter->scantable[i] */ | ||
669 | if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { | ||
670 | |||
671 | #define AD_HOC_CAP_PRIVACY_ON 1 | ||
672 | lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus set, privacy to WEP\n"); | ||
673 | pbssdesc->privacy = wlan802_11privfilter8021xWEP; | ||
674 | adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON; | ||
675 | } else { | ||
676 | lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus NOT set, Setting " | ||
677 | "privacy to ACCEPT ALL\n"); | ||
678 | pbssdesc->privacy = wlan802_11privfilteracceptall; | ||
679 | } | ||
680 | |||
681 | memset(adhs->datarate, 0, sizeof(adhs->datarate)); | ||
682 | |||
683 | if (adapter->adhoc_grate_enabled) { | ||
684 | memcpy(adhs->datarate, libertas_adhoc_rates_g, | ||
685 | min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g))); | ||
686 | } else { | ||
687 | memcpy(adhs->datarate, libertas_adhoc_rates_b, | ||
688 | min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b))); | ||
689 | } | ||
690 | |||
691 | /* Find the last non zero */ | ||
692 | for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ; | ||
693 | |||
694 | adapter->curbssparams.numofrates = i; | ||
695 | |||
696 | /* Copy the ad-hoc creating rates into Current BSS state structure */ | ||
697 | memcpy(&adapter->curbssparams.datarates, | ||
698 | &adhs->datarate, adapter->curbssparams.numofrates); | ||
699 | |||
700 | lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", | ||
701 | adhs->datarate[0], adhs->datarate[1], | ||
702 | adhs->datarate[2], adhs->datarate[3]); | ||
703 | |||
704 | lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n"); | ||
705 | |||
706 | if (libertas_create_dnld_countryinfo_11d(priv)) { | ||
707 | lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); | ||
708 | ret = -1; | ||
709 | goto done; | ||
710 | } | ||
711 | |||
712 | cmd->size = | ||
713 | cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) | ||
714 | + S_DS_GEN + cmdappendsize); | ||
715 | |||
716 | memcpy(&tmpcap, &adhs->cap, sizeof(u16)); | ||
717 | tmpcap = cpu_to_le16(tmpcap); | ||
718 | memcpy(&adhs->cap, &tmpcap, sizeof(u16)); | ||
719 | |||
720 | ret = 0; | ||
721 | done: | ||
722 | LEAVE(); | ||
723 | return ret; | ||
724 | } | ||
725 | |||
726 | int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, | ||
727 | struct cmd_ds_command *cmd) | ||
728 | { | ||
729 | cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop); | ||
730 | cmd->size = cpu_to_le16(S_DS_GEN); | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, | ||
736 | struct cmd_ds_command *cmd, void *pdata_buf) | ||
737 | { | ||
738 | wlan_adapter *adapter = priv->adapter; | ||
739 | struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj; | ||
740 | struct bss_descriptor *pbssdesc = pdata_buf; | ||
741 | int cmdappendsize = 0; | ||
742 | int ret = 0; | ||
743 | u8 *card_rates; | ||
744 | int card_rates_size; | ||
745 | u16 tmpcap; | ||
746 | int i; | ||
747 | |||
748 | ENTER(); | ||
749 | |||
750 | adapter->pattemptedbssdesc = pbssdesc; | ||
751 | |||
752 | cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join); | ||
753 | |||
754 | padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss; | ||
755 | |||
756 | padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod; | ||
757 | |||
758 | memcpy(&padhocjoin->bssdescriptor.BSSID, | ||
759 | &pbssdesc->macaddress, ETH_ALEN); | ||
760 | |||
761 | memcpy(&padhocjoin->bssdescriptor.SSID, | ||
762 | &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength); | ||
763 | |||
764 | memcpy(&padhocjoin->bssdescriptor.phyparamset, | ||
765 | &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset)); | ||
766 | |||
767 | memcpy(&padhocjoin->bssdescriptor.ssparamset, | ||
768 | &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset)); | ||
769 | |||
770 | memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo)); | ||
771 | tmpcap &= CAPINFO_MASK; | ||
772 | |||
773 | lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", | ||
774 | tmpcap, CAPINFO_MASK); | ||
775 | memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap, | ||
776 | sizeof(struct ieeetypes_capinfo)); | ||
777 | |||
778 | /* information on BSSID descriptor passed to FW */ | ||
779 | lbs_pr_debug(1, | ||
780 | "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n", | ||
781 | padhocjoin->bssdescriptor.BSSID[0], | ||
782 | padhocjoin->bssdescriptor.BSSID[1], | ||
783 | padhocjoin->bssdescriptor.BSSID[2], | ||
784 | padhocjoin->bssdescriptor.BSSID[3], | ||
785 | padhocjoin->bssdescriptor.BSSID[4], | ||
786 | padhocjoin->bssdescriptor.BSSID[5], | ||
787 | padhocjoin->bssdescriptor.SSID); | ||
788 | |||
789 | lbs_pr_debug(1, "ADHOC_J_CMD: Data Rate = %x\n", | ||
790 | (u32) padhocjoin->bssdescriptor.datarates); | ||
791 | |||
792 | /* failtimeout */ | ||
793 | padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); | ||
794 | |||
795 | /* probedelay */ | ||
796 | padhocjoin->probedelay = | ||
797 | cpu_to_le16(cmd_scan_probe_delay_time); | ||
798 | |||
799 | /* Copy Data rates from the rates recorded in scan response */ | ||
800 | memset(padhocjoin->bssdescriptor.datarates, 0, | ||
801 | sizeof(padhocjoin->bssdescriptor.datarates)); | ||
802 | memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates, | ||
803 | min(sizeof(padhocjoin->bssdescriptor.datarates), | ||
804 | sizeof(pbssdesc->datarates))); | ||
805 | |||
806 | card_rates = libertas_supported_rates; | ||
807 | card_rates_size = sizeof(libertas_supported_rates); | ||
808 | |||
809 | adapter->curbssparams.channel = pbssdesc->channel; | ||
810 | |||
811 | if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates, | ||
812 | sizeof(padhocjoin->bssdescriptor.datarates), | ||
813 | card_rates, card_rates_size)) { | ||
814 | lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n"); | ||
815 | ret = -1; | ||
816 | goto done; | ||
817 | } | ||
818 | |||
819 | /* Find the last non zero */ | ||
820 | for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates) | ||
821 | && padhocjoin->bssdescriptor.datarates[i]; i++) ; | ||
822 | |||
823 | adapter->curbssparams.numofrates = i; | ||
824 | |||
825 | /* | ||
826 | * Copy the adhoc joining rates to Current BSS State structure | ||
827 | */ | ||
828 | memcpy(adapter->curbssparams.datarates, | ||
829 | padhocjoin->bssdescriptor.datarates, | ||
830 | adapter->curbssparams.numofrates); | ||
831 | |||
832 | padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow = | ||
833 | cpu_to_le16(pbssdesc->atimwindow); | ||
834 | |||
835 | if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { | ||
836 | padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; | ||
837 | } | ||
838 | |||
839 | if (adapter->psmode == wlan802_11powermodemax_psp) { | ||
840 | /* wake up first */ | ||
841 | enum WLAN_802_11_POWER_MODE Localpsmode; | ||
842 | |||
843 | Localpsmode = wlan802_11powermodecam; | ||
844 | ret = libertas_prepare_and_send_command(priv, | ||
845 | cmd_802_11_ps_mode, | ||
846 | cmd_act_set, | ||
847 | 0, 0, &Localpsmode); | ||
848 | |||
849 | if (ret) { | ||
850 | ret = -1; | ||
851 | goto done; | ||
852 | } | ||
853 | } | ||
854 | |||
855 | if (libertas_parse_dnld_countryinfo_11d(priv)) { | ||
856 | ret = -1; | ||
857 | goto done; | ||
858 | } | ||
859 | |||
860 | cmd->size = | ||
861 | cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) | ||
862 | + S_DS_GEN + cmdappendsize); | ||
863 | |||
864 | memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap, | ||
865 | sizeof(struct ieeetypes_capinfo)); | ||
866 | tmpcap = cpu_to_le16(tmpcap); | ||
867 | |||
868 | memcpy(&padhocjoin->bssdescriptor.cap, | ||
869 | &tmpcap, sizeof(struct ieeetypes_capinfo)); | ||
870 | |||
871 | done: | ||
872 | LEAVE(); | ||
873 | return ret; | ||
874 | } | ||
875 | |||
876 | int libertas_ret_80211_associate(wlan_private * priv, | ||
877 | struct cmd_ds_command *resp) | ||
878 | { | ||
879 | wlan_adapter *adapter = priv->adapter; | ||
880 | int ret = 0; | ||
881 | union iwreq_data wrqu; | ||
882 | struct ieeetypes_assocrsp *passocrsp; | ||
883 | struct bss_descriptor *pbssdesc; | ||
884 | |||
885 | ENTER(); | ||
886 | |||
887 | passocrsp = (struct ieeetypes_assocrsp *) & resp->params; | ||
888 | |||
889 | if (passocrsp->statuscode) { | ||
890 | |||
891 | libertas_mac_event_disconnected(priv); | ||
892 | |||
893 | lbs_pr_debug(1, | ||
894 | "ASSOC_RESP: Association failed, status code = %d\n", | ||
895 | passocrsp->statuscode); | ||
896 | |||
897 | ret = -1; | ||
898 | goto done; | ||
899 | } | ||
900 | |||
901 | lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params, | ||
902 | le16_to_cpu(resp->size) - S_DS_GEN); | ||
903 | |||
904 | /* Send a Media Connected event, according to the Spec */ | ||
905 | adapter->connect_status = libertas_connected; | ||
906 | |||
907 | /* Set the attempted BSSID Index to current */ | ||
908 | pbssdesc = adapter->pattemptedbssdesc; | ||
909 | |||
910 | lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid); | ||
911 | |||
912 | /* Set the new SSID to current SSID */ | ||
913 | memcpy(&adapter->curbssparams.ssid, | ||
914 | &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); | ||
915 | |||
916 | /* Set the new BSSID (AP's MAC address) to current BSSID */ | ||
917 | memcpy(adapter->curbssparams.bssid, | ||
918 | pbssdesc->macaddress, ETH_ALEN); | ||
919 | |||
920 | /* Make a copy of current BSSID descriptor */ | ||
921 | memcpy(&adapter->curbssparams.bssdescriptor, | ||
922 | pbssdesc, sizeof(struct bss_descriptor)); | ||
923 | |||
924 | lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n", | ||
925 | adapter->currentpacketfilter); | ||
926 | |||
927 | adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0; | ||
928 | adapter->NF[TYPE_RXPD][TYPE_AVG] = 0; | ||
929 | |||
930 | memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); | ||
931 | memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); | ||
932 | adapter->nextSNRNF = 0; | ||
933 | adapter->numSNRNF = 0; | ||
934 | |||
935 | netif_carrier_on(priv->wlan_dev.netdev); | ||
936 | netif_wake_queue(priv->wlan_dev.netdev); | ||
937 | |||
938 | lbs_pr_debug(1, "ASSOC_RESP: Associated \n"); | ||
939 | |||
940 | memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); | ||
941 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
942 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); | ||
943 | |||
944 | done: | ||
945 | LEAVE(); | ||
946 | return ret; | ||
947 | } | ||
948 | |||
949 | int libertas_ret_80211_disassociate(wlan_private * priv, | ||
950 | struct cmd_ds_command *resp) | ||
951 | { | ||
952 | ENTER(); | ||
953 | |||
954 | libertas_mac_event_disconnected(priv); | ||
955 | |||
956 | LEAVE(); | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | int libertas_ret_80211_ad_hoc_start(wlan_private * priv, | ||
961 | struct cmd_ds_command *resp) | ||
962 | { | ||
963 | wlan_adapter *adapter = priv->adapter; | ||
964 | int ret = 0; | ||
965 | u16 command = le16_to_cpu(resp->command); | ||
966 | u16 result = le16_to_cpu(resp->result); | ||
967 | struct cmd_ds_802_11_ad_hoc_result *padhocresult; | ||
968 | union iwreq_data wrqu; | ||
969 | struct bss_descriptor *pbssdesc; | ||
970 | |||
971 | ENTER(); | ||
972 | |||
973 | padhocresult = &resp->params.result; | ||
974 | |||
975 | lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size)); | ||
976 | lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command); | ||
977 | lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result); | ||
978 | |||
979 | pbssdesc = adapter->pattemptedbssdesc; | ||
980 | |||
981 | /* | ||
982 | * Join result code 0 --> SUCCESS | ||
983 | */ | ||
984 | if (result) { | ||
985 | lbs_pr_debug(1, "ADHOC_RESP failed\n"); | ||
986 | if (adapter->connect_status == libertas_connected) { | ||
987 | libertas_mac_event_disconnected(priv); | ||
988 | } | ||
989 | |||
990 | memset(&adapter->curbssparams.bssdescriptor, | ||
991 | 0x00, sizeof(adapter->curbssparams.bssdescriptor)); | ||
992 | |||
993 | LEAVE(); | ||
994 | return -1; | ||
995 | } | ||
996 | |||
997 | /* | ||
998 | * Now the join cmd should be successful | ||
999 | * If BSSID has changed use SSID to compare instead of BSSID | ||
1000 | */ | ||
1001 | lbs_pr_debug(1, "ADHOC_J_RESP %s\n", pbssdesc->ssid.ssid); | ||
1002 | |||
1003 | /* Send a Media Connected event, according to the Spec */ | ||
1004 | adapter->connect_status = libertas_connected; | ||
1005 | |||
1006 | if (command == cmd_ret_802_11_ad_hoc_start) { | ||
1007 | /* Update the created network descriptor with the new BSSID */ | ||
1008 | memcpy(pbssdesc->macaddress, | ||
1009 | padhocresult->BSSID, ETH_ALEN); | ||
1010 | } else { | ||
1011 | |||
1012 | /* Make a copy of current BSSID descriptor, only needed for join since | ||
1013 | * the current descriptor is already being used for adhoc start | ||
1014 | */ | ||
1015 | memmove(&adapter->curbssparams.bssdescriptor, | ||
1016 | pbssdesc, sizeof(struct bss_descriptor)); | ||
1017 | } | ||
1018 | |||
1019 | /* Set the BSSID from the joined/started descriptor */ | ||
1020 | memcpy(&adapter->curbssparams.bssid, | ||
1021 | pbssdesc->macaddress, ETH_ALEN); | ||
1022 | |||
1023 | /* Set the new SSID to current SSID */ | ||
1024 | memcpy(&adapter->curbssparams.ssid, | ||
1025 | &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); | ||
1026 | |||
1027 | netif_carrier_on(priv->wlan_dev.netdev); | ||
1028 | netif_wake_queue(priv->wlan_dev.netdev); | ||
1029 | |||
1030 | memset(&wrqu, 0, sizeof(wrqu)); | ||
1031 | memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); | ||
1032 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
1033 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); | ||
1034 | |||
1035 | lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n"); | ||
1036 | lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel); | ||
1037 | lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
1038 | padhocresult->BSSID[0], padhocresult->BSSID[1], | ||
1039 | padhocresult->BSSID[2], padhocresult->BSSID[3], | ||
1040 | padhocresult->BSSID[4], padhocresult->BSSID[5]); | ||
1041 | |||
1042 | LEAVE(); | ||
1043 | return ret; | ||
1044 | } | ||
1045 | |||
1046 | int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, | ||
1047 | struct cmd_ds_command *resp) | ||
1048 | { | ||
1049 | ENTER(); | ||
1050 | |||
1051 | libertas_mac_event_disconnected(priv); | ||
1052 | |||
1053 | LEAVE(); | ||
1054 | return 0; | ||
1055 | } | ||
diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h new file mode 100644 index 000000000000..8efa2455af9a --- /dev/null +++ b/drivers/net/wireless/libertas/join.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ | ||
2 | /* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ | ||
3 | |||
4 | /** | ||
5 | * Interface for the wlan infrastructure and adhoc join routines | ||
6 | * | ||
7 | * Driver interface functions and type declarations for the join module | ||
8 | * implemented in wlan_join.c. Process all start/join requests for | ||
9 | * both adhoc and infrastructure networks | ||
10 | */ | ||
11 | #ifndef _WLAN_JOIN_H | ||
12 | #define _WLAN_JOIN_H | ||
13 | |||
14 | #include "defs.h" | ||
15 | |||
16 | struct cmd_ds_command; | ||
17 | extern int libertas_cmd_80211_authenticate(wlan_private * priv, | ||
18 | struct cmd_ds_command *cmd, | ||
19 | void *pdata_buf); | ||
20 | extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, | ||
21 | struct cmd_ds_command *cmd, | ||
22 | void *pdata_buf); | ||
23 | extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, | ||
24 | struct cmd_ds_command *cmd); | ||
25 | extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, | ||
26 | struct cmd_ds_command *cmd, | ||
27 | void *pssid); | ||
28 | extern int libertas_cmd_80211_deauthenticate(wlan_private * priv, | ||
29 | struct cmd_ds_command *cmd); | ||
30 | extern int libertas_cmd_80211_associate(wlan_private * priv, | ||
31 | struct cmd_ds_command *cmd, | ||
32 | void *pdata_buf); | ||
33 | |||
34 | extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv, | ||
35 | struct cmd_ds_command *resp); | ||
36 | extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, | ||
37 | struct cmd_ds_command *resp); | ||
38 | extern int libertas_ret_80211_disassociate(wlan_private * priv, | ||
39 | struct cmd_ds_command *resp); | ||
40 | extern int libertas_ret_80211_associate(wlan_private * priv, | ||
41 | struct cmd_ds_command *resp); | ||
42 | |||
43 | extern int libertas_idle_on(wlan_private * priv); | ||
44 | extern int libertas_idle_off(wlan_private * priv); | ||
45 | |||
46 | extern int libertas_do_adhocstop_ioctl(wlan_private * priv); | ||
47 | extern int libertas_reassociation_thread(void *data); | ||
48 | |||
49 | struct WLAN_802_11_SSID; | ||
50 | struct bss_descriptor; | ||
51 | |||
52 | extern int libertas_start_adhoc_network(wlan_private * priv, | ||
53 | struct WLAN_802_11_SSID *adhocssid); | ||
54 | extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc); | ||
55 | extern int libertas_stop_adhoc_network(wlan_private * priv); | ||
56 | |||
57 | extern int libertas_send_deauthentication(wlan_private * priv); | ||
58 | extern int libertas_send_deauth(wlan_private * priv); | ||
59 | |||
60 | extern int libertas_do_adhocstop_ioctl(wlan_private * priv); | ||
61 | |||
62 | int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc); | ||
63 | |||
64 | #endif | ||
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c new file mode 100644 index 000000000000..497046530117 --- /dev/null +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -0,0 +1,1258 @@ | |||
1 | /** | ||
2 | * This file contains the major functions in WLAN | ||
3 | * driver. It includes init, exit, open, close and main | ||
4 | * thread etc.. | ||
5 | */ | ||
6 | |||
7 | #include <linux/delay.h> | ||
8 | #include <linux/freezer.h> | ||
9 | #include <linux/etherdevice.h> | ||
10 | #include <linux/netdevice.h> | ||
11 | #include <linux/if_arp.h> | ||
12 | |||
13 | #include <net/iw_handler.h> | ||
14 | |||
15 | #include "host.h" | ||
16 | #include "sbi.h" | ||
17 | #include "decl.h" | ||
18 | #include "dev.h" | ||
19 | #include "fw.h" | ||
20 | #include "wext.h" | ||
21 | #include "debugfs.h" | ||
22 | #include "assoc.h" | ||
23 | |||
24 | #ifdef ENABLE_PM | ||
25 | static struct pm_dev *wlan_pm_dev = NULL; | ||
26 | #endif | ||
27 | |||
28 | #define WLAN_TX_PWR_DEFAULT 20 /*100mW */ | ||
29 | #define WLAN_TX_PWR_US_DEFAULT 20 /*100mW */ | ||
30 | #define WLAN_TX_PWR_JP_DEFAULT 16 /*50mW */ | ||
31 | #define WLAN_TX_PWR_FR_DEFAULT 20 /*100mW */ | ||
32 | #define WLAN_TX_PWR_EMEA_DEFAULT 20 /*100mW */ | ||
33 | |||
34 | /* Format { channel, frequency (MHz), maxtxpower } */ | ||
35 | /* band: 'B/G', region: USA FCC/Canada IC */ | ||
36 | static struct chan_freq_power channel_freq_power_US_BG[] = { | ||
37 | {1, 2412, WLAN_TX_PWR_US_DEFAULT}, | ||
38 | {2, 2417, WLAN_TX_PWR_US_DEFAULT}, | ||
39 | {3, 2422, WLAN_TX_PWR_US_DEFAULT}, | ||
40 | {4, 2427, WLAN_TX_PWR_US_DEFAULT}, | ||
41 | {5, 2432, WLAN_TX_PWR_US_DEFAULT}, | ||
42 | {6, 2437, WLAN_TX_PWR_US_DEFAULT}, | ||
43 | {7, 2442, WLAN_TX_PWR_US_DEFAULT}, | ||
44 | {8, 2447, WLAN_TX_PWR_US_DEFAULT}, | ||
45 | {9, 2452, WLAN_TX_PWR_US_DEFAULT}, | ||
46 | {10, 2457, WLAN_TX_PWR_US_DEFAULT}, | ||
47 | {11, 2462, WLAN_TX_PWR_US_DEFAULT} | ||
48 | }; | ||
49 | |||
50 | /* band: 'B/G', region: Europe ETSI */ | ||
51 | static struct chan_freq_power channel_freq_power_EU_BG[] = { | ||
52 | {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
53 | {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
54 | {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
55 | {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
56 | {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
57 | {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
58 | {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
59 | {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
60 | {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
61 | {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
62 | {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
63 | {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, | ||
64 | {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} | ||
65 | }; | ||
66 | |||
67 | /* band: 'B/G', region: Spain */ | ||
68 | static struct chan_freq_power channel_freq_power_SPN_BG[] = { | ||
69 | {10, 2457, WLAN_TX_PWR_DEFAULT}, | ||
70 | {11, 2462, WLAN_TX_PWR_DEFAULT} | ||
71 | }; | ||
72 | |||
73 | /* band: 'B/G', region: France */ | ||
74 | static struct chan_freq_power channel_freq_power_FR_BG[] = { | ||
75 | {10, 2457, WLAN_TX_PWR_FR_DEFAULT}, | ||
76 | {11, 2462, WLAN_TX_PWR_FR_DEFAULT}, | ||
77 | {12, 2467, WLAN_TX_PWR_FR_DEFAULT}, | ||
78 | {13, 2472, WLAN_TX_PWR_FR_DEFAULT} | ||
79 | }; | ||
80 | |||
81 | /* band: 'B/G', region: Japan */ | ||
82 | static struct chan_freq_power channel_freq_power_JPN_BG[] = { | ||
83 | {1, 2412, WLAN_TX_PWR_JP_DEFAULT}, | ||
84 | {2, 2417, WLAN_TX_PWR_JP_DEFAULT}, | ||
85 | {3, 2422, WLAN_TX_PWR_JP_DEFAULT}, | ||
86 | {4, 2427, WLAN_TX_PWR_JP_DEFAULT}, | ||
87 | {5, 2432, WLAN_TX_PWR_JP_DEFAULT}, | ||
88 | {6, 2437, WLAN_TX_PWR_JP_DEFAULT}, | ||
89 | {7, 2442, WLAN_TX_PWR_JP_DEFAULT}, | ||
90 | {8, 2447, WLAN_TX_PWR_JP_DEFAULT}, | ||
91 | {9, 2452, WLAN_TX_PWR_JP_DEFAULT}, | ||
92 | {10, 2457, WLAN_TX_PWR_JP_DEFAULT}, | ||
93 | {11, 2462, WLAN_TX_PWR_JP_DEFAULT}, | ||
94 | {12, 2467, WLAN_TX_PWR_JP_DEFAULT}, | ||
95 | {13, 2472, WLAN_TX_PWR_JP_DEFAULT}, | ||
96 | {14, 2484, WLAN_TX_PWR_JP_DEFAULT} | ||
97 | }; | ||
98 | |||
99 | /** | ||
100 | * the structure for channel, frequency and power | ||
101 | */ | ||
102 | struct region_cfp_table { | ||
103 | u8 region; | ||
104 | struct chan_freq_power *cfp_BG; | ||
105 | int cfp_no_BG; | ||
106 | }; | ||
107 | |||
108 | /** | ||
109 | * the structure for the mapping between region and CFP | ||
110 | */ | ||
111 | static struct region_cfp_table region_cfp_table[] = { | ||
112 | {0x10, /*US FCC */ | ||
113 | channel_freq_power_US_BG, | ||
114 | sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), | ||
115 | } | ||
116 | , | ||
117 | {0x20, /*CANADA IC */ | ||
118 | channel_freq_power_US_BG, | ||
119 | sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), | ||
120 | } | ||
121 | , | ||
122 | {0x30, /*EU*/ channel_freq_power_EU_BG, | ||
123 | sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power), | ||
124 | } | ||
125 | , | ||
126 | {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, | ||
127 | sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power), | ||
128 | } | ||
129 | , | ||
130 | {0x32, /*FRANCE*/ channel_freq_power_FR_BG, | ||
131 | sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power), | ||
132 | } | ||
133 | , | ||
134 | {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, | ||
135 | sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power), | ||
136 | } | ||
137 | , | ||
138 | /*Add new region here */ | ||
139 | }; | ||
140 | |||
141 | /** | ||
142 | * the rates supported by the card | ||
143 | */ | ||
144 | u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] = | ||
145 | { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, | ||
146 | 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 | ||
147 | }; | ||
148 | |||
149 | /** | ||
150 | * the rates supported | ||
151 | */ | ||
152 | u8 libertas_supported_rates[G_SUPPORTED_RATES] = | ||
153 | { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, | ||
154 | 0 }; | ||
155 | |||
156 | /** | ||
157 | * the rates supported for ad-hoc G mode | ||
158 | */ | ||
159 | u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] = | ||
160 | { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, | ||
161 | 0 }; | ||
162 | |||
163 | /** | ||
164 | * the rates supported for ad-hoc B mode | ||
165 | */ | ||
166 | u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 }; | ||
167 | |||
168 | /** | ||
169 | * the global variable of a pointer to wlan_private | ||
170 | * structure variable | ||
171 | */ | ||
172 | static wlan_private *wlanpriv = NULL; | ||
173 | |||
174 | #define MAX_DEVS 5 | ||
175 | static struct net_device *libertas_devs[MAX_DEVS]; | ||
176 | static int libertas_found = 0; | ||
177 | |||
178 | /** | ||
179 | * the table to keep region code | ||
180 | */ | ||
181 | u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] = | ||
182 | { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; | ||
183 | |||
184 | static u8 *default_fw_name = "usb8388.bin"; | ||
185 | |||
186 | /** | ||
187 | * Attributes exported through sysfs | ||
188 | */ | ||
189 | #define to_net_dev(class) container_of(class, struct net_device, class_dev) | ||
190 | |||
191 | /** | ||
192 | * @brief Get function for sysfs attribute libertas_mpp | ||
193 | */ | ||
194 | static ssize_t libertas_mpp_get(struct class_device * dev, char * buf) { | ||
195 | struct cmd_ds_mesh_access mesh_access; | ||
196 | |||
197 | memset(&mesh_access, 0, sizeof(mesh_access)); | ||
198 | libertas_prepare_and_send_command(to_net_dev(dev)->priv, | ||
199 | cmd_mesh_access, | ||
200 | cmd_act_mesh_get_mpp, | ||
201 | cmd_option_waitforrsp, 0, (void *)&mesh_access); | ||
202 | |||
203 | return snprintf(buf, 3, "%d\n", mesh_access.data[0]); | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * @brief Set function for sysfs attribute libertas_mpp | ||
208 | */ | ||
209 | static ssize_t libertas_mpp_set(struct class_device * dev, const char * buf, | ||
210 | size_t count) { | ||
211 | struct cmd_ds_mesh_access mesh_access; | ||
212 | |||
213 | |||
214 | memset(&mesh_access, 0, sizeof(mesh_access)); | ||
215 | sscanf(buf, "%d", &(mesh_access.data[0])); | ||
216 | libertas_prepare_and_send_command((to_net_dev(dev))->priv, | ||
217 | cmd_mesh_access, | ||
218 | cmd_act_mesh_set_mpp, | ||
219 | cmd_option_waitforrsp, 0, (void *)&mesh_access); | ||
220 | return strlen(buf); | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * libertas_mpp attribute to be exported per mshX interface | ||
225 | * through sysfs (/sys/class/net/mshX/libertas-mpp) | ||
226 | */ | ||
227 | static CLASS_DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get, | ||
228 | libertas_mpp_set ); | ||
229 | |||
230 | /** | ||
231 | * @brief Check if the device can be open and wait if necessary. | ||
232 | * | ||
233 | * @param dev A pointer to net_device structure | ||
234 | * @return 0 | ||
235 | * | ||
236 | * For USB adapter, on some systems the device open handler will be | ||
237 | * called before FW ready. Use the following flag check and wait | ||
238 | * function to work around the issue. | ||
239 | * | ||
240 | */ | ||
241 | static int pre_open_check(struct net_device *dev) { | ||
242 | wlan_private *priv = (wlan_private *) dev->priv; | ||
243 | wlan_adapter *adapter = priv->adapter; | ||
244 | int i = 0; | ||
245 | |||
246 | while (!adapter->fw_ready && i < 20) { | ||
247 | i++; | ||
248 | msleep_interruptible(100); | ||
249 | } | ||
250 | if (!adapter->fw_ready) { | ||
251 | lbs_pr_info("FW not ready, pre_open_check() return failure\n"); | ||
252 | LEAVE(); | ||
253 | return -1; | ||
254 | } | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * @brief This function opens the device | ||
261 | * | ||
262 | * @param dev A pointer to net_device structure | ||
263 | * @return 0 | ||
264 | */ | ||
265 | static int wlan_dev_open(struct net_device *dev) | ||
266 | { | ||
267 | wlan_private *priv = (wlan_private *) dev->priv; | ||
268 | wlan_adapter *adapter = priv->adapter; | ||
269 | |||
270 | ENTER(); | ||
271 | |||
272 | |||
273 | priv->open = 1; | ||
274 | |||
275 | if (adapter->connect_status == libertas_connected) { | ||
276 | netif_carrier_on(priv->wlan_dev.netdev); | ||
277 | } else | ||
278 | netif_carrier_off(priv->wlan_dev.netdev); | ||
279 | |||
280 | LEAVE(); | ||
281 | return 0; | ||
282 | } | ||
283 | /** | ||
284 | * @brief This function opens the mshX interface | ||
285 | * | ||
286 | * @param dev A pointer to net_device structure | ||
287 | * @return 0 | ||
288 | */ | ||
289 | static int mesh_open(struct net_device *dev) | ||
290 | { | ||
291 | wlan_private *priv = (wlan_private *) dev->priv ; | ||
292 | |||
293 | if(pre_open_check(dev) == -1) | ||
294 | return -1; | ||
295 | priv->mesh_open = 1 ; | ||
296 | netif_start_queue(priv->mesh_dev); | ||
297 | if (priv->infra_open == 0) | ||
298 | return wlan_dev_open(priv->wlan_dev.netdev) ; | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * @brief This function opens the ethX interface | ||
304 | * | ||
305 | * @param dev A pointer to net_device structure | ||
306 | * @return 0 | ||
307 | */ | ||
308 | static int wlan_open(struct net_device *dev) | ||
309 | { | ||
310 | wlan_private *priv = (wlan_private *) dev->priv ; | ||
311 | |||
312 | if(pre_open_check(dev) == -1) | ||
313 | return -1; | ||
314 | priv->infra_open = 1 ; | ||
315 | netif_wake_queue(priv->wlan_dev.netdev); | ||
316 | if (priv->open == 0) | ||
317 | return wlan_dev_open(priv->wlan_dev.netdev) ; | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int wlan_dev_close(struct net_device *dev) | ||
322 | { | ||
323 | wlan_private *priv = dev->priv; | ||
324 | |||
325 | ENTER(); | ||
326 | |||
327 | netif_carrier_off(priv->wlan_dev.netdev); | ||
328 | priv->open = 0; | ||
329 | |||
330 | LEAVE(); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * @brief This function closes the mshX interface | ||
336 | * | ||
337 | * @param dev A pointer to net_device structure | ||
338 | * @return 0 | ||
339 | */ | ||
340 | static int mesh_close(struct net_device *dev) | ||
341 | { | ||
342 | wlan_private *priv = (wlan_private *) (dev->priv); | ||
343 | |||
344 | priv->mesh_open = 0; | ||
345 | netif_stop_queue(priv->mesh_dev); | ||
346 | if (priv->infra_open == 0) | ||
347 | return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; | ||
348 | else | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * @brief This function closes the ethX interface | ||
354 | * | ||
355 | * @param dev A pointer to net_device structure | ||
356 | * @return 0 | ||
357 | */ | ||
358 | static int wlan_close(struct net_device *dev) { | ||
359 | wlan_private *priv = (wlan_private *) dev->priv; | ||
360 | |||
361 | netif_stop_queue(priv->wlan_dev.netdev); | ||
362 | priv->infra_open = 0; | ||
363 | if (priv->mesh_open == 0) | ||
364 | return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; | ||
365 | else | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | |||
370 | #ifdef ENABLE_PM | ||
371 | |||
372 | /** | ||
373 | * @brief This function is a callback function. it is called by | ||
374 | * kernel to enter or exit power saving mode. | ||
375 | * | ||
376 | * @param pmdev A pointer to pm_dev | ||
377 | * @param pmreq pm_request_t | ||
378 | * @param pmdata A pointer to pmdata | ||
379 | * @return 0 or -1 | ||
380 | */ | ||
381 | static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq, | ||
382 | void *pmdata) | ||
383 | { | ||
384 | wlan_private *priv = wlanpriv; | ||
385 | wlan_adapter *adapter = priv->adapter; | ||
386 | struct net_device *dev = priv->wlan_dev.netdev; | ||
387 | |||
388 | lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq); | ||
389 | |||
390 | switch (pmreq) { | ||
391 | case PM_SUSPEND: | ||
392 | lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n"); | ||
393 | |||
394 | /* in associated mode */ | ||
395 | if (adapter->connect_status == libertas_connected) { | ||
396 | if ((adapter->psstate != PS_STATE_SLEEP) | ||
397 | ) { | ||
398 | lbs_pr_debug(1, | ||
399 | "wlan_pm_callback: can't enter sleep mode\n"); | ||
400 | return -1; | ||
401 | } else { | ||
402 | |||
403 | /* | ||
404 | * Detach the network interface | ||
405 | * if the network is running | ||
406 | */ | ||
407 | if (netif_running(dev)) { | ||
408 | netif_device_detach(dev); | ||
409 | lbs_pr_debug(1, | ||
410 | "netif_device_detach().\n"); | ||
411 | } | ||
412 | libertas_sbi_suspend(priv); | ||
413 | } | ||
414 | break; | ||
415 | } | ||
416 | |||
417 | /* in non associated mode */ | ||
418 | |||
419 | /* | ||
420 | * Detach the network interface | ||
421 | * if the network is running | ||
422 | */ | ||
423 | if (netif_running(dev)) | ||
424 | netif_device_detach(dev); | ||
425 | |||
426 | /* | ||
427 | * Storing and restoring of the regs be taken care | ||
428 | * at the driver rest will be done at wlan driver | ||
429 | * this makes driver independent of the card | ||
430 | */ | ||
431 | |||
432 | libertas_sbi_suspend(priv); | ||
433 | |||
434 | break; | ||
435 | |||
436 | case PM_RESUME: | ||
437 | /* in associated mode */ | ||
438 | if (adapter->connect_status == libertas_connected) { | ||
439 | { | ||
440 | /* | ||
441 | * Bring the inteface up first | ||
442 | * This case should not happen still ... | ||
443 | */ | ||
444 | libertas_sbi_resume(priv); | ||
445 | |||
446 | /* | ||
447 | * Attach the network interface | ||
448 | * if the network is running | ||
449 | */ | ||
450 | if (netif_running(dev)) { | ||
451 | netif_device_attach(dev); | ||
452 | lbs_pr_debug(1, | ||
453 | "after netif_device_attach().\n"); | ||
454 | } | ||
455 | lbs_pr_debug(1, | ||
456 | "After netif attach, in associated mode.\n"); | ||
457 | } | ||
458 | break; | ||
459 | } | ||
460 | |||
461 | /* in non associated mode */ | ||
462 | |||
463 | /* | ||
464 | * Bring the inteface up first | ||
465 | * This case should not happen still ... | ||
466 | */ | ||
467 | |||
468 | libertas_sbi_resume(priv); | ||
469 | |||
470 | if (netif_running(dev)) | ||
471 | netif_device_attach(dev); | ||
472 | |||
473 | lbs_pr_debug(1, "after netif attach, in NON associated mode.\n"); | ||
474 | break; | ||
475 | } | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | #endif /* ENABLE_PM */ | ||
480 | |||
481 | static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
482 | { | ||
483 | int ret = 0; | ||
484 | wlan_private *priv = dev->priv; | ||
485 | |||
486 | ENTER(); | ||
487 | |||
488 | if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) { | ||
489 | priv->stats.tx_dropped++; | ||
490 | goto done; | ||
491 | } | ||
492 | |||
493 | netif_stop_queue(priv->wlan_dev.netdev); | ||
494 | |||
495 | if (libertas_process_tx(priv, skb) == 0) | ||
496 | dev->trans_start = jiffies; | ||
497 | done: | ||
498 | LEAVE(); | ||
499 | return ret; | ||
500 | } | ||
501 | |||
502 | /** | ||
503 | * @brief Mark mesh packets and handover them to wlan_hard_start_xmit | ||
504 | * | ||
505 | */ | ||
506 | static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
507 | { | ||
508 | wlan_private *priv = dev->priv; | ||
509 | ENTER(); | ||
510 | SET_MESH_FRAME(skb); | ||
511 | LEAVE(); | ||
512 | |||
513 | return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev); | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit | ||
518 | * | ||
519 | */ | ||
520 | static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) { | ||
521 | ENTER(); | ||
522 | UNSET_MESH_FRAME(skb); | ||
523 | LEAVE(); | ||
524 | return wlan_hard_start_xmit(skb, dev); | ||
525 | } | ||
526 | |||
527 | static void wlan_tx_timeout(struct net_device *dev) | ||
528 | { | ||
529 | wlan_private *priv = (wlan_private *) dev->priv; | ||
530 | |||
531 | ENTER(); | ||
532 | |||
533 | lbs_pr_err("tx watch dog timeout!\n"); | ||
534 | |||
535 | priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; | ||
536 | dev->trans_start = jiffies; | ||
537 | |||
538 | if (priv->adapter->currenttxskb) { | ||
539 | if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { | ||
540 | /* If we are here, we have not received feedback from | ||
541 | the previous packet. Assume TX_FAIL and move on. */ | ||
542 | priv->adapter->eventcause = 0x01000000; | ||
543 | libertas_send_tx_feedback(priv); | ||
544 | } else | ||
545 | wake_up_interruptible(&priv->mainthread.waitq); | ||
546 | } else if (priv->adapter->connect_status == libertas_connected) | ||
547 | netif_wake_queue(priv->wlan_dev.netdev); | ||
548 | |||
549 | LEAVE(); | ||
550 | } | ||
551 | |||
552 | /** | ||
553 | * @brief This function returns the network statistics | ||
554 | * | ||
555 | * @param dev A pointer to wlan_private structure | ||
556 | * @return A pointer to net_device_stats structure | ||
557 | */ | ||
558 | static struct net_device_stats *wlan_get_stats(struct net_device *dev) | ||
559 | { | ||
560 | wlan_private *priv = (wlan_private *) dev->priv; | ||
561 | |||
562 | return &priv->stats; | ||
563 | } | ||
564 | |||
565 | static int wlan_set_mac_address(struct net_device *dev, void *addr) | ||
566 | { | ||
567 | int ret = 0; | ||
568 | wlan_private *priv = (wlan_private *) dev->priv; | ||
569 | wlan_adapter *adapter = priv->adapter; | ||
570 | struct sockaddr *phwaddr = addr; | ||
571 | |||
572 | ENTER(); | ||
573 | |||
574 | memset(adapter->current_addr, 0, ETH_ALEN); | ||
575 | |||
576 | /* dev->dev_addr is 8 bytes */ | ||
577 | lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN); | ||
578 | |||
579 | lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN); | ||
580 | memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN); | ||
581 | |||
582 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address, | ||
583 | cmd_act_set, | ||
584 | cmd_option_waitforrsp, 0, NULL); | ||
585 | |||
586 | if (ret) { | ||
587 | lbs_pr_debug(1, "set mac address failed.\n"); | ||
588 | ret = -1; | ||
589 | goto done; | ||
590 | } | ||
591 | |||
592 | lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN); | ||
593 | memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN); | ||
594 | memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); | ||
595 | |||
596 | done: | ||
597 | LEAVE(); | ||
598 | return ret; | ||
599 | } | ||
600 | |||
601 | static int wlan_copy_multicast_address(wlan_adapter * adapter, | ||
602 | struct net_device *dev) | ||
603 | { | ||
604 | int i = 0; | ||
605 | struct dev_mc_list *mcptr = dev->mc_list; | ||
606 | |||
607 | for (i = 0; i < dev->mc_count; i++) { | ||
608 | memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); | ||
609 | mcptr = mcptr->next; | ||
610 | } | ||
611 | |||
612 | return i; | ||
613 | |||
614 | } | ||
615 | |||
616 | static void wlan_set_multicast_list(struct net_device *dev) | ||
617 | { | ||
618 | wlan_private *priv = dev->priv; | ||
619 | wlan_adapter *adapter = priv->adapter; | ||
620 | int oldpacketfilter; | ||
621 | |||
622 | ENTER(); | ||
623 | |||
624 | oldpacketfilter = adapter->currentpacketfilter; | ||
625 | |||
626 | if (dev->flags & IFF_PROMISC) { | ||
627 | lbs_pr_debug(1, "enable Promiscuous mode\n"); | ||
628 | adapter->currentpacketfilter |= | ||
629 | cmd_act_mac_promiscuous_enable; | ||
630 | adapter->currentpacketfilter &= | ||
631 | ~(cmd_act_mac_all_multicast_enable | | ||
632 | cmd_act_mac_multicast_enable); | ||
633 | } else { | ||
634 | /* Multicast */ | ||
635 | adapter->currentpacketfilter &= | ||
636 | ~cmd_act_mac_promiscuous_enable; | ||
637 | |||
638 | if (dev->flags & IFF_ALLMULTI || dev->mc_count > | ||
639 | MRVDRV_MAX_MULTICAST_LIST_SIZE) { | ||
640 | lbs_pr_debug(1, "Enabling All Multicast!\n"); | ||
641 | adapter->currentpacketfilter |= | ||
642 | cmd_act_mac_all_multicast_enable; | ||
643 | adapter->currentpacketfilter &= | ||
644 | ~cmd_act_mac_multicast_enable; | ||
645 | } else { | ||
646 | adapter->currentpacketfilter &= | ||
647 | ~cmd_act_mac_all_multicast_enable; | ||
648 | |||
649 | if (!dev->mc_count) { | ||
650 | lbs_pr_debug(1, "No multicast addresses - " | ||
651 | "disabling multicast!\n"); | ||
652 | adapter->currentpacketfilter &= | ||
653 | ~cmd_act_mac_multicast_enable; | ||
654 | } else { | ||
655 | int i; | ||
656 | |||
657 | adapter->currentpacketfilter |= | ||
658 | cmd_act_mac_multicast_enable; | ||
659 | |||
660 | adapter->nr_of_multicastmacaddr = | ||
661 | wlan_copy_multicast_address(adapter, dev); | ||
662 | |||
663 | lbs_pr_debug(1, "Multicast addresses: %d\n", | ||
664 | dev->mc_count); | ||
665 | |||
666 | for (i = 0; i < dev->mc_count; i++) { | ||
667 | lbs_pr_debug(1, "Multicast address %d:" | ||
668 | "%x %x %x %x %x %x\n", i, | ||
669 | adapter->multicastlist[i][0], | ||
670 | adapter->multicastlist[i][1], | ||
671 | adapter->multicastlist[i][2], | ||
672 | adapter->multicastlist[i][3], | ||
673 | adapter->multicastlist[i][4], | ||
674 | adapter->multicastlist[i][5]); | ||
675 | } | ||
676 | /* set multicast addresses to firmware */ | ||
677 | libertas_prepare_and_send_command(priv, | ||
678 | cmd_mac_multicast_adr, | ||
679 | cmd_act_set, 0, 0, | ||
680 | NULL); | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | |||
685 | if (adapter->currentpacketfilter != oldpacketfilter) { | ||
686 | libertas_set_mac_packet_filter(priv); | ||
687 | } | ||
688 | |||
689 | LEAVE(); | ||
690 | } | ||
691 | |||
692 | /** | ||
693 | * @brief This function hanldes the major job in WLAN driver. | ||
694 | * it handles the event generated by firmware, rx data received | ||
695 | * from firmware and tx data sent from kernel. | ||
696 | * | ||
697 | * @param data A pointer to wlan_thread structure | ||
698 | * @return 0 | ||
699 | */ | ||
700 | static int wlan_service_main_thread(void *data) | ||
701 | { | ||
702 | struct wlan_thread *thread = data; | ||
703 | wlan_private *priv = thread->priv; | ||
704 | wlan_adapter *adapter = priv->adapter; | ||
705 | wait_queue_t wait; | ||
706 | u8 ireg = 0; | ||
707 | |||
708 | ENTER(); | ||
709 | |||
710 | wlan_activate_thread(thread); | ||
711 | |||
712 | init_waitqueue_entry(&wait, current); | ||
713 | |||
714 | for (;;) { | ||
715 | lbs_pr_debug(1, "main-thread 111: intcounter=%d " | ||
716 | "currenttxskb=%p dnld_sent=%d\n", | ||
717 | adapter->intcounter, | ||
718 | adapter->currenttxskb, priv->wlan_dev.dnld_sent); | ||
719 | |||
720 | add_wait_queue(&thread->waitq, &wait); | ||
721 | set_current_state(TASK_INTERRUPTIBLE); | ||
722 | spin_lock_irq(&adapter->driver_lock); | ||
723 | if ((adapter->psstate == PS_STATE_SLEEP) || | ||
724 | (!adapter->intcounter | ||
725 | && (priv->wlan_dev.dnld_sent || adapter->cur_cmd || | ||
726 | list_empty(&adapter->cmdpendingq)))) { | ||
727 | lbs_pr_debug(1, | ||
728 | "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", | ||
729 | adapter->connect_status, adapter->intcounter, | ||
730 | adapter->psmode, adapter->psstate); | ||
731 | spin_unlock_irq(&adapter->driver_lock); | ||
732 | schedule(); | ||
733 | } else | ||
734 | spin_unlock_irq(&adapter->driver_lock); | ||
735 | |||
736 | |||
737 | lbs_pr_debug(1, | ||
738 | "main-thread 222 (waking up): intcounter=%d currenttxskb=%p " | ||
739 | "dnld_sent=%d\n", adapter->intcounter, | ||
740 | adapter->currenttxskb, priv->wlan_dev.dnld_sent); | ||
741 | |||
742 | set_current_state(TASK_RUNNING); | ||
743 | remove_wait_queue(&thread->waitq, &wait); | ||
744 | try_to_freeze(); | ||
745 | |||
746 | lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p " | ||
747 | "dnld_sent=%d\n", | ||
748 | adapter->intcounter, | ||
749 | adapter->currenttxskb, priv->wlan_dev.dnld_sent); | ||
750 | |||
751 | if (kthread_should_stop() | ||
752 | || adapter->surpriseremoved) { | ||
753 | lbs_pr_debug(1, | ||
754 | "main-thread: break from main thread: surpriseremoved=0x%x\n", | ||
755 | adapter->surpriseremoved); | ||
756 | break; | ||
757 | } | ||
758 | |||
759 | |||
760 | spin_lock_irq(&adapter->driver_lock); | ||
761 | if (adapter->intcounter) { | ||
762 | u8 int_status; | ||
763 | adapter->intcounter = 0; | ||
764 | int_status = libertas_sbi_get_int_status(priv, &ireg); | ||
765 | |||
766 | if (int_status) { | ||
767 | lbs_pr_debug(1, | ||
768 | "main-thread: reading HOST_INT_STATUS_REG failed\n"); | ||
769 | spin_unlock_irq(&adapter->driver_lock); | ||
770 | continue; | ||
771 | } | ||
772 | adapter->hisregcpy |= ireg; | ||
773 | } | ||
774 | |||
775 | lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p " | ||
776 | "dnld_sent=%d\n", | ||
777 | adapter->intcounter, | ||
778 | adapter->currenttxskb, priv->wlan_dev.dnld_sent); | ||
779 | |||
780 | /* command response? */ | ||
781 | if (adapter->hisregcpy & his_cmdupldrdy) { | ||
782 | lbs_pr_debug(1, "main-thread: cmd response ready.\n"); | ||
783 | |||
784 | adapter->hisregcpy &= ~his_cmdupldrdy; | ||
785 | spin_unlock_irq(&adapter->driver_lock); | ||
786 | libertas_process_rx_command(priv); | ||
787 | spin_lock_irq(&adapter->driver_lock); | ||
788 | } | ||
789 | |||
790 | /* Any Card Event */ | ||
791 | if (adapter->hisregcpy & his_cardevent) { | ||
792 | lbs_pr_debug(1, "main-thread: Card Event Activity.\n"); | ||
793 | |||
794 | adapter->hisregcpy &= ~his_cardevent; | ||
795 | |||
796 | if (libertas_sbi_read_event_cause(priv)) { | ||
797 | lbs_pr_alert( | ||
798 | "main-thread: libertas_sbi_read_event_cause failed.\n"); | ||
799 | spin_unlock_irq(&adapter->driver_lock); | ||
800 | continue; | ||
801 | } | ||
802 | spin_unlock_irq(&adapter->driver_lock); | ||
803 | libertas_process_event(priv); | ||
804 | } else | ||
805 | spin_unlock_irq(&adapter->driver_lock); | ||
806 | |||
807 | /* Check if we need to confirm Sleep Request received previously */ | ||
808 | if (adapter->psstate == PS_STATE_PRE_SLEEP) { | ||
809 | if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) { | ||
810 | if (adapter->connect_status == | ||
811 | libertas_connected) { | ||
812 | lbs_pr_debug(1, | ||
813 | "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p " | ||
814 | "dnld_sent=%d cur_cmd=%p, confirm now\n", | ||
815 | adapter->intcounter, | ||
816 | adapter->currenttxskb, | ||
817 | priv->wlan_dev.dnld_sent, | ||
818 | adapter->cur_cmd); | ||
819 | |||
820 | libertas_ps_confirm_sleep(priv, | ||
821 | (u16) adapter->psmode); | ||
822 | } else { | ||
823 | /* workaround for firmware sending | ||
824 | * deauth/linkloss event immediately | ||
825 | * after sleep request, remove this | ||
826 | * after firmware fixes it | ||
827 | */ | ||
828 | adapter->psstate = PS_STATE_AWAKE; | ||
829 | lbs_pr_alert( | ||
830 | "main-thread: ignore PS_SleepConfirm in non-connected state\n"); | ||
831 | } | ||
832 | } | ||
833 | } | ||
834 | |||
835 | /* The PS state is changed during processing of Sleep Request | ||
836 | * event above | ||
837 | */ | ||
838 | if ((priv->adapter->psstate == PS_STATE_SLEEP) || | ||
839 | (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) | ||
840 | continue; | ||
841 | |||
842 | /* Execute the next command */ | ||
843 | if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd) | ||
844 | libertas_execute_next_command(priv); | ||
845 | |||
846 | /* Wake-up command waiters which can't sleep in | ||
847 | * libertas_prepare_and_send_command | ||
848 | */ | ||
849 | if (!adapter->nr_cmd_pending) | ||
850 | wake_up_all(&adapter->cmd_pending); | ||
851 | |||
852 | libertas_tx_runqueue(priv); | ||
853 | } | ||
854 | |||
855 | del_timer(&adapter->command_timer); | ||
856 | adapter->nr_cmd_pending = 0; | ||
857 | wake_up_all(&adapter->cmd_pending); | ||
858 | wlan_deactivate_thread(thread); | ||
859 | |||
860 | LEAVE(); | ||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | /** | ||
865 | * @brief This function adds the card. it will probe the | ||
866 | * card, allocate the wlan_priv and initialize the device. | ||
867 | * | ||
868 | * @param card A pointer to card | ||
869 | * @return A pointer to wlan_private structure | ||
870 | */ | ||
871 | wlan_private *wlan_add_card(void *card) | ||
872 | { | ||
873 | struct net_device *dev = NULL; | ||
874 | struct net_device *mesh_dev = NULL; | ||
875 | wlan_private *priv = NULL; | ||
876 | |||
877 | ENTER(); | ||
878 | |||
879 | /* Allocate an Ethernet device and register it */ | ||
880 | if (!(dev = alloc_etherdev(sizeof(wlan_private)))) { | ||
881 | lbs_pr_alert( "Init ethernet device failed!\n"); | ||
882 | return NULL; | ||
883 | } | ||
884 | |||
885 | priv = dev->priv; | ||
886 | |||
887 | /* allocate buffer for wlan_adapter */ | ||
888 | if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) { | ||
889 | lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n"); | ||
890 | goto err_kmalloc; | ||
891 | } | ||
892 | |||
893 | /* Allocate a virtual mesh device */ | ||
894 | if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) { | ||
895 | lbs_pr_debug(1, "Init ethernet device failed!\n"); | ||
896 | return NULL; | ||
897 | } | ||
898 | |||
899 | /* Both intervaces share the priv structure */ | ||
900 | mesh_dev->priv = priv; | ||
901 | |||
902 | /* init wlan_adapter */ | ||
903 | memset(priv->adapter, 0, sizeof(wlan_adapter)); | ||
904 | |||
905 | priv->wlan_dev.netdev = dev; | ||
906 | priv->wlan_dev.card = card; | ||
907 | priv->mesh_open = 0; | ||
908 | priv->infra_open = 0; | ||
909 | priv->mesh_dev = mesh_dev; | ||
910 | wlanpriv = priv; | ||
911 | |||
912 | SET_MODULE_OWNER(dev); | ||
913 | SET_MODULE_OWNER(mesh_dev); | ||
914 | |||
915 | /* Setup the OS Interface to our functions */ | ||
916 | dev->open = wlan_open; | ||
917 | dev->hard_start_xmit = wlan_pre_start_xmit; | ||
918 | dev->stop = wlan_close; | ||
919 | dev->do_ioctl = libertas_do_ioctl; | ||
920 | dev->set_mac_address = wlan_set_mac_address; | ||
921 | mesh_dev->open = mesh_open; | ||
922 | mesh_dev->hard_start_xmit = mesh_pre_start_xmit; | ||
923 | mesh_dev->stop = mesh_close; | ||
924 | mesh_dev->do_ioctl = libertas_do_ioctl; | ||
925 | memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr, | ||
926 | sizeof(wlanpriv->wlan_dev.netdev->dev_addr)); | ||
927 | |||
928 | #define WLAN_WATCHDOG_TIMEOUT (5 * HZ) | ||
929 | |||
930 | dev->tx_timeout = wlan_tx_timeout; | ||
931 | dev->get_stats = wlan_get_stats; | ||
932 | dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT; | ||
933 | dev->ethtool_ops = &libertas_ethtool_ops; | ||
934 | mesh_dev->get_stats = wlan_get_stats; | ||
935 | mesh_dev->ethtool_ops = &libertas_ethtool_ops; | ||
936 | |||
937 | #ifdef WIRELESS_EXT | ||
938 | dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; | ||
939 | mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; | ||
940 | #endif | ||
941 | #define NETIF_F_DYNALLOC 16 | ||
942 | dev->features |= NETIF_F_DYNALLOC; | ||
943 | dev->flags |= IFF_BROADCAST | IFF_MULTICAST; | ||
944 | dev->set_multicast_list = wlan_set_multicast_list; | ||
945 | |||
946 | INIT_LIST_HEAD(&priv->adapter->cmdfreeq); | ||
947 | INIT_LIST_HEAD(&priv->adapter->cmdpendingq); | ||
948 | |||
949 | spin_lock_init(&priv->adapter->driver_lock); | ||
950 | init_waitqueue_head(&priv->adapter->cmd_pending); | ||
951 | priv->adapter->nr_cmd_pending = 0; | ||
952 | |||
953 | lbs_pr_debug(1, "Starting kthread...\n"); | ||
954 | priv->mainthread.priv = priv; | ||
955 | wlan_create_thread(wlan_service_main_thread, | ||
956 | &priv->mainthread, "wlan_main_service"); | ||
957 | |||
958 | priv->assoc_thread = | ||
959 | create_singlethread_workqueue("libertas_assoc"); | ||
960 | INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker); | ||
961 | |||
962 | /* | ||
963 | * Register the device. Fillup the private data structure with | ||
964 | * relevant information from the card and request for the required | ||
965 | * IRQ. | ||
966 | */ | ||
967 | if (libertas_sbi_register_dev(priv) < 0) { | ||
968 | lbs_pr_info("failed to register wlan device!\n"); | ||
969 | goto err_registerdev; | ||
970 | } | ||
971 | |||
972 | /* init FW and HW */ | ||
973 | if (libertas_init_fw(priv)) { | ||
974 | lbs_pr_debug(1, "Firmware Init failed\n"); | ||
975 | goto err_registerdev; | ||
976 | } | ||
977 | |||
978 | if (register_netdev(dev)) { | ||
979 | lbs_pr_err("Cannot register network device!\n"); | ||
980 | goto err_init_fw; | ||
981 | } | ||
982 | |||
983 | /* Register virtual mesh interface */ | ||
984 | if (register_netdev(mesh_dev)) { | ||
985 | lbs_pr_info("Cannot register mesh virtual interface!\n"); | ||
986 | goto err_init_fw; | ||
987 | } | ||
988 | |||
989 | lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name); | ||
990 | |||
991 | libertas_debugfs_init_one(priv, dev); | ||
992 | |||
993 | if (libertas_found == MAX_DEVS) | ||
994 | goto err_init_fw; | ||
995 | libertas_devs[libertas_found] = dev; | ||
996 | libertas_found++; | ||
997 | #ifdef ENABLE_PM | ||
998 | if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback))) | ||
999 | lbs_pr_alert( "failed to register PM callback\n"); | ||
1000 | #endif | ||
1001 | if (class_device_create_file(&(mesh_dev->class_dev), &class_device_attr_libertas_mpp)) | ||
1002 | goto err_create_file; | ||
1003 | |||
1004 | LEAVE(); | ||
1005 | return priv; | ||
1006 | |||
1007 | err_create_file: | ||
1008 | class_device_remove_file(&(mesh_dev->class_dev), &class_device_attr_libertas_mpp); | ||
1009 | err_init_fw: | ||
1010 | libertas_sbi_unregister_dev(priv); | ||
1011 | err_registerdev: | ||
1012 | destroy_workqueue(priv->assoc_thread); | ||
1013 | /* Stop the thread servicing the interrupts */ | ||
1014 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1015 | wlan_terminate_thread(&priv->mainthread); | ||
1016 | kfree(priv->adapter); | ||
1017 | err_kmalloc: | ||
1018 | free_netdev(dev); | ||
1019 | free_netdev(mesh_dev); | ||
1020 | wlanpriv = NULL; | ||
1021 | |||
1022 | LEAVE(); | ||
1023 | return NULL; | ||
1024 | } | ||
1025 | |||
1026 | static void wake_pending_cmdnodes(wlan_private *priv) | ||
1027 | { | ||
1028 | struct cmd_ctrl_node *cmdnode; | ||
1029 | unsigned long flags; | ||
1030 | |||
1031 | spin_lock_irqsave(&priv->adapter->driver_lock, flags); | ||
1032 | list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) { | ||
1033 | cmdnode->cmdwaitqwoken = 1; | ||
1034 | wake_up_interruptible(&cmdnode->cmdwait_q); | ||
1035 | } | ||
1036 | spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); | ||
1037 | } | ||
1038 | |||
1039 | |||
1040 | int wlan_remove_card(void *card) | ||
1041 | { | ||
1042 | wlan_private *priv = libertas_sbi_get_priv(card); | ||
1043 | wlan_adapter *adapter; | ||
1044 | struct net_device *dev; | ||
1045 | struct net_device *mesh_dev; | ||
1046 | union iwreq_data wrqu; | ||
1047 | int i; | ||
1048 | |||
1049 | ENTER(); | ||
1050 | |||
1051 | if (!priv) { | ||
1052 | LEAVE(); | ||
1053 | return 0; | ||
1054 | } | ||
1055 | |||
1056 | adapter = priv->adapter; | ||
1057 | |||
1058 | if (!adapter) { | ||
1059 | LEAVE(); | ||
1060 | return 0; | ||
1061 | } | ||
1062 | |||
1063 | dev = priv->wlan_dev.netdev; | ||
1064 | mesh_dev = priv->mesh_dev; | ||
1065 | |||
1066 | netif_stop_queue(mesh_dev); | ||
1067 | netif_stop_queue(priv->wlan_dev.netdev); | ||
1068 | netif_carrier_off(priv->wlan_dev.netdev); | ||
1069 | |||
1070 | wake_pending_cmdnodes(priv); | ||
1071 | |||
1072 | class_device_remove_file(&(mesh_dev->class_dev), &class_device_attr_libertas_mpp); | ||
1073 | unregister_netdev(mesh_dev); | ||
1074 | unregister_netdev(dev); | ||
1075 | |||
1076 | cancel_delayed_work(&priv->assoc_work); | ||
1077 | destroy_workqueue(priv->assoc_thread); | ||
1078 | |||
1079 | if (adapter->psmode == wlan802_11powermodemax_psp) { | ||
1080 | adapter->psmode = wlan802_11powermodecam; | ||
1081 | libertas_ps_wakeup(priv, cmd_option_waitforrsp); | ||
1082 | } | ||
1083 | |||
1084 | memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN); | ||
1085 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
1086 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); | ||
1087 | |||
1088 | #ifdef ENABLE_PM | ||
1089 | pm_unregister(wlan_pm_dev); | ||
1090 | #endif | ||
1091 | |||
1092 | adapter->surpriseremoved = 1; | ||
1093 | |||
1094 | /* Stop the thread servicing the interrupts */ | ||
1095 | wlan_terminate_thread(&priv->mainthread); | ||
1096 | |||
1097 | libertas_debugfs_remove_one(priv); | ||
1098 | |||
1099 | lbs_pr_debug(1, "Free adapter\n"); | ||
1100 | libertas_free_adapter(priv); | ||
1101 | |||
1102 | for (i = 0; i<libertas_found; i++) { | ||
1103 | if (libertas_devs[i]==priv->wlan_dev.netdev) { | ||
1104 | libertas_devs[i] = libertas_devs[--libertas_found]; | ||
1105 | libertas_devs[libertas_found] = NULL ; | ||
1106 | break ; | ||
1107 | } | ||
1108 | } | ||
1109 | |||
1110 | lbs_pr_debug(1, "Unregister finish\n"); | ||
1111 | |||
1112 | priv->wlan_dev.netdev = NULL; | ||
1113 | priv->mesh_dev = NULL ; | ||
1114 | free_netdev(mesh_dev); | ||
1115 | free_netdev(dev); | ||
1116 | wlanpriv = NULL; | ||
1117 | |||
1118 | LEAVE(); | ||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | /** | ||
1123 | * @brief This function finds the CFP in | ||
1124 | * region_cfp_table based on region and band parameter. | ||
1125 | * | ||
1126 | * @param region The region code | ||
1127 | * @param band The band | ||
1128 | * @param cfp_no A pointer to CFP number | ||
1129 | * @return A pointer to CFP | ||
1130 | */ | ||
1131 | struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no) | ||
1132 | { | ||
1133 | int i, end; | ||
1134 | |||
1135 | ENTER(); | ||
1136 | |||
1137 | end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table); | ||
1138 | |||
1139 | for (i = 0; i < end ; i++) { | ||
1140 | lbs_pr_debug(1, "region_cfp_table[i].region=%d\n", | ||
1141 | region_cfp_table[i].region); | ||
1142 | if (region_cfp_table[i].region == region) { | ||
1143 | *cfp_no = region_cfp_table[i].cfp_no_BG; | ||
1144 | LEAVE(); | ||
1145 | return region_cfp_table[i].cfp_BG; | ||
1146 | } | ||
1147 | } | ||
1148 | |||
1149 | LEAVE(); | ||
1150 | return NULL; | ||
1151 | } | ||
1152 | |||
1153 | int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band) | ||
1154 | { | ||
1155 | wlan_adapter *adapter = priv->adapter; | ||
1156 | int i = 0; | ||
1157 | |||
1158 | struct chan_freq_power *cfp; | ||
1159 | int cfp_no; | ||
1160 | |||
1161 | ENTER(); | ||
1162 | |||
1163 | memset(adapter->region_channel, 0, sizeof(adapter->region_channel)); | ||
1164 | |||
1165 | { | ||
1166 | cfp = libertas_get_region_cfp_table(region, band, &cfp_no); | ||
1167 | if (cfp != NULL) { | ||
1168 | adapter->region_channel[i].nrcfp = cfp_no; | ||
1169 | adapter->region_channel[i].CFP = cfp; | ||
1170 | } else { | ||
1171 | lbs_pr_debug(1, "wrong region code %#x in band B-G\n", | ||
1172 | region); | ||
1173 | return -1; | ||
1174 | } | ||
1175 | adapter->region_channel[i].valid = 1; | ||
1176 | adapter->region_channel[i].region = region; | ||
1177 | adapter->region_channel[i].band = band; | ||
1178 | i++; | ||
1179 | } | ||
1180 | LEAVE(); | ||
1181 | return 0; | ||
1182 | } | ||
1183 | |||
1184 | /** | ||
1185 | * @brief This function handles the interrupt. it will change PS | ||
1186 | * state if applicable. it will wake up main_thread to handle | ||
1187 | * the interrupt event as well. | ||
1188 | * | ||
1189 | * @param dev A pointer to net_device structure | ||
1190 | * @return n/a | ||
1191 | */ | ||
1192 | void libertas_interrupt(struct net_device *dev) | ||
1193 | { | ||
1194 | wlan_private *priv = dev->priv; | ||
1195 | |||
1196 | ENTER(); | ||
1197 | |||
1198 | lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n", | ||
1199 | priv->adapter->intcounter); | ||
1200 | |||
1201 | priv->adapter->intcounter++; | ||
1202 | |||
1203 | if (priv->adapter->psstate == PS_STATE_SLEEP) { | ||
1204 | priv->adapter->psstate = PS_STATE_AWAKE; | ||
1205 | netif_wake_queue(dev); | ||
1206 | } | ||
1207 | |||
1208 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1209 | |||
1210 | LEAVE(); | ||
1211 | } | ||
1212 | |||
1213 | static int wlan_init_module(void) | ||
1214 | { | ||
1215 | int ret = 0; | ||
1216 | |||
1217 | ENTER(); | ||
1218 | |||
1219 | if (libertas_fw_name == NULL) { | ||
1220 | libertas_fw_name = default_fw_name; | ||
1221 | } | ||
1222 | |||
1223 | libertas_debugfs_init(); | ||
1224 | |||
1225 | if (libertas_sbi_register()) { | ||
1226 | ret = -1; | ||
1227 | libertas_debugfs_remove(); | ||
1228 | goto done; | ||
1229 | } | ||
1230 | |||
1231 | done: | ||
1232 | LEAVE(); | ||
1233 | return ret; | ||
1234 | } | ||
1235 | |||
1236 | static void wlan_cleanup_module(void) | ||
1237 | { | ||
1238 | int i; | ||
1239 | |||
1240 | ENTER(); | ||
1241 | |||
1242 | for (i = 0; i<libertas_found; i++) { | ||
1243 | wlan_private *priv = libertas_devs[i]->priv; | ||
1244 | reset_device(priv); | ||
1245 | } | ||
1246 | |||
1247 | libertas_sbi_unregister(); | ||
1248 | libertas_debugfs_remove(); | ||
1249 | |||
1250 | LEAVE(); | ||
1251 | } | ||
1252 | |||
1253 | module_init(wlan_init_module); | ||
1254 | module_exit(wlan_cleanup_module); | ||
1255 | |||
1256 | MODULE_DESCRIPTION("M-WLAN Driver"); | ||
1257 | MODULE_AUTHOR("Marvell International Ltd."); | ||
1258 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h new file mode 100644 index 000000000000..5d118f40cfbc --- /dev/null +++ b/drivers/net/wireless/libertas/radiotap.h | |||
@@ -0,0 +1,57 @@ | |||
1 | #include <net/ieee80211_radiotap.h> | ||
2 | |||
3 | struct tx_radiotap_hdr { | ||
4 | struct ieee80211_radiotap_header hdr; | ||
5 | u8 rate; | ||
6 | u8 txpower; | ||
7 | u8 rts_retries; | ||
8 | u8 data_retries; | ||
9 | #if 0 | ||
10 | u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12]; | ||
11 | #endif | ||
12 | } __attribute__ ((packed)); | ||
13 | |||
14 | #define TX_RADIOTAP_PRESENT ( \ | ||
15 | (1 << IEEE80211_RADIOTAP_RATE) | \ | ||
16 | (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ | ||
17 | (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ | ||
18 | (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ | ||
19 | 0) | ||
20 | |||
21 | #define IEEE80211_FC_VERSION_MASK 0x0003 | ||
22 | #define IEEE80211_FC_TYPE_MASK 0x000c | ||
23 | #define IEEE80211_FC_TYPE_MGT 0x0000 | ||
24 | #define IEEE80211_FC_TYPE_CTL 0x0004 | ||
25 | #define IEEE80211_FC_TYPE_DATA 0x0008 | ||
26 | #define IEEE80211_FC_SUBTYPE_MASK 0x00f0 | ||
27 | #define IEEE80211_FC_TOFROMDS_MASK 0x0300 | ||
28 | #define IEEE80211_FC_TODS_MASK 0x0100 | ||
29 | #define IEEE80211_FC_FROMDS_MASK 0x0200 | ||
30 | #define IEEE80211_FC_NODS 0x0000 | ||
31 | #define IEEE80211_FC_TODS 0x0100 | ||
32 | #define IEEE80211_FC_FROMDS 0x0200 | ||
33 | #define IEEE80211_FC_DSTODS 0x0300 | ||
34 | |||
35 | struct rx_radiotap_hdr { | ||
36 | struct ieee80211_radiotap_header hdr; | ||
37 | u8 flags; | ||
38 | u8 rate; | ||
39 | u16 chan_freq; | ||
40 | u16 chan_flags; | ||
41 | u8 antenna; | ||
42 | u8 antsignal; | ||
43 | u16 rx_flags; | ||
44 | #if 0 | ||
45 | u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; | ||
46 | #endif | ||
47 | } __attribute__ ((packed)); | ||
48 | |||
49 | #define RX_RADIOTAP_PRESENT ( \ | ||
50 | (1 << IEEE80211_RADIOTAP_FLAGS) | \ | ||
51 | (1 << IEEE80211_RADIOTAP_RATE) | \ | ||
52 | (1 << IEEE80211_RADIOTAP_CHANNEL) | \ | ||
53 | (1 << IEEE80211_RADIOTAP_ANTENNA) | \ | ||
54 | (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ | ||
55 | (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ | ||
56 | 0) | ||
57 | |||
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c new file mode 100644 index 000000000000..7e3f78f092dc --- /dev/null +++ b/drivers/net/wireless/libertas/rx.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /** | ||
2 | * This file contains the handling of RX in wlan driver. | ||
3 | */ | ||
4 | #include <linux/etherdevice.h> | ||
5 | #include <linux/types.h> | ||
6 | |||
7 | #include "hostcmd.h" | ||
8 | #include "radiotap.h" | ||
9 | #include "decl.h" | ||
10 | #include "dev.h" | ||
11 | #include "wext.h" | ||
12 | |||
13 | struct eth803hdr { | ||
14 | u8 dest_addr[6]; | ||
15 | u8 src_addr[6]; | ||
16 | u16 h803_len; | ||
17 | } __attribute__ ((packed)); | ||
18 | |||
19 | struct rfc1042hdr { | ||
20 | u8 llc_dsap; | ||
21 | u8 llc_ssap; | ||
22 | u8 llc_ctrl; | ||
23 | u8 snap_oui[3]; | ||
24 | u16 snap_type; | ||
25 | } __attribute__ ((packed)); | ||
26 | |||
27 | struct rxpackethdr { | ||
28 | struct rxpd rx_pd; | ||
29 | struct eth803hdr eth803_hdr; | ||
30 | struct rfc1042hdr rfc1042_hdr; | ||
31 | } __attribute__ ((packed)); | ||
32 | |||
33 | struct rx80211packethdr { | ||
34 | struct rxpd rx_pd; | ||
35 | void *eth80211_hdr; | ||
36 | } __attribute__ ((packed)); | ||
37 | |||
38 | static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb); | ||
39 | |||
40 | /** | ||
41 | * @brief This function computes the avgSNR . | ||
42 | * | ||
43 | * @param priv A pointer to wlan_private structure | ||
44 | * @return avgSNR | ||
45 | */ | ||
46 | static u8 wlan_getavgsnr(wlan_private * priv) | ||
47 | { | ||
48 | u8 i; | ||
49 | u16 temp = 0; | ||
50 | wlan_adapter *adapter = priv->adapter; | ||
51 | if (adapter->numSNRNF == 0) | ||
52 | return 0; | ||
53 | for (i = 0; i < adapter->numSNRNF; i++) | ||
54 | temp += adapter->rawSNR[i]; | ||
55 | return (u8) (temp / adapter->numSNRNF); | ||
56 | |||
57 | } | ||
58 | |||
59 | /** | ||
60 | * @brief This function computes the AvgNF | ||
61 | * | ||
62 | * @param priv A pointer to wlan_private structure | ||
63 | * @return AvgNF | ||
64 | */ | ||
65 | static u8 wlan_getavgnf(wlan_private * priv) | ||
66 | { | ||
67 | u8 i; | ||
68 | u16 temp = 0; | ||
69 | wlan_adapter *adapter = priv->adapter; | ||
70 | if (adapter->numSNRNF == 0) | ||
71 | return 0; | ||
72 | for (i = 0; i < adapter->numSNRNF; i++) | ||
73 | temp += adapter->rawNF[i]; | ||
74 | return (u8) (temp / adapter->numSNRNF); | ||
75 | |||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @brief This function save the raw SNR/NF to our internel buffer | ||
80 | * | ||
81 | * @param priv A pointer to wlan_private structure | ||
82 | * @param prxpd A pointer to rxpd structure of received packet | ||
83 | * @return n/a | ||
84 | */ | ||
85 | static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd) | ||
86 | { | ||
87 | wlan_adapter *adapter = priv->adapter; | ||
88 | if (adapter->numSNRNF < adapter->data_avg_factor) | ||
89 | adapter->numSNRNF++; | ||
90 | adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr; | ||
91 | adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf; | ||
92 | adapter->nextSNRNF++; | ||
93 | if (adapter->nextSNRNF >= adapter->data_avg_factor) | ||
94 | adapter->nextSNRNF = 0; | ||
95 | return; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * @brief This function computes the RSSI in received packet. | ||
100 | * | ||
101 | * @param priv A pointer to wlan_private structure | ||
102 | * @param prxpd A pointer to rxpd structure of received packet | ||
103 | * @return n/a | ||
104 | */ | ||
105 | static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd) | ||
106 | { | ||
107 | wlan_adapter *adapter = priv->adapter; | ||
108 | |||
109 | ENTER(); | ||
110 | |||
111 | lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf); | ||
112 | lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n", | ||
113 | adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
114 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
115 | |||
116 | adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr; | ||
117 | adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf; | ||
118 | wlan_save_rawSNRNF(priv, p_rx_pd); | ||
119 | |||
120 | adapter->rxpd_rate = p_rx_pd->rx_rate; | ||
121 | |||
122 | adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE; | ||
123 | adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE; | ||
124 | lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n", | ||
125 | adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
126 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
127 | |||
128 | adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] = | ||
129 | CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], | ||
130 | adapter->NF[TYPE_RXPD][TYPE_NOAVG]); | ||
131 | |||
132 | adapter->RSSI[TYPE_RXPD][TYPE_AVG] = | ||
133 | CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
134 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
135 | |||
136 | LEAVE(); | ||
137 | } | ||
138 | |||
139 | int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb) | ||
140 | { | ||
141 | lbs_pr_debug(1, "skb->data=%p\n", skb->data); | ||
142 | |||
143 | if(IS_MESH_FRAME(skb)) | ||
144 | skb->dev = priv->mesh_dev; | ||
145 | else | ||
146 | skb->dev = priv->wlan_dev.netdev; | ||
147 | skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev); | ||
148 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
149 | |||
150 | netif_rx(skb); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * @brief This function processes received packet and forwards it | ||
157 | * to kernel/upper layer | ||
158 | * | ||
159 | * @param priv A pointer to wlan_private | ||
160 | * @param skb A pointer to skb which includes the received packet | ||
161 | * @return 0 or -1 | ||
162 | */ | ||
163 | int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb) | ||
164 | { | ||
165 | wlan_adapter *adapter = priv->adapter; | ||
166 | int ret = 0; | ||
167 | |||
168 | struct rxpackethdr *p_rx_pkt; | ||
169 | struct rxpd *p_rx_pd; | ||
170 | |||
171 | int hdrchop; | ||
172 | struct ethhdr *p_ethhdr; | ||
173 | |||
174 | const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; | ||
175 | |||
176 | ENTER(); | ||
177 | |||
178 | if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH) | ||
179 | lbs_dbg_hex("RX packet: ", skb->data, | ||
180 | min_t(unsigned int, skb->len, 100)); | ||
181 | |||
182 | if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) | ||
183 | return process_rxed_802_11_packet(priv, skb); | ||
184 | |||
185 | p_rx_pkt = (struct rxpackethdr *) skb->data; | ||
186 | p_rx_pd = &p_rx_pkt->rx_pd; | ||
187 | if (p_rx_pd->rx_control & RxPD_MESH_FRAME) | ||
188 | SET_MESH_FRAME(skb); | ||
189 | else | ||
190 | UNSET_MESH_FRAME(skb); | ||
191 | |||
192 | lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, | ||
193 | min_t(unsigned int, skb->len, 100)); | ||
194 | |||
195 | if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { | ||
196 | lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); | ||
197 | priv->stats.rx_length_errors++; | ||
198 | ret = 0; | ||
199 | goto done; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * Check rxpd status and update 802.3 stat, | ||
204 | */ | ||
205 | if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) { | ||
206 | lbs_pr_debug(1, "RX error: frame received with bad status\n"); | ||
207 | lbs_pr_alert("rxpd Not OK\n"); | ||
208 | priv->stats.rx_errors++; | ||
209 | ret = 0; | ||
210 | goto done; | ||
211 | } | ||
212 | |||
213 | lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", | ||
214 | skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); | ||
215 | |||
216 | lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, | ||
217 | sizeof(p_rx_pkt->eth803_hdr.dest_addr)); | ||
218 | lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, | ||
219 | sizeof(p_rx_pkt->eth803_hdr.src_addr)); | ||
220 | |||
221 | if (memcmp(&p_rx_pkt->rfc1042_hdr, | ||
222 | rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { | ||
223 | /* | ||
224 | * Replace the 803 header and rfc1042 header (llc/snap) with an | ||
225 | * EthernetII header, keep the src/dst and snap_type (ethertype) | ||
226 | * | ||
227 | * The firmware only passes up SNAP frames converting | ||
228 | * all RX Data from 802.11 to 802.2/LLC/SNAP frames. | ||
229 | * | ||
230 | * To create the Ethernet II, just move the src, dst address right | ||
231 | * before the snap_type. | ||
232 | */ | ||
233 | p_ethhdr = (struct ethhdr *) | ||
234 | ((u8 *) & p_rx_pkt->eth803_hdr | ||
235 | + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) | ||
236 | - sizeof(p_rx_pkt->eth803_hdr.dest_addr) | ||
237 | - sizeof(p_rx_pkt->eth803_hdr.src_addr) | ||
238 | - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); | ||
239 | |||
240 | memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, | ||
241 | sizeof(p_ethhdr->h_source)); | ||
242 | memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, | ||
243 | sizeof(p_ethhdr->h_dest)); | ||
244 | |||
245 | /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header | ||
246 | * that was removed | ||
247 | */ | ||
248 | hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt; | ||
249 | } else { | ||
250 | lbs_dbg_hex("RX Data: LLC/SNAP", | ||
251 | (u8 *) & p_rx_pkt->rfc1042_hdr, | ||
252 | sizeof(p_rx_pkt->rfc1042_hdr)); | ||
253 | |||
254 | /* Chop off the rxpd */ | ||
255 | hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt; | ||
256 | } | ||
257 | |||
258 | /* Chop off the leading header bytes so the skb points to the start of | ||
259 | * either the reconstructed EthII frame or the 802.2/llc/snap frame | ||
260 | */ | ||
261 | skb_pull(skb, hdrchop); | ||
262 | |||
263 | /* Take the data rate from the rxpd structure | ||
264 | * only if the rate is auto | ||
265 | */ | ||
266 | if (adapter->is_datarate_auto) | ||
267 | adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate); | ||
268 | |||
269 | wlan_compute_rssi(priv, p_rx_pd); | ||
270 | |||
271 | lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); | ||
272 | if (libertas_upload_rx_packet(priv, skb)) { | ||
273 | lbs_pr_debug(1, "RX error: libertas_upload_rx_packet" | ||
274 | " returns failure\n"); | ||
275 | ret = -1; | ||
276 | goto done; | ||
277 | } | ||
278 | priv->stats.rx_bytes += skb->len; | ||
279 | priv->stats.rx_packets++; | ||
280 | |||
281 | ret = 0; | ||
282 | done: | ||
283 | LEAVE(); | ||
284 | |||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * @brief This function converts Tx/Rx rates from the Marvell WLAN format | ||
290 | * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) | ||
291 | * | ||
292 | * @param rate Input rate | ||
293 | * @return Output Rate (0 if invalid) | ||
294 | */ | ||
295 | static u8 convert_mv_rate_to_radiotap(u8 rate) | ||
296 | { | ||
297 | switch (rate) { | ||
298 | case 0: /* 1 Mbps */ | ||
299 | return 2; | ||
300 | case 1: /* 2 Mbps */ | ||
301 | return 4; | ||
302 | case 2: /* 5.5 Mbps */ | ||
303 | return 11; | ||
304 | case 3: /* 11 Mbps */ | ||
305 | return 22; | ||
306 | case 4: /* 6 Mbps */ | ||
307 | return 12; | ||
308 | case 5: /* 9 Mbps */ | ||
309 | return 18; | ||
310 | case 6: /* 12 Mbps */ | ||
311 | return 24; | ||
312 | case 7: /* 18 Mbps */ | ||
313 | return 36; | ||
314 | case 8: /* 24 Mbps */ | ||
315 | return 48; | ||
316 | case 9: /* 36 Mbps */ | ||
317 | return 72; | ||
318 | case 10: /* 48 Mbps */ | ||
319 | return 96; | ||
320 | case 11: /* 54 Mbps */ | ||
321 | return 108; | ||
322 | } | ||
323 | lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate); | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * @brief This function processes a received 802.11 packet and forwards it | ||
329 | * to kernel/upper layer | ||
330 | * | ||
331 | * @param priv A pointer to wlan_private | ||
332 | * @param skb A pointer to skb which includes the received packet | ||
333 | * @return 0 or -1 | ||
334 | */ | ||
335 | static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb) | ||
336 | { | ||
337 | wlan_adapter *adapter = priv->adapter; | ||
338 | int ret = 0; | ||
339 | |||
340 | struct rx80211packethdr *p_rx_pkt; | ||
341 | struct rxpd *prxpd; | ||
342 | struct rx_radiotap_hdr radiotap_hdr; | ||
343 | struct rx_radiotap_hdr *pradiotap_hdr; | ||
344 | |||
345 | ENTER(); | ||
346 | |||
347 | p_rx_pkt = (struct rx80211packethdr *) skb->data; | ||
348 | prxpd = &p_rx_pkt->rx_pd; | ||
349 | |||
350 | // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); | ||
351 | |||
352 | if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { | ||
353 | lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); | ||
354 | priv->stats.rx_length_errors++; | ||
355 | ret = 0; | ||
356 | goto done; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Check rxpd status and update 802.3 stat, | ||
361 | */ | ||
362 | if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) { | ||
363 | //lbs_pr_debug(1, "RX error: frame received with bad status\n"); | ||
364 | priv->stats.rx_errors++; | ||
365 | } | ||
366 | |||
367 | lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", | ||
368 | skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); | ||
369 | |||
370 | /* create the exported radio header */ | ||
371 | switch (priv->adapter->radiomode) { | ||
372 | case WLAN_RADIOMODE_NONE: | ||
373 | /* no radio header */ | ||
374 | /* chop the rxpd */ | ||
375 | skb_pull(skb, sizeof(struct rxpd)); | ||
376 | break; | ||
377 | |||
378 | case WLAN_RADIOMODE_RADIOTAP: | ||
379 | /* radiotap header */ | ||
380 | radiotap_hdr.hdr.it_version = 0; | ||
381 | /* XXX must check this value for pad */ | ||
382 | radiotap_hdr.hdr.it_pad = 0; | ||
383 | radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr); | ||
384 | radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT; | ||
385 | /* unknown values */ | ||
386 | radiotap_hdr.flags = 0; | ||
387 | radiotap_hdr.chan_freq = 0; | ||
388 | radiotap_hdr.chan_flags = 0; | ||
389 | radiotap_hdr.antenna = 0; | ||
390 | /* known values */ | ||
391 | radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); | ||
392 | /* XXX must check no carryout */ | ||
393 | radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; | ||
394 | radiotap_hdr.rx_flags = 0; | ||
395 | if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) | ||
396 | radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; | ||
397 | //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); | ||
398 | |||
399 | // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100)); | ||
400 | |||
401 | /* chop the rxpd */ | ||
402 | skb_pull(skb, sizeof(struct rxpd)); | ||
403 | |||
404 | /* add space for the new radio header */ | ||
405 | if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && | ||
406 | pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, | ||
407 | GFP_ATOMIC)) { | ||
408 | lbs_pr_alert( "%s: couldn't pskb_expand_head\n", | ||
409 | __func__); | ||
410 | } | ||
411 | |||
412 | pradiotap_hdr = | ||
413 | (struct rx_radiotap_hdr *)skb_push(skb, | ||
414 | sizeof(struct | ||
415 | rx_radiotap_hdr)); | ||
416 | memcpy(pradiotap_hdr, &radiotap_hdr, | ||
417 | sizeof(struct rx_radiotap_hdr)); | ||
418 | //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100)); | ||
419 | break; | ||
420 | |||
421 | default: | ||
422 | /* unknown header */ | ||
423 | lbs_pr_alert( "Unknown radiomode (%i)\n", | ||
424 | priv->adapter->radiomode); | ||
425 | /* don't export any header */ | ||
426 | /* chop the rxpd */ | ||
427 | skb_pull(skb, sizeof(struct rxpd)); | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | /* Take the data rate from the rxpd structure | ||
432 | * only if the rate is auto | ||
433 | */ | ||
434 | if (adapter->is_datarate_auto) { | ||
435 | adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate); | ||
436 | } | ||
437 | |||
438 | wlan_compute_rssi(priv, prxpd); | ||
439 | |||
440 | lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); | ||
441 | |||
442 | if (libertas_upload_rx_packet(priv, skb)) { | ||
443 | lbs_pr_debug(1, "RX error: libertas_upload_rx_packet " | ||
444 | "returns failure\n"); | ||
445 | ret = -1; | ||
446 | goto done; | ||
447 | } | ||
448 | |||
449 | priv->stats.rx_bytes += skb->len; | ||
450 | priv->stats.rx_packets++; | ||
451 | |||
452 | ret = 0; | ||
453 | done: | ||
454 | LEAVE(); | ||
455 | |||
456 | skb->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */ | ||
457 | |||
458 | return (ret); | ||
459 | } | ||
diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h new file mode 100644 index 000000000000..59d3a59ccef0 --- /dev/null +++ b/drivers/net/wireless/libertas/sbi.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /** | ||
2 | * This file contains IF layer definitions. | ||
3 | */ | ||
4 | |||
5 | #ifndef _SBI_H_ | ||
6 | #define _SBI_H_ | ||
7 | |||
8 | #include <linux/interrupt.h> | ||
9 | |||
10 | #include "defs.h" | ||
11 | |||
12 | /** INT status Bit Definition*/ | ||
13 | #define his_cmddnldrdy 0x01 | ||
14 | #define his_cardevent 0x02 | ||
15 | #define his_cmdupldrdy 0x04 | ||
16 | |||
17 | #ifndef DEV_NAME_LEN | ||
18 | #define DEV_NAME_LEN 32 | ||
19 | #endif | ||
20 | |||
21 | #define SBI_EVENT_CAUSE_SHIFT 3 | ||
22 | |||
23 | /* Probe and Check if the card is present*/ | ||
24 | int libertas_sbi_register_dev(wlan_private * priv); | ||
25 | int libertas_sbi_unregister_dev(wlan_private *); | ||
26 | int libertas_sbi_get_int_status(wlan_private * priv, u8 *); | ||
27 | int libertas_sbi_register(void); | ||
28 | void libertas_sbi_unregister(void); | ||
29 | int libertas_sbi_prog_firmware(wlan_private *); | ||
30 | |||
31 | int libertas_sbi_read_event_cause(wlan_private *); | ||
32 | int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb); | ||
33 | wlan_private *libertas_sbi_get_priv(void *card); | ||
34 | |||
35 | #ifdef ENABLE_PM | ||
36 | int libertas_sbi_suspend(wlan_private *); | ||
37 | int libertas_sbi_resume(wlan_private *); | ||
38 | #endif | ||
39 | |||
40 | #endif /* _SBI_H */ | ||
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c new file mode 100644 index 000000000000..e18706238951 --- /dev/null +++ b/drivers/net/wireless/libertas/scan.c | |||
@@ -0,0 +1,2044 @@ | |||
1 | /* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ | ||
2 | /* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ | ||
3 | |||
4 | /** | ||
5 | * Functions implementing wlan scan IOCTL and firmware command APIs | ||
6 | * | ||
7 | * IOCTL handlers as well as command preperation and response routines | ||
8 | * for sending scan commands to the firmware. | ||
9 | */ | ||
10 | #include <linux/ctype.h> | ||
11 | #include <linux/if.h> | ||
12 | #include <linux/netdevice.h> | ||
13 | #include <linux/wireless.h> | ||
14 | |||
15 | #include <net/ieee80211.h> | ||
16 | #include <net/iw_handler.h> | ||
17 | |||
18 | #include "host.h" | ||
19 | #include "decl.h" | ||
20 | #include "dev.h" | ||
21 | #include "scan.h" | ||
22 | |||
23 | //! Approximate amount of data needed to pass a scan result back to iwlist | ||
24 | #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ | ||
25 | + IW_ESSID_MAX_SIZE \ | ||
26 | + IW_EV_UINT_LEN \ | ||
27 | + IW_EV_FREQ_LEN \ | ||
28 | + IW_EV_QUAL_LEN \ | ||
29 | + IW_ESSID_MAX_SIZE \ | ||
30 | + IW_EV_PARAM_LEN \ | ||
31 | + 40) /* 40 for WPAIE */ | ||
32 | |||
33 | //! Memory needed to store a max sized channel List TLV for a firmware scan | ||
34 | #define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ | ||
35 | + (MRVDRV_MAX_CHANNELS_PER_SCAN \ | ||
36 | * sizeof(struct chanscanparamset))) | ||
37 | |||
38 | //! Memory needed to store a max number/size SSID TLV for a firmware scan | ||
39 | #define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) | ||
40 | |||
41 | //! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max | ||
42 | #define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ | ||
43 | + sizeof(struct mrvlietypes_numprobes) \ | ||
44 | + CHAN_TLV_MAX_SIZE \ | ||
45 | + SSID_TLV_MAX_SIZE) | ||
46 | |||
47 | //! The maximum number of channels the firmware can scan per command | ||
48 | #define MRVDRV_MAX_CHANNELS_PER_SCAN 14 | ||
49 | |||
50 | /** | ||
51 | * @brief Number of channels to scan per firmware scan command issuance. | ||
52 | * | ||
53 | * Number restricted to prevent hitting the limit on the amount of scan data | ||
54 | * returned in a single firmware scan command. | ||
55 | */ | ||
56 | #define MRVDRV_CHANNELS_PER_SCAN_CMD 4 | ||
57 | |||
58 | //! Scan time specified in the channel TLV for each channel for passive scans | ||
59 | #define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 | ||
60 | |||
61 | //! Scan time specified in the channel TLV for each channel for active scans | ||
62 | #define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 | ||
63 | |||
64 | //! Macro to enable/disable SSID checking before storing a scan table | ||
65 | #ifdef DISCARD_BAD_SSID | ||
66 | #define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) | ||
67 | #else | ||
68 | #define CHECK_SSID_IS_VALID(x) 1 | ||
69 | #endif | ||
70 | |||
71 | /** | ||
72 | * @brief Check if a scanned network compatible with the driver settings | ||
73 | * | ||
74 | * WEP WPA WPA2 ad-hoc encrypt Network | ||
75 | * enabled enabled enabled AES mode privacy WPA WPA2 Compatible | ||
76 | * 0 0 0 0 NONE 0 0 0 yes No security | ||
77 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | ||
78 | * 0 1 0 0 x 1x 1 x yes WPA | ||
79 | * 0 0 1 0 x 1x x 1 yes WPA2 | ||
80 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | ||
81 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | ||
82 | * | ||
83 | * | ||
84 | * @param adapter A pointer to wlan_adapter | ||
85 | * @param index Index in scantable to check against current driver settings | ||
86 | * @param mode Network mode: Infrastructure or IBSS | ||
87 | * | ||
88 | * @return Index in scantable, or error code if negative | ||
89 | */ | ||
90 | static int is_network_compatible(wlan_adapter * adapter, int index, int mode) | ||
91 | { | ||
92 | ENTER(); | ||
93 | |||
94 | if (adapter->scantable[index].inframode == mode) { | ||
95 | if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | ||
96 | && !adapter->secinfo.WPAenabled | ||
97 | && !adapter->secinfo.WPA2enabled | ||
98 | && adapter->scantable[index].wpa_supplicant.wpa_ie[0] != | ||
99 | WPA_IE | ||
100 | && adapter->scantable[index].wpa2_supplicant.wpa_ie[0] != | ||
101 | WPA2_IE && adapter->secinfo.Encryptionmode == CIPHER_NONE | ||
102 | && !adapter->scantable[index].privacy) { | ||
103 | /* no security */ | ||
104 | LEAVE(); | ||
105 | return index; | ||
106 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled | ||
107 | && !adapter->secinfo.WPAenabled | ||
108 | && !adapter->secinfo.WPA2enabled | ||
109 | && adapter->scantable[index].privacy) { | ||
110 | /* static WEP enabled */ | ||
111 | LEAVE(); | ||
112 | return index; | ||
113 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | ||
114 | && adapter->secinfo.WPAenabled | ||
115 | && !adapter->secinfo.WPA2enabled | ||
116 | && (adapter->scantable[index].wpa_supplicant. | ||
117 | wpa_ie[0] | ||
118 | == WPA_IE) | ||
119 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G | ||
120 | && adapter->scantable[index].privacy */ | ||
121 | ) { | ||
122 | /* WPA enabled */ | ||
123 | lbs_pr_debug(1, | ||
124 | "is_network_compatible() WPA: index=%d wpa_ie=%#x " | ||
125 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " | ||
126 | "privacy=%#x\n", index, | ||
127 | adapter->scantable[index].wpa_supplicant. | ||
128 | wpa_ie[0], | ||
129 | adapter->scantable[index].wpa2_supplicant. | ||
130 | wpa_ie[0], | ||
131 | (adapter->secinfo.WEPstatus == | ||
132 | wlan802_11WEPenabled) ? "e" : "d", | ||
133 | (adapter->secinfo.WPAenabled) ? "e" : "d", | ||
134 | (adapter->secinfo.WPA2enabled) ? "e" : "d", | ||
135 | adapter->secinfo.Encryptionmode, | ||
136 | adapter->scantable[index].privacy); | ||
137 | LEAVE(); | ||
138 | return index; | ||
139 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | ||
140 | && !adapter->secinfo.WPAenabled | ||
141 | && adapter->secinfo.WPA2enabled | ||
142 | && (adapter->scantable[index].wpa2_supplicant. | ||
143 | wpa_ie[0] | ||
144 | == WPA2_IE) | ||
145 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G | ||
146 | && adapter->scantable[index].privacy */ | ||
147 | ) { | ||
148 | /* WPA2 enabled */ | ||
149 | lbs_pr_debug(1, | ||
150 | "is_network_compatible() WPA2: index=%d wpa_ie=%#x " | ||
151 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " | ||
152 | "privacy=%#x\n", index, | ||
153 | adapter->scantable[index].wpa_supplicant. | ||
154 | wpa_ie[0], | ||
155 | adapter->scantable[index].wpa2_supplicant. | ||
156 | wpa_ie[0], | ||
157 | (adapter->secinfo.WEPstatus == | ||
158 | wlan802_11WEPenabled) ? "e" : "d", | ||
159 | (adapter->secinfo.WPAenabled) ? "e" : "d", | ||
160 | (adapter->secinfo.WPA2enabled) ? "e" : "d", | ||
161 | adapter->secinfo.Encryptionmode, | ||
162 | adapter->scantable[index].privacy); | ||
163 | LEAVE(); | ||
164 | return index; | ||
165 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | ||
166 | && !adapter->secinfo.WPAenabled | ||
167 | && !adapter->secinfo.WPA2enabled | ||
168 | && (adapter->scantable[index].wpa_supplicant. | ||
169 | wpa_ie[0] | ||
170 | != WPA_IE) | ||
171 | && (adapter->scantable[index].wpa2_supplicant. | ||
172 | wpa_ie[0] | ||
173 | != WPA2_IE) | ||
174 | && adapter->secinfo.Encryptionmode != CIPHER_NONE | ||
175 | && adapter->scantable[index].privacy) { | ||
176 | /* dynamic WEP enabled */ | ||
177 | lbs_pr_debug(1, | ||
178 | "is_network_compatible() dynamic WEP: index=%d " | ||
179 | "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n", | ||
180 | index, | ||
181 | adapter->scantable[index].wpa_supplicant. | ||
182 | wpa_ie[0], | ||
183 | adapter->scantable[index].wpa2_supplicant. | ||
184 | wpa_ie[0], adapter->secinfo.Encryptionmode, | ||
185 | adapter->scantable[index].privacy); | ||
186 | LEAVE(); | ||
187 | return index; | ||
188 | } | ||
189 | |||
190 | /* security doesn't match */ | ||
191 | lbs_pr_debug(1, | ||
192 | "is_network_compatible() FAILED: index=%d wpa_ie=%#x " | ||
193 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n", | ||
194 | index, | ||
195 | adapter->scantable[index].wpa_supplicant.wpa_ie[0], | ||
196 | adapter->scantable[index].wpa2_supplicant.wpa_ie[0], | ||
197 | (adapter->secinfo.WEPstatus == | ||
198 | wlan802_11WEPenabled) ? "e" : "d", | ||
199 | (adapter->secinfo.WPAenabled) ? "e" : "d", | ||
200 | (adapter->secinfo.WPA2enabled) ? "e" : "d", | ||
201 | adapter->secinfo.Encryptionmode, | ||
202 | adapter->scantable[index].privacy); | ||
203 | LEAVE(); | ||
204 | return -ECONNREFUSED; | ||
205 | } | ||
206 | |||
207 | /* mode doesn't match */ | ||
208 | LEAVE(); | ||
209 | return -ENETUNREACH; | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * @brief This function validates a SSID as being able to be printed | ||
214 | * | ||
215 | * @param pssid SSID structure to validate | ||
216 | * | ||
217 | * @return TRUE or FALSE | ||
218 | */ | ||
219 | static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) | ||
220 | { | ||
221 | int ssididx; | ||
222 | |||
223 | for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { | ||
224 | if (!isprint(pssid->ssid[ssididx])) { | ||
225 | return 0; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return 1; | ||
230 | } | ||
231 | |||
232 | /** | ||
233 | * @brief Post process the scan table after a new scan command has completed | ||
234 | * | ||
235 | * Inspect each entry of the scan table and try to find an entry that | ||
236 | * matches our current associated/joined network from the scan. If | ||
237 | * one is found, update the stored copy of the bssdescriptor for our | ||
238 | * current network. | ||
239 | * | ||
240 | * Debug dump the current scan table contents if compiled accordingly. | ||
241 | * | ||
242 | * @param priv A pointer to wlan_private structure | ||
243 | * | ||
244 | * @return void | ||
245 | */ | ||
246 | static void wlan_scan_process_results(wlan_private * priv) | ||
247 | { | ||
248 | wlan_adapter *adapter = priv->adapter; | ||
249 | int foundcurrent; | ||
250 | int i; | ||
251 | |||
252 | foundcurrent = 0; | ||
253 | |||
254 | if (adapter->connect_status == libertas_connected) { | ||
255 | /* try to find the current BSSID in the new scan list */ | ||
256 | for (i = 0; i < adapter->numinscantable; i++) { | ||
257 | if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, | ||
258 | &adapter->curbssparams.ssid) && | ||
259 | !memcmp(adapter->curbssparams.bssid, | ||
260 | adapter->scantable[i].macaddress, | ||
261 | ETH_ALEN)) { | ||
262 | foundcurrent = 1; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | if (foundcurrent) { | ||
267 | /* Make a copy of current BSSID descriptor */ | ||
268 | memcpy(&adapter->curbssparams.bssdescriptor, | ||
269 | &adapter->scantable[i], | ||
270 | sizeof(adapter->curbssparams.bssdescriptor)); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | for (i = 0; i < adapter->numinscantable; i++) { | ||
275 | lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " | ||
276 | "RSSI[%03d], SSID[%s]\n", | ||
277 | i, | ||
278 | adapter->scantable[i].macaddress[0], | ||
279 | adapter->scantable[i].macaddress[1], | ||
280 | adapter->scantable[i].macaddress[2], | ||
281 | adapter->scantable[i].macaddress[3], | ||
282 | adapter->scantable[i].macaddress[4], | ||
283 | adapter->scantable[i].macaddress[5], | ||
284 | (s32) adapter->scantable[i].rssi, | ||
285 | adapter->scantable[i].ssid.ssid); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * @brief Create a channel list for the driver to scan based on region info | ||
291 | * | ||
292 | * Use the driver region/band information to construct a comprehensive list | ||
293 | * of channels to scan. This routine is used for any scan that is not | ||
294 | * provided a specific channel list to scan. | ||
295 | * | ||
296 | * @param priv A pointer to wlan_private structure | ||
297 | * @param scanchanlist Output parameter: resulting channel list to scan | ||
298 | * @param filteredscan Flag indicating whether or not a BSSID or SSID filter | ||
299 | * is being sent in the command to firmware. Used to | ||
300 | * increase the number of channels sent in a scan | ||
301 | * command and to disable the firmware channel scan | ||
302 | * filter. | ||
303 | * | ||
304 | * @return void | ||
305 | */ | ||
306 | static void wlan_scan_create_channel_list(wlan_private * priv, | ||
307 | struct chanscanparamset * scanchanlist, | ||
308 | u8 filteredscan) | ||
309 | { | ||
310 | |||
311 | wlan_adapter *adapter = priv->adapter; | ||
312 | struct region_channel *scanregion; | ||
313 | struct chan_freq_power *cfp; | ||
314 | int rgnidx; | ||
315 | int chanidx; | ||
316 | int nextchan; | ||
317 | u8 scantype; | ||
318 | |||
319 | chanidx = 0; | ||
320 | |||
321 | /* Set the default scan type to the user specified type, will later | ||
322 | * be changed to passive on a per channel basis if restricted by | ||
323 | * regulatory requirements (11d or 11h) | ||
324 | */ | ||
325 | scantype = adapter->scantype; | ||
326 | |||
327 | for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { | ||
328 | if (priv->adapter->enable11d && | ||
329 | adapter->connect_status != libertas_connected) { | ||
330 | /* Scan all the supported chan for the first scan */ | ||
331 | if (!adapter->universal_channel[rgnidx].valid) | ||
332 | continue; | ||
333 | scanregion = &adapter->universal_channel[rgnidx]; | ||
334 | |||
335 | /* clear the parsed_region_chan for the first scan */ | ||
336 | memset(&adapter->parsed_region_chan, 0x00, | ||
337 | sizeof(adapter->parsed_region_chan)); | ||
338 | } else { | ||
339 | if (!adapter->region_channel[rgnidx].valid) | ||
340 | continue; | ||
341 | scanregion = &adapter->region_channel[rgnidx]; | ||
342 | } | ||
343 | |||
344 | for (nextchan = 0; | ||
345 | nextchan < scanregion->nrcfp; nextchan++, chanidx++) { | ||
346 | |||
347 | cfp = scanregion->CFP + nextchan; | ||
348 | |||
349 | if (priv->adapter->enable11d) { | ||
350 | scantype = | ||
351 | libertas_get_scan_type_11d(cfp->channel, | ||
352 | &adapter-> | ||
353 | parsed_region_chan); | ||
354 | } | ||
355 | |||
356 | switch (scanregion->band) { | ||
357 | case BAND_B: | ||
358 | case BAND_G: | ||
359 | default: | ||
360 | scanchanlist[chanidx].radiotype = | ||
361 | cmd_scan_radio_type_bg; | ||
362 | break; | ||
363 | } | ||
364 | |||
365 | if (scantype == cmd_scan_type_passive) { | ||
366 | scanchanlist[chanidx].maxscantime = | ||
367 | cpu_to_le16 | ||
368 | (MRVDRV_PASSIVE_SCAN_CHAN_TIME); | ||
369 | scanchanlist[chanidx].chanscanmode.passivescan = | ||
370 | 1; | ||
371 | } else { | ||
372 | scanchanlist[chanidx].maxscantime = | ||
373 | cpu_to_le16 | ||
374 | (MRVDRV_ACTIVE_SCAN_CHAN_TIME); | ||
375 | scanchanlist[chanidx].chanscanmode.passivescan = | ||
376 | 0; | ||
377 | } | ||
378 | |||
379 | scanchanlist[chanidx].channumber = cfp->channel; | ||
380 | |||
381 | if (filteredscan) { | ||
382 | scanchanlist[chanidx].chanscanmode. | ||
383 | disablechanfilt = 1; | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds | ||
391 | * | ||
392 | * Application layer or other functions can invoke wlan_scan_networks | ||
393 | * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. | ||
394 | * This structure is used as the basis of one or many wlan_scan_cmd_config | ||
395 | * commands that are sent to the command processing module and sent to | ||
396 | * firmware. | ||
397 | * | ||
398 | * Create a wlan_scan_cmd_config based on the following user supplied | ||
399 | * parameters (if present): | ||
400 | * - SSID filter | ||
401 | * - BSSID filter | ||
402 | * - Number of Probes to be sent | ||
403 | * - channel list | ||
404 | * | ||
405 | * If the SSID or BSSID filter is not present, disable/clear the filter. | ||
406 | * If the number of probes is not set, use the adapter default setting | ||
407 | * Qualify the channel | ||
408 | * | ||
409 | * @param priv A pointer to wlan_private structure | ||
410 | * @param puserscanin NULL or pointer to scan configuration parameters | ||
411 | * @param ppchantlvout Output parameter: Pointer to the start of the | ||
412 | * channel TLV portion of the output scan config | ||
413 | * @param pscanchanlist Output parameter: Pointer to the resulting channel | ||
414 | * list to scan | ||
415 | * @param pmaxchanperscan Output parameter: Number of channels to scan for | ||
416 | * each issuance of the firmware scan command | ||
417 | * @param pfilteredscan Output parameter: Flag indicating whether or not | ||
418 | * a BSSID or SSID filter is being sent in the | ||
419 | * command to firmware. Used to increase the number | ||
420 | * of channels sent in a scan command and to | ||
421 | * disable the firmware channel scan filter. | ||
422 | * @param pscancurrentonly Output parameter: Flag indicating whether or not | ||
423 | * we are only scanning our current active channel | ||
424 | * | ||
425 | * @return resulting scan configuration | ||
426 | */ | ||
427 | static struct wlan_scan_cmd_config * | ||
428 | wlan_scan_setup_scan_config(wlan_private * priv, | ||
429 | const struct wlan_ioctl_user_scan_cfg * puserscanin, | ||
430 | struct mrvlietypes_chanlistparamset ** ppchantlvout, | ||
431 | struct chanscanparamset * pscanchanlist, | ||
432 | int *pmaxchanperscan, | ||
433 | u8 * pfilteredscan, | ||
434 | u8 * pscancurrentonly) | ||
435 | { | ||
436 | wlan_adapter *adapter = priv->adapter; | ||
437 | const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
438 | struct mrvlietypes_numprobes *pnumprobestlv; | ||
439 | struct mrvlietypes_ssidparamset *pssidtlv; | ||
440 | struct wlan_scan_cmd_config * pscancfgout = NULL; | ||
441 | u8 *ptlvpos; | ||
442 | u16 numprobes; | ||
443 | u16 ssidlen; | ||
444 | int chanidx; | ||
445 | int scantype; | ||
446 | int scandur; | ||
447 | int channel; | ||
448 | int radiotype; | ||
449 | |||
450 | pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); | ||
451 | if (pscancfgout == NULL) | ||
452 | goto out; | ||
453 | |||
454 | /* The tlvbufferlen is calculated for each scan command. The TLVs added | ||
455 | * in this routine will be preserved since the routine that sends | ||
456 | * the command will append channelTLVs at *ppchantlvout. The difference | ||
457 | * between the *ppchantlvout and the tlvbuffer start will be used | ||
458 | * to calculate the size of anything we add in this routine. | ||
459 | */ | ||
460 | pscancfgout->tlvbufferlen = 0; | ||
461 | |||
462 | /* Running tlv pointer. Assigned to ppchantlvout at end of function | ||
463 | * so later routines know where channels can be added to the command buf | ||
464 | */ | ||
465 | ptlvpos = pscancfgout->tlvbuffer; | ||
466 | |||
467 | /* | ||
468 | * Set the initial scan paramters for progressive scanning. If a specific | ||
469 | * BSSID or SSID is used, the number of channels in the scan command | ||
470 | * will be increased to the absolute maximum | ||
471 | */ | ||
472 | *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; | ||
473 | |||
474 | /* Initialize the scan as un-filtered by firmware, set to TRUE below if | ||
475 | * a SSID or BSSID filter is sent in the command | ||
476 | */ | ||
477 | *pfilteredscan = 0; | ||
478 | |||
479 | /* Initialize the scan as not being only on the current channel. If | ||
480 | * the channel list is customized, only contains one channel, and | ||
481 | * is the active channel, this is set true and data flow is not halted. | ||
482 | */ | ||
483 | *pscancurrentonly = 0; | ||
484 | |||
485 | if (puserscanin) { | ||
486 | |||
487 | /* Set the bss type scan filter, use adapter setting if unset */ | ||
488 | pscancfgout->bsstype = | ||
489 | (puserscanin->bsstype ? puserscanin->bsstype : adapter-> | ||
490 | scanmode); | ||
491 | |||
492 | /* Set the number of probes to send, use adapter setting if unset */ | ||
493 | numprobes = (puserscanin->numprobes ? puserscanin->numprobes : | ||
494 | adapter->scanprobes); | ||
495 | |||
496 | /* | ||
497 | * Set the BSSID filter to the incoming configuration, | ||
498 | * if non-zero. If not set, it will remain disabled (all zeros). | ||
499 | */ | ||
500 | memcpy(pscancfgout->specificBSSID, | ||
501 | puserscanin->specificBSSID, | ||
502 | sizeof(pscancfgout->specificBSSID)); | ||
503 | |||
504 | ssidlen = strlen(puserscanin->specificSSID); | ||
505 | |||
506 | if (ssidlen) { | ||
507 | pssidtlv = | ||
508 | (struct mrvlietypes_ssidparamset *) pscancfgout-> | ||
509 | tlvbuffer; | ||
510 | pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); | ||
511 | pssidtlv->header.len = cpu_to_le16(ssidlen); | ||
512 | memcpy(pssidtlv->ssid, puserscanin->specificSSID, | ||
513 | ssidlen); | ||
514 | ptlvpos += sizeof(pssidtlv->header) + ssidlen; | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * The default number of channels sent in the command is low to | ||
519 | * ensure the response buffer from the firmware does not truncate | ||
520 | * scan results. That is not an issue with an SSID or BSSID | ||
521 | * filter applied to the scan results in the firmware. | ||
522 | */ | ||
523 | if (ssidlen || (memcmp(pscancfgout->specificBSSID, | ||
524 | &zeromac, sizeof(zeromac)) != 0)) { | ||
525 | *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; | ||
526 | *pfilteredscan = 1; | ||
527 | } | ||
528 | } else { | ||
529 | pscancfgout->bsstype = adapter->scanmode; | ||
530 | numprobes = adapter->scanprobes; | ||
531 | } | ||
532 | |||
533 | /* If the input config or adapter has the number of Probes set, add tlv */ | ||
534 | if (numprobes) { | ||
535 | pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; | ||
536 | pnumprobestlv->header.type = | ||
537 | cpu_to_le16(TLV_TYPE_NUMPROBES); | ||
538 | pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); | ||
539 | pnumprobestlv->numprobes = cpu_to_le16(numprobes); | ||
540 | |||
541 | ptlvpos += | ||
542 | sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; | ||
543 | |||
544 | pnumprobestlv->header.len = | ||
545 | cpu_to_le16(pnumprobestlv->header.len); | ||
546 | } | ||
547 | |||
548 | /* | ||
549 | * Set the output for the channel TLV to the address in the tlv buffer | ||
550 | * past any TLVs that were added in this fuction (SSID, numprobes). | ||
551 | * channel TLVs will be added past this for each scan command, preserving | ||
552 | * the TLVs that were previously added. | ||
553 | */ | ||
554 | *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; | ||
555 | |||
556 | if (puserscanin && puserscanin->chanlist[0].channumber) { | ||
557 | |||
558 | lbs_pr_debug(1, "Scan: Using supplied channel list\n"); | ||
559 | |||
560 | for (chanidx = 0; | ||
561 | chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX | ||
562 | && puserscanin->chanlist[chanidx].channumber; chanidx++) { | ||
563 | |||
564 | channel = puserscanin->chanlist[chanidx].channumber; | ||
565 | (pscanchanlist + chanidx)->channumber = channel; | ||
566 | |||
567 | radiotype = puserscanin->chanlist[chanidx].radiotype; | ||
568 | (pscanchanlist + chanidx)->radiotype = radiotype; | ||
569 | |||
570 | scantype = puserscanin->chanlist[chanidx].scantype; | ||
571 | |||
572 | if (scantype == cmd_scan_type_passive) { | ||
573 | (pscanchanlist + | ||
574 | chanidx)->chanscanmode.passivescan = 1; | ||
575 | } else { | ||
576 | (pscanchanlist + | ||
577 | chanidx)->chanscanmode.passivescan = 0; | ||
578 | } | ||
579 | |||
580 | if (puserscanin->chanlist[chanidx].scantime) { | ||
581 | scandur = | ||
582 | puserscanin->chanlist[chanidx].scantime; | ||
583 | } else { | ||
584 | if (scantype == cmd_scan_type_passive) { | ||
585 | scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; | ||
586 | } else { | ||
587 | scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; | ||
588 | } | ||
589 | } | ||
590 | |||
591 | (pscanchanlist + chanidx)->minscantime = | ||
592 | cpu_to_le16(scandur); | ||
593 | (pscanchanlist + chanidx)->maxscantime = | ||
594 | cpu_to_le16(scandur); | ||
595 | } | ||
596 | |||
597 | /* Check if we are only scanning the current channel */ | ||
598 | if ((chanidx == 1) && (puserscanin->chanlist[0].channumber | ||
599 | == | ||
600 | priv->adapter->curbssparams.channel)) { | ||
601 | *pscancurrentonly = 1; | ||
602 | lbs_pr_debug(1, "Scan: Scanning current channel only"); | ||
603 | } | ||
604 | |||
605 | } else { | ||
606 | lbs_pr_debug(1, "Scan: Creating full region channel list\n"); | ||
607 | wlan_scan_create_channel_list(priv, pscanchanlist, | ||
608 | *pfilteredscan); | ||
609 | } | ||
610 | |||
611 | out: | ||
612 | return pscancfgout; | ||
613 | } | ||
614 | |||
615 | /** | ||
616 | * @brief Construct and send multiple scan config commands to the firmware | ||
617 | * | ||
618 | * Previous routines have created a wlan_scan_cmd_config with any requested | ||
619 | * TLVs. This function splits the channel TLV into maxchanperscan lists | ||
620 | * and sends the portion of the channel TLV along with the other TLVs | ||
621 | * to the wlan_cmd routines for execution in the firmware. | ||
622 | * | ||
623 | * @param priv A pointer to wlan_private structure | ||
624 | * @param maxchanperscan Maximum number channels to be included in each | ||
625 | * scan command sent to firmware | ||
626 | * @param filteredscan Flag indicating whether or not a BSSID or SSID | ||
627 | * filter is being used for the firmware command | ||
628 | * scan command sent to firmware | ||
629 | * @param pscancfgout Scan configuration used for this scan. | ||
630 | * @param pchantlvout Pointer in the pscancfgout where the channel TLV | ||
631 | * should start. This is past any other TLVs that | ||
632 | * must be sent down in each firmware command. | ||
633 | * @param pscanchanlist List of channels to scan in maxchanperscan segments | ||
634 | * | ||
635 | * @return 0 or error return otherwise | ||
636 | */ | ||
637 | static int wlan_scan_channel_list(wlan_private * priv, | ||
638 | int maxchanperscan, | ||
639 | u8 filteredscan, | ||
640 | struct wlan_scan_cmd_config * pscancfgout, | ||
641 | struct mrvlietypes_chanlistparamset * pchantlvout, | ||
642 | struct chanscanparamset * pscanchanlist) | ||
643 | { | ||
644 | struct chanscanparamset *ptmpchan; | ||
645 | struct chanscanparamset *pstartchan; | ||
646 | u8 scanband; | ||
647 | int doneearly; | ||
648 | int tlvidx; | ||
649 | int ret = 0; | ||
650 | |||
651 | ENTER(); | ||
652 | |||
653 | if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { | ||
654 | lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", | ||
655 | pscancfgout, pchantlvout, pscanchanlist); | ||
656 | return -1; | ||
657 | } | ||
658 | |||
659 | pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
660 | |||
661 | /* Set the temp channel struct pointer to the start of the desired list */ | ||
662 | ptmpchan = pscanchanlist; | ||
663 | |||
664 | /* Loop through the desired channel list, sending a new firmware scan | ||
665 | * commands for each maxchanperscan channels (or for 1,6,11 individually | ||
666 | * if configured accordingly) | ||
667 | */ | ||
668 | while (ptmpchan->channumber) { | ||
669 | |||
670 | tlvidx = 0; | ||
671 | pchantlvout->header.len = 0; | ||
672 | scanband = ptmpchan->radiotype; | ||
673 | pstartchan = ptmpchan; | ||
674 | doneearly = 0; | ||
675 | |||
676 | /* Construct the channel TLV for the scan command. Continue to | ||
677 | * insert channel TLVs until: | ||
678 | * - the tlvidx hits the maximum configured per scan command | ||
679 | * - the next channel to insert is 0 (end of desired channel list) | ||
680 | * - doneearly is set (controlling individual scanning of 1,6,11) | ||
681 | */ | ||
682 | while (tlvidx < maxchanperscan && ptmpchan->channumber | ||
683 | && !doneearly) { | ||
684 | |||
685 | lbs_pr_debug(1, | ||
686 | "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", | ||
687 | ptmpchan->channumber, ptmpchan->radiotype, | ||
688 | ptmpchan->chanscanmode.passivescan, | ||
689 | ptmpchan->chanscanmode.disablechanfilt, | ||
690 | ptmpchan->maxscantime); | ||
691 | |||
692 | /* Copy the current channel TLV to the command being prepared */ | ||
693 | memcpy(pchantlvout->chanscanparam + tlvidx, | ||
694 | ptmpchan, sizeof(pchantlvout->chanscanparam)); | ||
695 | |||
696 | /* Increment the TLV header length by the size appended */ | ||
697 | pchantlvout->header.len += | ||
698 | sizeof(pchantlvout->chanscanparam); | ||
699 | |||
700 | /* | ||
701 | * The tlv buffer length is set to the number of bytes of the | ||
702 | * between the channel tlv pointer and the start of the | ||
703 | * tlv buffer. This compensates for any TLVs that were appended | ||
704 | * before the channel list. | ||
705 | */ | ||
706 | pscancfgout->tlvbufferlen = ((u8 *) pchantlvout | ||
707 | - pscancfgout->tlvbuffer); | ||
708 | |||
709 | /* Add the size of the channel tlv header and the data length */ | ||
710 | pscancfgout->tlvbufferlen += | ||
711 | (sizeof(pchantlvout->header) | ||
712 | + pchantlvout->header.len); | ||
713 | |||
714 | /* Increment the index to the channel tlv we are constructing */ | ||
715 | tlvidx++; | ||
716 | |||
717 | doneearly = 0; | ||
718 | |||
719 | /* Stop the loop if the *current* channel is in the 1,6,11 set | ||
720 | * and we are not filtering on a BSSID or SSID. | ||
721 | */ | ||
722 | if (!filteredscan && (ptmpchan->channumber == 1 | ||
723 | || ptmpchan->channumber == 6 | ||
724 | || ptmpchan->channumber == 11)) { | ||
725 | doneearly = 1; | ||
726 | } | ||
727 | |||
728 | /* Increment the tmp pointer to the next channel to be scanned */ | ||
729 | ptmpchan++; | ||
730 | |||
731 | /* Stop the loop if the *next* channel is in the 1,6,11 set. | ||
732 | * This will cause it to be the only channel scanned on the next | ||
733 | * interation | ||
734 | */ | ||
735 | if (!filteredscan && (ptmpchan->channumber == 1 | ||
736 | || ptmpchan->channumber == 6 | ||
737 | || ptmpchan->channumber == 11)) { | ||
738 | doneearly = 1; | ||
739 | } | ||
740 | } | ||
741 | |||
742 | /* Send the scan command to the firmware with the specified cfg */ | ||
743 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, | ||
744 | 0, 0, pscancfgout); | ||
745 | } | ||
746 | |||
747 | LEAVE(); | ||
748 | return ret; | ||
749 | } | ||
750 | |||
751 | /** | ||
752 | * @brief Internal function used to start a scan based on an input config | ||
753 | * | ||
754 | * Use the input user scan configuration information when provided in | ||
755 | * order to send the appropriate scan commands to firmware to populate or | ||
756 | * update the internal driver scan table | ||
757 | * | ||
758 | * @param priv A pointer to wlan_private structure | ||
759 | * @param puserscanin Pointer to the input configuration for the requested | ||
760 | * scan. | ||
761 | * | ||
762 | * @return 0 or < 0 if error | ||
763 | */ | ||
764 | int wlan_scan_networks(wlan_private * priv, | ||
765 | const struct wlan_ioctl_user_scan_cfg * puserscanin) | ||
766 | { | ||
767 | wlan_adapter *adapter = priv->adapter; | ||
768 | struct mrvlietypes_chanlistparamset *pchantlvout; | ||
769 | struct chanscanparamset * scan_chan_list = NULL; | ||
770 | struct wlan_scan_cmd_config * scan_cfg = NULL; | ||
771 | u8 keeppreviousscan; | ||
772 | u8 filteredscan; | ||
773 | u8 scancurrentchanonly; | ||
774 | int maxchanperscan; | ||
775 | int ret; | ||
776 | |||
777 | ENTER(); | ||
778 | |||
779 | scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * | ||
780 | WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); | ||
781 | if (scan_chan_list == NULL) { | ||
782 | ret = -ENOMEM; | ||
783 | goto out; | ||
784 | } | ||
785 | |||
786 | scan_cfg = wlan_scan_setup_scan_config(priv, | ||
787 | puserscanin, | ||
788 | &pchantlvout, | ||
789 | scan_chan_list, | ||
790 | &maxchanperscan, | ||
791 | &filteredscan, | ||
792 | &scancurrentchanonly); | ||
793 | if (scan_cfg == NULL) { | ||
794 | ret = -ENOMEM; | ||
795 | goto out; | ||
796 | } | ||
797 | |||
798 | keeppreviousscan = 0; | ||
799 | |||
800 | if (puserscanin) { | ||
801 | keeppreviousscan = puserscanin->keeppreviousscan; | ||
802 | } | ||
803 | |||
804 | if (!keeppreviousscan) { | ||
805 | memset(adapter->scantable, 0x00, | ||
806 | sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); | ||
807 | adapter->numinscantable = 0; | ||
808 | } | ||
809 | |||
810 | /* Keep the data path active if we are only scanning our current channel */ | ||
811 | if (!scancurrentchanonly) { | ||
812 | netif_stop_queue(priv->wlan_dev.netdev); | ||
813 | netif_carrier_off(priv->wlan_dev.netdev); | ||
814 | } | ||
815 | |||
816 | ret = wlan_scan_channel_list(priv, | ||
817 | maxchanperscan, | ||
818 | filteredscan, | ||
819 | scan_cfg, | ||
820 | pchantlvout, | ||
821 | scan_chan_list); | ||
822 | |||
823 | /* Process the resulting scan table: | ||
824 | * - Remove any bad ssids | ||
825 | * - Update our current BSS information from scan data | ||
826 | */ | ||
827 | wlan_scan_process_results(priv); | ||
828 | |||
829 | if (priv->adapter->connect_status == libertas_connected) { | ||
830 | netif_carrier_on(priv->wlan_dev.netdev); | ||
831 | netif_wake_queue(priv->wlan_dev.netdev); | ||
832 | } | ||
833 | |||
834 | out: | ||
835 | if (scan_cfg) | ||
836 | kfree(scan_cfg); | ||
837 | |||
838 | if (scan_chan_list) | ||
839 | kfree(scan_chan_list); | ||
840 | |||
841 | LEAVE(); | ||
842 | return ret; | ||
843 | } | ||
844 | |||
845 | /** | ||
846 | * @brief Inspect the scan response buffer for pointers to expected TLVs | ||
847 | * | ||
848 | * TLVs can be included at the end of the scan response BSS information. | ||
849 | * Parse the data in the buffer for pointers to TLVs that can potentially | ||
850 | * be passed back in the response | ||
851 | * | ||
852 | * @param ptlv Pointer to the start of the TLV buffer to parse | ||
853 | * @param tlvbufsize size of the TLV buffer | ||
854 | * @param ptsftlv Output parameter: Pointer to the TSF TLV if found | ||
855 | * | ||
856 | * @return void | ||
857 | */ | ||
858 | static | ||
859 | void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, | ||
860 | int tlvbufsize, | ||
861 | struct mrvlietypes_tsftimestamp ** ptsftlv) | ||
862 | { | ||
863 | struct mrvlietypes_data *pcurrenttlv; | ||
864 | int tlvbufleft; | ||
865 | u16 tlvtype; | ||
866 | u16 tlvlen; | ||
867 | |||
868 | pcurrenttlv = ptlv; | ||
869 | tlvbufleft = tlvbufsize; | ||
870 | *ptsftlv = NULL; | ||
871 | |||
872 | lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); | ||
873 | lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); | ||
874 | |||
875 | while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { | ||
876 | tlvtype = le16_to_cpu(pcurrenttlv->header.type); | ||
877 | tlvlen = le16_to_cpu(pcurrenttlv->header.len); | ||
878 | |||
879 | switch (tlvtype) { | ||
880 | case TLV_TYPE_TSFTIMESTAMP: | ||
881 | *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; | ||
882 | break; | ||
883 | |||
884 | default: | ||
885 | lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", | ||
886 | tlvtype); | ||
887 | /* Give up, this seems corrupted */ | ||
888 | return; | ||
889 | } /* switch */ | ||
890 | |||
891 | tlvbufleft -= (sizeof(ptlv->header) + tlvlen); | ||
892 | pcurrenttlv = | ||
893 | (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); | ||
894 | } /* while */ | ||
895 | } | ||
896 | |||
897 | /** | ||
898 | * @brief Interpret a BSS scan response returned from the firmware | ||
899 | * | ||
900 | * Parse the various fixed fields and IEs passed back for a a BSS probe | ||
901 | * response or beacon from the scan command. Record information as needed | ||
902 | * in the scan table struct bss_descriptor for that entry. | ||
903 | * | ||
904 | * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry | ||
905 | * | ||
906 | * @return 0 or -1 | ||
907 | */ | ||
908 | static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, | ||
909 | u8 ** pbeaconinfo, int *bytesleft) | ||
910 | { | ||
911 | enum ieeetypes_elementid elemID; | ||
912 | struct ieeetypes_fhparamset *pFH; | ||
913 | struct ieeetypes_dsparamset *pDS; | ||
914 | struct ieeetypes_cfparamset *pCF; | ||
915 | struct ieeetypes_ibssparamset *pibss; | ||
916 | struct ieeetypes_capinfo *pcap; | ||
917 | struct WLAN_802_11_FIXED_IEs fixedie; | ||
918 | u8 *pcurrentptr; | ||
919 | u8 *pRate; | ||
920 | u8 elemlen; | ||
921 | u8 bytestocopy; | ||
922 | u8 ratesize; | ||
923 | u16 beaconsize; | ||
924 | u8 founddatarateie; | ||
925 | int bytesleftforcurrentbeacon; | ||
926 | |||
927 | struct WPA_SUPPLICANT *pwpa_supplicant; | ||
928 | struct WPA_SUPPLICANT *pwpa2_supplicant; | ||
929 | struct IE_WPA *pIe; | ||
930 | const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; | ||
931 | |||
932 | struct ieeetypes_countryinfoset *pcountryinfo; | ||
933 | |||
934 | ENTER(); | ||
935 | |||
936 | founddatarateie = 0; | ||
937 | ratesize = 0; | ||
938 | beaconsize = 0; | ||
939 | |||
940 | if (*bytesleft >= sizeof(beaconsize)) { | ||
941 | /* Extract & convert beacon size from the command buffer */ | ||
942 | memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); | ||
943 | beaconsize = le16_to_cpu(beaconsize); | ||
944 | *bytesleft -= sizeof(beaconsize); | ||
945 | *pbeaconinfo += sizeof(beaconsize); | ||
946 | } | ||
947 | |||
948 | if (beaconsize == 0 || beaconsize > *bytesleft) { | ||
949 | |||
950 | *pbeaconinfo += *bytesleft; | ||
951 | *bytesleft = 0; | ||
952 | |||
953 | return -1; | ||
954 | } | ||
955 | |||
956 | /* Initialize the current working beacon pointer for this BSS iteration */ | ||
957 | pcurrentptr = *pbeaconinfo; | ||
958 | |||
959 | /* Advance the return beacon pointer past the current beacon */ | ||
960 | *pbeaconinfo += beaconsize; | ||
961 | *bytesleft -= beaconsize; | ||
962 | |||
963 | bytesleftforcurrentbeacon = beaconsize; | ||
964 | |||
965 | pwpa_supplicant = &pBSSEntry->wpa_supplicant; | ||
966 | pwpa2_supplicant = &pBSSEntry->wpa2_supplicant; | ||
967 | |||
968 | memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); | ||
969 | lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", | ||
970 | pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], | ||
971 | pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], | ||
972 | pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); | ||
973 | |||
974 | pcurrentptr += ETH_ALEN; | ||
975 | bytesleftforcurrentbeacon -= ETH_ALEN; | ||
976 | |||
977 | if (bytesleftforcurrentbeacon < 12) { | ||
978 | lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); | ||
979 | return -1; | ||
980 | } | ||
981 | |||
982 | /* | ||
983 | * next 4 fields are RSSI, time stamp, beacon interval, | ||
984 | * and capability information | ||
985 | */ | ||
986 | |||
987 | /* RSSI is 1 byte long */ | ||
988 | pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); | ||
989 | lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); | ||
990 | pcurrentptr += 1; | ||
991 | bytesleftforcurrentbeacon -= 1; | ||
992 | |||
993 | /* time stamp is 8 bytes long */ | ||
994 | memcpy(fixedie.timestamp, pcurrentptr, 8); | ||
995 | memcpy(pBSSEntry->timestamp, pcurrentptr, 8); | ||
996 | pcurrentptr += 8; | ||
997 | bytesleftforcurrentbeacon -= 8; | ||
998 | |||
999 | /* beacon interval is 2 bytes long */ | ||
1000 | memcpy(&fixedie.beaconinterval, pcurrentptr, 2); | ||
1001 | pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); | ||
1002 | pcurrentptr += 2; | ||
1003 | bytesleftforcurrentbeacon -= 2; | ||
1004 | |||
1005 | /* capability information is 2 bytes long */ | ||
1006 | memcpy(&fixedie.capabilities, pcurrentptr, 2); | ||
1007 | lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", | ||
1008 | fixedie.capabilities); | ||
1009 | fixedie.capabilities = le16_to_cpu(fixedie.capabilities); | ||
1010 | pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; | ||
1011 | memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); | ||
1012 | pcurrentptr += 2; | ||
1013 | bytesleftforcurrentbeacon -= 2; | ||
1014 | |||
1015 | /* rest of the current buffer are IE's */ | ||
1016 | lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", | ||
1017 | bytesleftforcurrentbeacon); | ||
1018 | |||
1019 | lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, | ||
1020 | bytesleftforcurrentbeacon); | ||
1021 | |||
1022 | if (pcap->privacy) { | ||
1023 | lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); | ||
1024 | pBSSEntry->privacy = wlan802_11privfilter8021xWEP; | ||
1025 | } else { | ||
1026 | pBSSEntry->privacy = wlan802_11privfilteracceptall; | ||
1027 | } | ||
1028 | |||
1029 | if (pcap->ibss == 1) { | ||
1030 | pBSSEntry->inframode = wlan802_11ibss; | ||
1031 | } else { | ||
1032 | pBSSEntry->inframode = wlan802_11infrastructure; | ||
1033 | } | ||
1034 | |||
1035 | /* process variable IE */ | ||
1036 | while (bytesleftforcurrentbeacon >= 2) { | ||
1037 | elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); | ||
1038 | elemlen = *((u8 *) pcurrentptr + 1); | ||
1039 | |||
1040 | if (bytesleftforcurrentbeacon < elemlen) { | ||
1041 | lbs_pr_debug(1, "InterpretIE: error in processing IE, " | ||
1042 | "bytes left < IE length\n"); | ||
1043 | bytesleftforcurrentbeacon = 0; | ||
1044 | continue; | ||
1045 | } | ||
1046 | |||
1047 | switch (elemID) { | ||
1048 | |||
1049 | case SSID: | ||
1050 | pBSSEntry->ssid.ssidlength = elemlen; | ||
1051 | memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), | ||
1052 | elemlen); | ||
1053 | lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); | ||
1054 | break; | ||
1055 | |||
1056 | case SUPPORTED_RATES: | ||
1057 | memcpy(pBSSEntry->datarates, (pcurrentptr + 2), | ||
1058 | elemlen); | ||
1059 | memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), | ||
1060 | elemlen); | ||
1061 | ratesize = elemlen; | ||
1062 | founddatarateie = 1; | ||
1063 | break; | ||
1064 | |||
1065 | case EXTRA_IE: | ||
1066 | lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); | ||
1067 | pBSSEntry->extra_ie = 1; | ||
1068 | break; | ||
1069 | |||
1070 | case FH_PARAM_SET: | ||
1071 | pFH = (struct ieeetypes_fhparamset *) pcurrentptr; | ||
1072 | memmove(&pBSSEntry->phyparamset.fhparamset, pFH, | ||
1073 | sizeof(struct ieeetypes_fhparamset)); | ||
1074 | pBSSEntry->phyparamset.fhparamset.dwelltime | ||
1075 | = | ||
1076 | le16_to_cpu(pBSSEntry->phyparamset.fhparamset. | ||
1077 | dwelltime); | ||
1078 | break; | ||
1079 | |||
1080 | case DS_PARAM_SET: | ||
1081 | pDS = (struct ieeetypes_dsparamset *) pcurrentptr; | ||
1082 | |||
1083 | pBSSEntry->channel = pDS->currentchan; | ||
1084 | |||
1085 | memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, | ||
1086 | sizeof(struct ieeetypes_dsparamset)); | ||
1087 | break; | ||
1088 | |||
1089 | case CF_PARAM_SET: | ||
1090 | pCF = (struct ieeetypes_cfparamset *) pcurrentptr; | ||
1091 | |||
1092 | memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, | ||
1093 | sizeof(struct ieeetypes_cfparamset)); | ||
1094 | break; | ||
1095 | |||
1096 | case IBSS_PARAM_SET: | ||
1097 | pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; | ||
1098 | pBSSEntry->atimwindow = | ||
1099 | le32_to_cpu(pibss->atimwindow); | ||
1100 | |||
1101 | memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, | ||
1102 | sizeof(struct ieeetypes_ibssparamset)); | ||
1103 | |||
1104 | pBSSEntry->ssparamset.ibssparamset.atimwindow | ||
1105 | = | ||
1106 | le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. | ||
1107 | atimwindow); | ||
1108 | break; | ||
1109 | |||
1110 | /* Handle Country Info IE */ | ||
1111 | case COUNTRY_INFO: | ||
1112 | pcountryinfo = | ||
1113 | (struct ieeetypes_countryinfoset *) pcurrentptr; | ||
1114 | |||
1115 | if (pcountryinfo->len < | ||
1116 | sizeof(pcountryinfo->countrycode) | ||
1117 | || pcountryinfo->len > 254) { | ||
1118 | lbs_pr_debug(1, "InterpretIE: 11D- Err " | ||
1119 | "CountryInfo len =%d min=%d max=254\n", | ||
1120 | pcountryinfo->len, | ||
1121 | sizeof(pcountryinfo->countrycode)); | ||
1122 | LEAVE(); | ||
1123 | return -1; | ||
1124 | } | ||
1125 | |||
1126 | memcpy(&pBSSEntry->countryinfo, | ||
1127 | pcountryinfo, pcountryinfo->len + 2); | ||
1128 | lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", | ||
1129 | (u8 *) pcountryinfo, | ||
1130 | (u32) (pcountryinfo->len + 2)); | ||
1131 | break; | ||
1132 | |||
1133 | case EXTENDED_SUPPORTED_RATES: | ||
1134 | /* | ||
1135 | * only process extended supported rate | ||
1136 | * if data rate is already found. | ||
1137 | * data rate IE should come before | ||
1138 | * extended supported rate IE | ||
1139 | */ | ||
1140 | if (founddatarateie) { | ||
1141 | if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { | ||
1142 | bytestocopy = | ||
1143 | (WLAN_SUPPORTED_RATES - ratesize); | ||
1144 | } else { | ||
1145 | bytestocopy = elemlen; | ||
1146 | } | ||
1147 | |||
1148 | pRate = (u8 *) pBSSEntry->datarates; | ||
1149 | pRate += ratesize; | ||
1150 | memmove(pRate, (pcurrentptr + 2), bytestocopy); | ||
1151 | |||
1152 | pRate = (u8 *) pBSSEntry->libertas_supported_rates; | ||
1153 | |||
1154 | pRate += ratesize; | ||
1155 | memmove(pRate, (pcurrentptr + 2), bytestocopy); | ||
1156 | } | ||
1157 | break; | ||
1158 | |||
1159 | case VENDOR_SPECIFIC_221: | ||
1160 | #define IE_ID_LEN_FIELDS_BYTES 2 | ||
1161 | pIe = (struct IE_WPA *)pcurrentptr; | ||
1162 | |||
1163 | if (!memcmp(pIe->oui, oui01, sizeof(oui01))) { | ||
1164 | pwpa_supplicant->wpa_ie_len | ||
1165 | = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, | ||
1166 | sizeof(pwpa_supplicant->wpa_ie)); | ||
1167 | memcpy(pwpa_supplicant->wpa_ie, | ||
1168 | pcurrentptr, | ||
1169 | pwpa_supplicant->wpa_ie_len); | ||
1170 | lbs_dbg_hex("InterpretIE: Resp WPA_IE", | ||
1171 | pwpa_supplicant->wpa_ie, elemlen); | ||
1172 | } | ||
1173 | break; | ||
1174 | case WPA2_IE: | ||
1175 | pIe = (struct IE_WPA *)pcurrentptr; | ||
1176 | pwpa2_supplicant->wpa_ie_len | ||
1177 | = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, | ||
1178 | sizeof(pwpa2_supplicant->wpa_ie)); | ||
1179 | memcpy(pwpa2_supplicant->wpa_ie, | ||
1180 | pcurrentptr, pwpa2_supplicant->wpa_ie_len); | ||
1181 | |||
1182 | lbs_dbg_hex("InterpretIE: Resp WPA2_IE", | ||
1183 | pwpa2_supplicant->wpa_ie, elemlen); | ||
1184 | break; | ||
1185 | case TIM: | ||
1186 | break; | ||
1187 | |||
1188 | case CHALLENGE_TEXT: | ||
1189 | break; | ||
1190 | } | ||
1191 | |||
1192 | pcurrentptr += elemlen + 2; | ||
1193 | |||
1194 | /* need to account for IE ID and IE len */ | ||
1195 | bytesleftforcurrentbeacon -= (elemlen + 2); | ||
1196 | |||
1197 | } /* while (bytesleftforcurrentbeacon > 2) */ | ||
1198 | |||
1199 | return 0; | ||
1200 | } | ||
1201 | |||
1202 | /** | ||
1203 | * @brief Compare two SSIDs | ||
1204 | * | ||
1205 | * @param ssid1 A pointer to ssid to compare | ||
1206 | * @param ssid2 A pointer to ssid to compare | ||
1207 | * | ||
1208 | * @return 0--ssid is same, otherwise is different | ||
1209 | */ | ||
1210 | int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) | ||
1211 | { | ||
1212 | if (!ssid1 || !ssid2) | ||
1213 | return -1; | ||
1214 | |||
1215 | if (ssid1->ssidlength != ssid2->ssidlength) | ||
1216 | return -1; | ||
1217 | |||
1218 | return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); | ||
1219 | } | ||
1220 | |||
1221 | /** | ||
1222 | * @brief This function finds a specific compatible BSSID in the scan list | ||
1223 | * | ||
1224 | * @param adapter A pointer to wlan_adapter | ||
1225 | * @param bssid BSSID to find in the scan list | ||
1226 | * @param mode Network mode: Infrastructure or IBSS | ||
1227 | * | ||
1228 | * @return index in BSSID list, or error return code (< 0) | ||
1229 | */ | ||
1230 | int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode) | ||
1231 | { | ||
1232 | int ret = -ENETUNREACH; | ||
1233 | int i; | ||
1234 | |||
1235 | if (!bssid) | ||
1236 | return -EFAULT; | ||
1237 | |||
1238 | lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", | ||
1239 | adapter->numinscantable); | ||
1240 | |||
1241 | /* Look through the scan table for a compatible match. The ret return | ||
1242 | * variable will be equal to the index in the scan table (greater | ||
1243 | * than zero) if the network is compatible. The loop will continue | ||
1244 | * past a matched bssid that is not compatible in case there is an | ||
1245 | * AP with multiple SSIDs assigned to the same BSSID | ||
1246 | */ | ||
1247 | for (i = 0; ret < 0 && i < adapter->numinscantable; i++) { | ||
1248 | if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { | ||
1249 | switch (mode) { | ||
1250 | case wlan802_11infrastructure: | ||
1251 | case wlan802_11ibss: | ||
1252 | ret = is_network_compatible(adapter, i, mode); | ||
1253 | break; | ||
1254 | default: | ||
1255 | ret = i; | ||
1256 | break; | ||
1257 | } | ||
1258 | } | ||
1259 | } | ||
1260 | |||
1261 | return ret; | ||
1262 | } | ||
1263 | |||
1264 | /** | ||
1265 | * @brief This function finds ssid in ssid list. | ||
1266 | * | ||
1267 | * @param adapter A pointer to wlan_adapter | ||
1268 | * @param ssid SSID to find in the list | ||
1269 | * @param bssid BSSID to qualify the SSID selection (if provided) | ||
1270 | * @param mode Network mode: Infrastructure or IBSS | ||
1271 | * | ||
1272 | * @return index in BSSID list | ||
1273 | */ | ||
1274 | int libertas_find_SSID_in_list(wlan_adapter * adapter, | ||
1275 | struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode) | ||
1276 | { | ||
1277 | int net = -ENETUNREACH; | ||
1278 | u8 bestrssi = 0; | ||
1279 | int i; | ||
1280 | int j; | ||
1281 | |||
1282 | lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); | ||
1283 | |||
1284 | for (i = 0; i < adapter->numinscantable; i++) { | ||
1285 | if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && | ||
1286 | (!bssid || | ||
1287 | !memcmp(adapter->scantable[i]. | ||
1288 | macaddress, bssid, ETH_ALEN))) { | ||
1289 | switch (mode) { | ||
1290 | case wlan802_11infrastructure: | ||
1291 | case wlan802_11ibss: | ||
1292 | j = is_network_compatible(adapter, i, mode); | ||
1293 | |||
1294 | if (j >= 0) { | ||
1295 | if (bssid) { | ||
1296 | return i; | ||
1297 | } | ||
1298 | |||
1299 | if (SCAN_RSSI | ||
1300 | (adapter->scantable[i].rssi) | ||
1301 | > bestrssi) { | ||
1302 | bestrssi = | ||
1303 | SCAN_RSSI(adapter-> | ||
1304 | scantable[i]. | ||
1305 | rssi); | ||
1306 | net = i; | ||
1307 | } | ||
1308 | } else { | ||
1309 | if (net == -ENETUNREACH) { | ||
1310 | net = j; | ||
1311 | } | ||
1312 | } | ||
1313 | break; | ||
1314 | case wlan802_11autounknown: | ||
1315 | default: | ||
1316 | if (SCAN_RSSI(adapter->scantable[i].rssi) | ||
1317 | > bestrssi) { | ||
1318 | bestrssi = | ||
1319 | SCAN_RSSI(adapter->scantable[i]. | ||
1320 | rssi); | ||
1321 | net = i; | ||
1322 | } | ||
1323 | break; | ||
1324 | } | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | return net; | ||
1329 | } | ||
1330 | |||
1331 | /** | ||
1332 | * @brief This function finds the best SSID in the Scan List | ||
1333 | * | ||
1334 | * Search the scan table for the best SSID that also matches the current | ||
1335 | * adapter network preference (infrastructure or adhoc) | ||
1336 | * | ||
1337 | * @param adapter A pointer to wlan_adapter | ||
1338 | * | ||
1339 | * @return index in BSSID list | ||
1340 | */ | ||
1341 | int libertas_find_best_SSID_in_list(wlan_adapter * adapter, | ||
1342 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode) | ||
1343 | { | ||
1344 | int bestnet = -ENETUNREACH; | ||
1345 | u8 bestrssi = 0; | ||
1346 | int i; | ||
1347 | |||
1348 | ENTER(); | ||
1349 | |||
1350 | lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); | ||
1351 | |||
1352 | for (i = 0; i < adapter->numinscantable; i++) { | ||
1353 | switch (mode) { | ||
1354 | case wlan802_11infrastructure: | ||
1355 | case wlan802_11ibss: | ||
1356 | if (is_network_compatible(adapter, i, mode) >= 0) { | ||
1357 | if (SCAN_RSSI(adapter->scantable[i].rssi) > | ||
1358 | bestrssi) { | ||
1359 | bestrssi = | ||
1360 | SCAN_RSSI(adapter->scantable[i]. | ||
1361 | rssi); | ||
1362 | bestnet = i; | ||
1363 | } | ||
1364 | } | ||
1365 | break; | ||
1366 | case wlan802_11autounknown: | ||
1367 | default: | ||
1368 | if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { | ||
1369 | bestrssi = | ||
1370 | SCAN_RSSI(adapter->scantable[i].rssi); | ||
1371 | bestnet = i; | ||
1372 | } | ||
1373 | break; | ||
1374 | } | ||
1375 | } | ||
1376 | |||
1377 | LEAVE(); | ||
1378 | return bestnet; | ||
1379 | } | ||
1380 | |||
1381 | /** | ||
1382 | * @brief Find the AP with specific ssid in the scan list | ||
1383 | * | ||
1384 | * @param priv A pointer to wlan_private structure | ||
1385 | * @param pSSID A pointer to AP's ssid | ||
1386 | * | ||
1387 | * @return 0--success, otherwise--fail | ||
1388 | */ | ||
1389 | int libertas_find_best_network_SSID(wlan_private * priv, | ||
1390 | struct WLAN_802_11_SSID *pSSID, | ||
1391 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, | ||
1392 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode) | ||
1393 | { | ||
1394 | wlan_adapter *adapter = priv->adapter; | ||
1395 | int ret = 0; | ||
1396 | struct bss_descriptor *preqbssid; | ||
1397 | int i; | ||
1398 | |||
1399 | ENTER(); | ||
1400 | |||
1401 | memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); | ||
1402 | |||
1403 | wlan_scan_networks(priv, NULL); | ||
1404 | if (adapter->surpriseremoved) | ||
1405 | return -1; | ||
1406 | wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); | ||
1407 | |||
1408 | i = libertas_find_best_SSID_in_list(adapter, preferred_mode); | ||
1409 | if (i < 0) { | ||
1410 | ret = -1; | ||
1411 | goto out; | ||
1412 | } | ||
1413 | |||
1414 | preqbssid = &adapter->scantable[i]; | ||
1415 | memcpy(pSSID, &preqbssid->ssid, | ||
1416 | sizeof(struct WLAN_802_11_SSID)); | ||
1417 | *out_mode = preqbssid->inframode; | ||
1418 | |||
1419 | if (!pSSID->ssidlength) { | ||
1420 | ret = -1; | ||
1421 | } | ||
1422 | |||
1423 | out: | ||
1424 | LEAVE(); | ||
1425 | return ret; | ||
1426 | } | ||
1427 | |||
1428 | /** | ||
1429 | * @brief Scan Network | ||
1430 | * | ||
1431 | * @param dev A pointer to net_device structure | ||
1432 | * @param info A pointer to iw_request_info structure | ||
1433 | * @param vwrq A pointer to iw_param structure | ||
1434 | * @param extra A pointer to extra data buf | ||
1435 | * | ||
1436 | * @return 0 --success, otherwise fail | ||
1437 | */ | ||
1438 | int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, | ||
1439 | struct iw_param *vwrq, char *extra) | ||
1440 | { | ||
1441 | wlan_private *priv = dev->priv; | ||
1442 | wlan_adapter *adapter = priv->adapter; | ||
1443 | union iwreq_data wrqu; | ||
1444 | |||
1445 | ENTER(); | ||
1446 | |||
1447 | if (!wlan_scan_networks(priv, NULL)) { | ||
1448 | memset(&wrqu, 0, sizeof(union iwreq_data)); | ||
1449 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, | ||
1450 | NULL); | ||
1451 | } | ||
1452 | |||
1453 | if (adapter->surpriseremoved) | ||
1454 | return -1; | ||
1455 | |||
1456 | LEAVE(); | ||
1457 | return 0; | ||
1458 | } | ||
1459 | |||
1460 | /** | ||
1461 | * @brief Send a scan command for all available channels filtered on a spec | ||
1462 | * | ||
1463 | * @param priv A pointer to wlan_private structure | ||
1464 | * @param prequestedssid A pointer to AP's ssid | ||
1465 | * @param keeppreviousscan Flag used to save/clear scan table before scan | ||
1466 | * | ||
1467 | * @return 0-success, otherwise fail | ||
1468 | */ | ||
1469 | int libertas_send_specific_SSID_scan(wlan_private * priv, | ||
1470 | struct WLAN_802_11_SSID *prequestedssid, | ||
1471 | u8 keeppreviousscan) | ||
1472 | { | ||
1473 | wlan_adapter *adapter = priv->adapter; | ||
1474 | struct wlan_ioctl_user_scan_cfg scancfg; | ||
1475 | |||
1476 | ENTER(); | ||
1477 | |||
1478 | if (prequestedssid == NULL) { | ||
1479 | return -1; | ||
1480 | } | ||
1481 | |||
1482 | memset(&scancfg, 0x00, sizeof(scancfg)); | ||
1483 | |||
1484 | memcpy(scancfg.specificSSID, prequestedssid->ssid, | ||
1485 | prequestedssid->ssidlength); | ||
1486 | scancfg.keeppreviousscan = keeppreviousscan; | ||
1487 | |||
1488 | wlan_scan_networks(priv, &scancfg); | ||
1489 | if (adapter->surpriseremoved) | ||
1490 | return -1; | ||
1491 | wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); | ||
1492 | |||
1493 | LEAVE(); | ||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1497 | /** | ||
1498 | * @brief scan an AP with specific BSSID | ||
1499 | * | ||
1500 | * @param priv A pointer to wlan_private structure | ||
1501 | * @param bssid A pointer to AP's bssid | ||
1502 | * @param keeppreviousscan Flag used to save/clear scan table before scan | ||
1503 | * | ||
1504 | * @return 0-success, otherwise fail | ||
1505 | */ | ||
1506 | int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) | ||
1507 | { | ||
1508 | struct wlan_ioctl_user_scan_cfg scancfg; | ||
1509 | |||
1510 | ENTER(); | ||
1511 | |||
1512 | if (bssid == NULL) { | ||
1513 | return -1; | ||
1514 | } | ||
1515 | |||
1516 | memset(&scancfg, 0x00, sizeof(scancfg)); | ||
1517 | memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); | ||
1518 | scancfg.keeppreviousscan = keeppreviousscan; | ||
1519 | |||
1520 | wlan_scan_networks(priv, &scancfg); | ||
1521 | if (priv->adapter->surpriseremoved) | ||
1522 | return -1; | ||
1523 | wait_event_interruptible(priv->adapter->cmd_pending, | ||
1524 | !priv->adapter->nr_cmd_pending); | ||
1525 | |||
1526 | LEAVE(); | ||
1527 | return 0; | ||
1528 | } | ||
1529 | |||
1530 | /** | ||
1531 | * @brief Retrieve the scan table entries via wireless tools IOCTL call | ||
1532 | * | ||
1533 | * @param dev A pointer to net_device structure | ||
1534 | * @param info A pointer to iw_request_info structure | ||
1535 | * @param dwrq A pointer to iw_point structure | ||
1536 | * @param extra A pointer to extra data buf | ||
1537 | * | ||
1538 | * @return 0 --success, otherwise fail | ||
1539 | */ | ||
1540 | int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, | ||
1541 | struct iw_point *dwrq, char *extra) | ||
1542 | { | ||
1543 | wlan_private *priv = dev->priv; | ||
1544 | wlan_adapter *adapter = priv->adapter; | ||
1545 | int ret = 0; | ||
1546 | char *current_ev = extra; | ||
1547 | char *end_buf = extra + IW_SCAN_MAX_DATA; | ||
1548 | struct chan_freq_power *cfp; | ||
1549 | struct bss_descriptor *pscantable; | ||
1550 | char *current_val; /* For rates */ | ||
1551 | struct iw_event iwe; /* Temporary buffer */ | ||
1552 | int i; | ||
1553 | int j; | ||
1554 | int rate; | ||
1555 | #define PERFECT_RSSI ((u8)50) | ||
1556 | #define WORST_RSSI ((u8)0) | ||
1557 | #define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) | ||
1558 | u8 rssi; | ||
1559 | |||
1560 | u8 buf[16 + 256 * 2]; | ||
1561 | u8 *ptr; | ||
1562 | |||
1563 | ENTER(); | ||
1564 | |||
1565 | /* | ||
1566 | * if there's either commands in the queue or one being | ||
1567 | * processed return -EAGAIN for iwlist to retry later. | ||
1568 | */ | ||
1569 | if (adapter->nr_cmd_pending) | ||
1570 | return -EAGAIN; | ||
1571 | |||
1572 | if (adapter->connect_status == libertas_connected) | ||
1573 | lbs_pr_debug(1, "Current ssid: %32s\n", | ||
1574 | adapter->curbssparams.ssid.ssid); | ||
1575 | |||
1576 | lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", | ||
1577 | adapter->numinscantable); | ||
1578 | |||
1579 | /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. | ||
1580 | * The new API using SIOCGIWSCAN is only limited by buffer size | ||
1581 | * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes | ||
1582 | * which is 4096. | ||
1583 | */ | ||
1584 | for (i = 0; i < adapter->numinscantable; i++) { | ||
1585 | if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { | ||
1586 | lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " | ||
1587 | "MAX_SCAN_CELL_SIZE=%d\n", | ||
1588 | i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); | ||
1589 | break; | ||
1590 | } | ||
1591 | |||
1592 | pscantable = &adapter->scantable[i]; | ||
1593 | |||
1594 | lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); | ||
1595 | |||
1596 | cfp = | ||
1597 | libertas_find_cfp_by_band_and_channel(adapter, 0, | ||
1598 | pscantable->channel); | ||
1599 | if (!cfp) { | ||
1600 | lbs_pr_debug(1, "Invalid channel number %d\n", | ||
1601 | pscantable->channel); | ||
1602 | continue; | ||
1603 | } | ||
1604 | |||
1605 | if (!ssid_valid(&adapter->scantable[i].ssid)) { | ||
1606 | continue; | ||
1607 | } | ||
1608 | |||
1609 | /* First entry *MUST* be the AP MAC address */ | ||
1610 | iwe.cmd = SIOCGIWAP; | ||
1611 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
1612 | memcpy(iwe.u.ap_addr.sa_data, | ||
1613 | &adapter->scantable[i].macaddress, ETH_ALEN); | ||
1614 | |||
1615 | iwe.len = IW_EV_ADDR_LEN; | ||
1616 | current_ev = | ||
1617 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | ||
1618 | |||
1619 | //Add the ESSID | ||
1620 | iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; | ||
1621 | |||
1622 | if (iwe.u.data.length > 32) { | ||
1623 | iwe.u.data.length = 32; | ||
1624 | } | ||
1625 | |||
1626 | iwe.cmd = SIOCGIWESSID; | ||
1627 | iwe.u.data.flags = 1; | ||
1628 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | ||
1629 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, | ||
1630 | adapter->scantable[i].ssid. | ||
1631 | ssid); | ||
1632 | |||
1633 | //Add mode | ||
1634 | iwe.cmd = SIOCGIWMODE; | ||
1635 | iwe.u.mode = adapter->scantable[i].inframode + 1; | ||
1636 | iwe.len = IW_EV_UINT_LEN; | ||
1637 | current_ev = | ||
1638 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | ||
1639 | |||
1640 | //frequency | ||
1641 | iwe.cmd = SIOCGIWFREQ; | ||
1642 | iwe.u.freq.m = (long)cfp->freq * 100000; | ||
1643 | iwe.u.freq.e = 1; | ||
1644 | iwe.len = IW_EV_FREQ_LEN; | ||
1645 | current_ev = | ||
1646 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | ||
1647 | |||
1648 | /* Add quality statistics */ | ||
1649 | iwe.cmd = IWEVQUAL; | ||
1650 | iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; | ||
1651 | iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); | ||
1652 | |||
1653 | rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; | ||
1654 | iwe.u.qual.qual = | ||
1655 | (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * | ||
1656 | (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / | ||
1657 | (RSSI_DIFF * RSSI_DIFF); | ||
1658 | if (iwe.u.qual.qual > 100) | ||
1659 | iwe.u.qual.qual = 100; | ||
1660 | else if (iwe.u.qual.qual < 1) | ||
1661 | iwe.u.qual.qual = 0; | ||
1662 | |||
1663 | if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { | ||
1664 | iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; | ||
1665 | } else { | ||
1666 | iwe.u.qual.noise = | ||
1667 | CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | ||
1668 | } | ||
1669 | if ((adapter->inframode == wlan802_11ibss) && | ||
1670 | !libertas_SSID_cmp(&adapter->curbssparams.ssid, | ||
1671 | &adapter->scantable[i].ssid) | ||
1672 | && adapter->adhoccreate) { | ||
1673 | ret = libertas_prepare_and_send_command(priv, | ||
1674 | cmd_802_11_rssi, | ||
1675 | 0, | ||
1676 | cmd_option_waitforrsp, | ||
1677 | 0, NULL); | ||
1678 | |||
1679 | if (!ret) { | ||
1680 | iwe.u.qual.level = | ||
1681 | CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / | ||
1682 | AVG_SCALE, | ||
1683 | adapter->NF[TYPE_RXPD][TYPE_AVG] / | ||
1684 | AVG_SCALE); | ||
1685 | } | ||
1686 | } | ||
1687 | iwe.len = IW_EV_QUAL_LEN; | ||
1688 | current_ev = | ||
1689 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | ||
1690 | |||
1691 | /* Add encryption capability */ | ||
1692 | iwe.cmd = SIOCGIWENCODE; | ||
1693 | if (adapter->scantable[i].privacy) { | ||
1694 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
1695 | } else { | ||
1696 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
1697 | } | ||
1698 | iwe.u.data.length = 0; | ||
1699 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | ||
1700 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, | ||
1701 | adapter->scantable->ssid. | ||
1702 | ssid); | ||
1703 | |||
1704 | current_val = current_ev + IW_EV_LCP_LEN; | ||
1705 | |||
1706 | iwe.cmd = SIOCGIWRATE; | ||
1707 | |||
1708 | iwe.u.bitrate.fixed = 0; | ||
1709 | iwe.u.bitrate.disabled = 0; | ||
1710 | iwe.u.bitrate.value = 0; | ||
1711 | |||
1712 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
1713 | for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); | ||
1714 | j++) { | ||
1715 | if (adapter->scantable[i].libertas_supported_rates[j] == 0) { | ||
1716 | break; | ||
1717 | } | ||
1718 | rate = | ||
1719 | (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * | ||
1720 | 500000; | ||
1721 | if (rate > iwe.u.bitrate.value) { | ||
1722 | iwe.u.bitrate.value = rate; | ||
1723 | } | ||
1724 | |||
1725 | iwe.u.bitrate.value = | ||
1726 | (adapter->scantable[i].libertas_supported_rates[j] | ||
1727 | & 0x7f) * 500000; | ||
1728 | iwe.len = IW_EV_PARAM_LEN; | ||
1729 | current_ev = | ||
1730 | iwe_stream_add_value(current_ev, current_val, | ||
1731 | end_buf, &iwe, iwe.len); | ||
1732 | |||
1733 | } | ||
1734 | if ((adapter->scantable[i].inframode == wlan802_11ibss) | ||
1735 | && !libertas_SSID_cmp(&adapter->curbssparams.ssid, | ||
1736 | &adapter->scantable[i].ssid) | ||
1737 | && adapter->adhoccreate) { | ||
1738 | iwe.u.bitrate.value = 22 * 500000; | ||
1739 | } | ||
1740 | iwe.len = IW_EV_PARAM_LEN; | ||
1741 | current_ev = | ||
1742 | iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, | ||
1743 | iwe.len); | ||
1744 | |||
1745 | /* Add new value to event */ | ||
1746 | current_val = current_ev + IW_EV_LCP_LEN; | ||
1747 | |||
1748 | if (adapter->scantable[i].wpa2_supplicant.wpa_ie[0] == WPA2_IE) { | ||
1749 | memset(&iwe, 0, sizeof(iwe)); | ||
1750 | memset(buf, 0, sizeof(buf)); | ||
1751 | memcpy(buf, adapter->scantable[i]. | ||
1752 | wpa2_supplicant.wpa_ie, | ||
1753 | adapter->scantable[i].wpa2_supplicant. | ||
1754 | wpa_ie_len); | ||
1755 | iwe.cmd = IWEVGENIE; | ||
1756 | iwe.u.data.length = adapter->scantable[i]. | ||
1757 | wpa2_supplicant.wpa_ie_len; | ||
1758 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | ||
1759 | current_ev = iwe_stream_add_point(current_ev, end_buf, | ||
1760 | &iwe, buf); | ||
1761 | } | ||
1762 | if (adapter->scantable[i].wpa_supplicant.wpa_ie[0] == WPA_IE) { | ||
1763 | memset(&iwe, 0, sizeof(iwe)); | ||
1764 | memset(buf, 0, sizeof(buf)); | ||
1765 | memcpy(buf, adapter->scantable[i]. | ||
1766 | wpa_supplicant.wpa_ie, | ||
1767 | adapter->scantable[i].wpa_supplicant. | ||
1768 | wpa_ie_len); | ||
1769 | iwe.cmd = IWEVGENIE; | ||
1770 | iwe.u.data.length = adapter->scantable[i]. | ||
1771 | wpa_supplicant.wpa_ie_len; | ||
1772 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | ||
1773 | current_ev = iwe_stream_add_point(current_ev, end_buf, | ||
1774 | &iwe, buf); | ||
1775 | } | ||
1776 | |||
1777 | |||
1778 | if (adapter->scantable[i].extra_ie != 0) { | ||
1779 | memset(&iwe, 0, sizeof(iwe)); | ||
1780 | memset(buf, 0, sizeof(buf)); | ||
1781 | ptr = buf; | ||
1782 | ptr += sprintf(ptr, "extra_ie"); | ||
1783 | iwe.u.data.length = strlen(buf); | ||
1784 | |||
1785 | lbs_pr_debug(1, "iwe.u.data.length %d\n", | ||
1786 | iwe.u.data.length); | ||
1787 | lbs_pr_debug(1, "BUF: %s \n", buf); | ||
1788 | |||
1789 | iwe.cmd = IWEVCUSTOM; | ||
1790 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | ||
1791 | current_ev = | ||
1792 | iwe_stream_add_point(current_ev, end_buf, &iwe, | ||
1793 | buf); | ||
1794 | } | ||
1795 | |||
1796 | current_val = current_ev + IW_EV_LCP_LEN; | ||
1797 | |||
1798 | /* | ||
1799 | * Check if we added any event | ||
1800 | */ | ||
1801 | if ((current_val - current_ev) > IW_EV_LCP_LEN) | ||
1802 | current_ev = current_val; | ||
1803 | } | ||
1804 | |||
1805 | dwrq->length = (current_ev - extra); | ||
1806 | dwrq->flags = 0; | ||
1807 | |||
1808 | LEAVE(); | ||
1809 | return 0; | ||
1810 | } | ||
1811 | |||
1812 | /** | ||
1813 | * @brief Prepare a scan command to be sent to the firmware | ||
1814 | * | ||
1815 | * Use the wlan_scan_cmd_config sent to the command processing module in | ||
1816 | * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command | ||
1817 | * struct to send to firmware. | ||
1818 | * | ||
1819 | * The fixed fields specifying the BSS type and BSSID filters as well as a | ||
1820 | * variable number/length of TLVs are sent in the command to firmware. | ||
1821 | * | ||
1822 | * @param priv A pointer to wlan_private structure | ||
1823 | * @param cmd A pointer to cmd_ds_command structure to be sent to | ||
1824 | * firmware with the cmd_DS_801_11_SCAN structure | ||
1825 | * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used | ||
1826 | * to set the fields/TLVs for the command sent to firmware | ||
1827 | * | ||
1828 | * @return 0 or -1 | ||
1829 | * | ||
1830 | * @sa wlan_scan_create_channel_list | ||
1831 | */ | ||
1832 | int libertas_cmd_80211_scan(wlan_private * priv, | ||
1833 | struct cmd_ds_command *cmd, void *pdata_buf) | ||
1834 | { | ||
1835 | struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; | ||
1836 | struct wlan_scan_cmd_config *pscancfg; | ||
1837 | |||
1838 | ENTER(); | ||
1839 | |||
1840 | pscancfg = pdata_buf; | ||
1841 | |||
1842 | /* Set fixed field variables in scan command */ | ||
1843 | pscan->bsstype = pscancfg->bsstype; | ||
1844 | memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); | ||
1845 | memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); | ||
1846 | |||
1847 | cmd->command = cpu_to_le16(cmd_802_11_scan); | ||
1848 | |||
1849 | /* size is equal to the sizeof(fixed portions) + the TLV len + header */ | ||
1850 | cmd->size = cpu_to_le16(sizeof(pscan->bsstype) | ||
1851 | + sizeof(pscan->BSSID) | ||
1852 | + pscancfg->tlvbufferlen + S_DS_GEN); | ||
1853 | |||
1854 | lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", | ||
1855 | cmd->command, cmd->size, cmd->seqnum); | ||
1856 | LEAVE(); | ||
1857 | return 0; | ||
1858 | } | ||
1859 | |||
1860 | /** | ||
1861 | * @brief This function handles the command response of scan | ||
1862 | * | ||
1863 | * The response buffer for the scan command has the following | ||
1864 | * memory layout: | ||
1865 | * | ||
1866 | * .-----------------------------------------------------------. | ||
1867 | * | header (4 * sizeof(u16)): Standard command response hdr | | ||
1868 | * .-----------------------------------------------------------. | ||
1869 | * | bufsize (u16) : sizeof the BSS Description data | | ||
1870 | * .-----------------------------------------------------------. | ||
1871 | * | NumOfSet (u8) : Number of BSS Descs returned | | ||
1872 | * .-----------------------------------------------------------. | ||
1873 | * | BSSDescription data (variable, size given in bufsize) | | ||
1874 | * .-----------------------------------------------------------. | ||
1875 | * | TLV data (variable, size calculated using header->size, | | ||
1876 | * | bufsize and sizeof the fixed fields above) | | ||
1877 | * .-----------------------------------------------------------. | ||
1878 | * | ||
1879 | * @param priv A pointer to wlan_private structure | ||
1880 | * @param resp A pointer to cmd_ds_command | ||
1881 | * | ||
1882 | * @return 0 or -1 | ||
1883 | */ | ||
1884 | int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) | ||
1885 | { | ||
1886 | wlan_adapter *adapter = priv->adapter; | ||
1887 | struct cmd_ds_802_11_scan_rsp *pscan; | ||
1888 | struct bss_descriptor newbssentry; | ||
1889 | struct mrvlietypes_data *ptlv; | ||
1890 | struct mrvlietypes_tsftimestamp *ptsftlv; | ||
1891 | u8 *pbssinfo; | ||
1892 | u16 scanrespsize; | ||
1893 | int bytesleft; | ||
1894 | int numintable; | ||
1895 | int bssIdx; | ||
1896 | int idx; | ||
1897 | int tlvbufsize; | ||
1898 | u64 tsfval; | ||
1899 | |||
1900 | ENTER(); | ||
1901 | |||
1902 | pscan = &resp->params.scanresp; | ||
1903 | |||
1904 | if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { | ||
1905 | lbs_pr_debug(1, | ||
1906 | "SCAN_RESP: Invalid number of AP returned (%d)!!\n", | ||
1907 | pscan->nr_sets); | ||
1908 | LEAVE(); | ||
1909 | return -1; | ||
1910 | } | ||
1911 | |||
1912 | bytesleft = le16_to_cpu(pscan->bssdescriptsize); | ||
1913 | lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); | ||
1914 | |||
1915 | scanrespsize = le16_to_cpu(resp->size); | ||
1916 | lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", | ||
1917 | pscan->nr_sets); | ||
1918 | |||
1919 | numintable = adapter->numinscantable; | ||
1920 | pbssinfo = pscan->bssdesc_and_tlvbuffer; | ||
1921 | |||
1922 | /* The size of the TLV buffer is equal to the entire command response | ||
1923 | * size (scanrespsize) minus the fixed fields (sizeof()'s), the | ||
1924 | * BSS Descriptions (bssdescriptsize as bytesLef) and the command | ||
1925 | * response header (S_DS_GEN) | ||
1926 | */ | ||
1927 | tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) | ||
1928 | + sizeof(pscan->nr_sets) | ||
1929 | + S_DS_GEN); | ||
1930 | |||
1931 | ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); | ||
1932 | |||
1933 | /* Search the TLV buffer space in the scan response for any valid TLVs */ | ||
1934 | wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); | ||
1935 | |||
1936 | /* | ||
1937 | * Process each scan response returned (pscan->nr_sets). Save | ||
1938 | * the information in the newbssentry and then insert into the | ||
1939 | * driver scan table either as an update to an existing entry | ||
1940 | * or as an addition at the end of the table | ||
1941 | */ | ||
1942 | for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { | ||
1943 | /* Zero out the newbssentry we are about to store info in */ | ||
1944 | memset(&newbssentry, 0x00, sizeof(newbssentry)); | ||
1945 | |||
1946 | /* Process the data fields and IEs returned for this BSS */ | ||
1947 | if ((InterpretBSSDescriptionWithIE(&newbssentry, | ||
1948 | &pbssinfo, | ||
1949 | &bytesleft) == | ||
1950 | 0) | ||
1951 | && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { | ||
1952 | |||
1953 | lbs_pr_debug(1, | ||
1954 | "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
1955 | newbssentry.macaddress[0], | ||
1956 | newbssentry.macaddress[1], | ||
1957 | newbssentry.macaddress[2], | ||
1958 | newbssentry.macaddress[3], | ||
1959 | newbssentry.macaddress[4], | ||
1960 | newbssentry.macaddress[5]); | ||
1961 | |||
1962 | /* | ||
1963 | * Search the scan table for the same bssid | ||
1964 | */ | ||
1965 | for (bssIdx = 0; bssIdx < numintable; bssIdx++) { | ||
1966 | if (memcmp(newbssentry.macaddress, | ||
1967 | adapter->scantable[bssIdx]. | ||
1968 | macaddress, | ||
1969 | sizeof(newbssentry.macaddress)) == | ||
1970 | 0) { | ||
1971 | /* | ||
1972 | * If the SSID matches as well, it is a duplicate of | ||
1973 | * this entry. Keep the bssIdx set to this | ||
1974 | * entry so we replace the old contents in the table | ||
1975 | */ | ||
1976 | if ((newbssentry.ssid.ssidlength == | ||
1977 | adapter->scantable[bssIdx].ssid. | ||
1978 | ssidlength) | ||
1979 | && | ||
1980 | (memcmp | ||
1981 | (newbssentry.ssid.ssid, | ||
1982 | adapter->scantable[bssIdx].ssid. | ||
1983 | ssid, | ||
1984 | newbssentry.ssid.ssidlength) == | ||
1985 | 0)) { | ||
1986 | lbs_pr_debug(1, | ||
1987 | "SCAN_RESP: Duplicate of index: %d\n", | ||
1988 | bssIdx); | ||
1989 | break; | ||
1990 | } | ||
1991 | } | ||
1992 | } | ||
1993 | /* | ||
1994 | * If the bssIdx is equal to the number of entries in the table, | ||
1995 | * the new entry was not a duplicate; append it to the scan | ||
1996 | * table | ||
1997 | */ | ||
1998 | if (bssIdx == numintable) { | ||
1999 | /* Range check the bssIdx, keep it limited to the last entry */ | ||
2000 | if (bssIdx == MRVDRV_MAX_BSSID_LIST) { | ||
2001 | bssIdx--; | ||
2002 | } else { | ||
2003 | numintable++; | ||
2004 | } | ||
2005 | } | ||
2006 | |||
2007 | /* | ||
2008 | * If the TSF TLV was appended to the scan results, save the | ||
2009 | * this entries TSF value in the networktsf field. The | ||
2010 | * networktsf is the firmware's TSF value at the time the | ||
2011 | * beacon or probe response was received. | ||
2012 | */ | ||
2013 | if (ptsftlv) { | ||
2014 | memcpy(&tsfval, &ptsftlv->tsftable[idx], | ||
2015 | sizeof(tsfval)); | ||
2016 | tsfval = le64_to_cpu(tsfval); | ||
2017 | |||
2018 | memcpy(&newbssentry.networktsf, | ||
2019 | &tsfval, sizeof(newbssentry.networktsf)); | ||
2020 | } | ||
2021 | |||
2022 | /* Copy the locally created newbssentry to the scan table */ | ||
2023 | memcpy(&adapter->scantable[bssIdx], | ||
2024 | &newbssentry, | ||
2025 | sizeof(adapter->scantable[bssIdx])); | ||
2026 | |||
2027 | } else { | ||
2028 | |||
2029 | /* error parsing/interpreting the scan response, skipped */ | ||
2030 | lbs_pr_debug(1, "SCAN_RESP: " | ||
2031 | "InterpretBSSDescriptionWithIE returned ERROR\n"); | ||
2032 | } | ||
2033 | } | ||
2034 | |||
2035 | lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", | ||
2036 | pscan->nr_sets, numintable - adapter->numinscantable, | ||
2037 | numintable); | ||
2038 | |||
2039 | /* Update the total number of BSSIDs in the scan table */ | ||
2040 | adapter->numinscantable = numintable; | ||
2041 | |||
2042 | LEAVE(); | ||
2043 | return 0; | ||
2044 | } | ||
diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h new file mode 100644 index 000000000000..d93aa7fa44fd --- /dev/null +++ b/drivers/net/wireless/libertas/scan.h | |||
@@ -0,0 +1,216 @@ | |||
1 | /* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ | ||
2 | /* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ | ||
3 | |||
4 | /** | ||
5 | * Interface for the wlan network scan routines | ||
6 | * | ||
7 | * Driver interface functions and type declarations for the scan module | ||
8 | * implemented in wlan_scan.c. | ||
9 | */ | ||
10 | #ifndef _WLAN_SCAN_H | ||
11 | #define _WLAN_SCAN_H | ||
12 | |||
13 | #include "hostcmd.h" | ||
14 | |||
15 | /** | ||
16 | * @brief Maximum number of channels that can be sent in a setuserscan ioctl | ||
17 | * | ||
18 | * @sa wlan_ioctl_user_scan_cfg | ||
19 | */ | ||
20 | #define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 | ||
21 | |||
22 | //! Infrastructure BSS scan type in wlan_scan_cmd_config | ||
23 | #define WLAN_SCAN_BSS_TYPE_BSS 1 | ||
24 | |||
25 | //! Adhoc BSS scan type in wlan_scan_cmd_config | ||
26 | #define WLAN_SCAN_BSS_TYPE_IBSS 2 | ||
27 | |||
28 | //! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter | ||
29 | #define WLAN_SCAN_BSS_TYPE_ANY 3 | ||
30 | |||
31 | /** | ||
32 | * @brief Structure used internally in the wlan driver to configure a scan. | ||
33 | * | ||
34 | * Sent to the command processing module to configure the firmware | ||
35 | * scan command prepared by libertas_cmd_80211_scan. | ||
36 | * | ||
37 | * @sa wlan_scan_networks | ||
38 | * | ||
39 | */ | ||
40 | struct wlan_scan_cmd_config { | ||
41 | /** | ||
42 | * @brief BSS type to be sent in the firmware command | ||
43 | * | ||
44 | * Field can be used to restrict the types of networks returned in the | ||
45 | * scan. valid settings are: | ||
46 | * | ||
47 | * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) | ||
48 | * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) | ||
49 | * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) | ||
50 | */ | ||
51 | u8 bsstype; | ||
52 | |||
53 | /** | ||
54 | * @brief Specific BSSID used to filter scan results in the firmware | ||
55 | */ | ||
56 | u8 specificBSSID[ETH_ALEN]; | ||
57 | |||
58 | /** | ||
59 | * @brief length of TLVs sent in command starting at tlvBuffer | ||
60 | */ | ||
61 | int tlvbufferlen; | ||
62 | |||
63 | /** | ||
64 | * @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command | ||
65 | * | ||
66 | * @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t | ||
67 | * @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t | ||
68 | */ | ||
69 | u8 tlvbuffer[1]; //!< SSID TLV(s) and ChanList TLVs are stored here | ||
70 | }; | ||
71 | |||
72 | /** | ||
73 | * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg | ||
74 | * | ||
75 | * Multiple instances of this structure are included in the IOCTL command | ||
76 | * to configure a instance of a scan on the specific channel. | ||
77 | */ | ||
78 | struct wlan_ioctl_user_scan_chan { | ||
79 | u8 channumber; //!< channel Number to scan | ||
80 | u8 radiotype; //!< Radio type: 'B/G' band = 0, 'A' band = 1 | ||
81 | u8 scantype; //!< Scan type: Active = 0, Passive = 1 | ||
82 | u16 scantime; //!< Scan duration in milliseconds; if 0 default used | ||
83 | }; | ||
84 | |||
85 | /** | ||
86 | * @brief IOCTL input structure to configure an immediate scan cmd to firmware | ||
87 | * | ||
88 | * Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl. Specifies | ||
89 | * a number of parameters to be used in general for the scan as well | ||
90 | * as a channel list (wlan_ioctl_user_scan_chan) for each scan period | ||
91 | * desired. | ||
92 | * | ||
93 | * @sa libertas_set_user_scan_ioctl | ||
94 | */ | ||
95 | struct wlan_ioctl_user_scan_cfg { | ||
96 | |||
97 | /** | ||
98 | * @brief Flag set to keep the previous scan table intact | ||
99 | * | ||
100 | * If set, the scan results will accumulate, replacing any previous | ||
101 | * matched entries for a BSS with the new scan data | ||
102 | */ | ||
103 | u8 keeppreviousscan; //!< Do not erase the existing scan results | ||
104 | |||
105 | /** | ||
106 | * @brief BSS type to be sent in the firmware command | ||
107 | * | ||
108 | * Field can be used to restrict the types of networks returned in the | ||
109 | * scan. valid settings are: | ||
110 | * | ||
111 | * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) | ||
112 | * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) | ||
113 | * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) | ||
114 | */ | ||
115 | u8 bsstype; | ||
116 | |||
117 | /** | ||
118 | * @brief Configure the number of probe requests for active chan scans | ||
119 | */ | ||
120 | u8 numprobes; | ||
121 | |||
122 | /** | ||
123 | * @brief BSSID filter sent in the firmware command to limit the results | ||
124 | */ | ||
125 | u8 specificBSSID[ETH_ALEN]; | ||
126 | |||
127 | /** | ||
128 | * @brief SSID filter sent in the firmware command to limit the results | ||
129 | */ | ||
130 | char specificSSID[IW_ESSID_MAX_SIZE + 1]; | ||
131 | |||
132 | /** | ||
133 | * @brief Variable number (fixed maximum) of channels to scan up | ||
134 | */ | ||
135 | struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; | ||
136 | }; | ||
137 | |||
138 | /** | ||
139 | * @brief Structure used to store information for each beacon/probe response | ||
140 | */ | ||
141 | struct bss_descriptor { | ||
142 | u8 macaddress[ETH_ALEN]; | ||
143 | |||
144 | struct WLAN_802_11_SSID ssid; | ||
145 | |||
146 | /* WEP encryption requirement */ | ||
147 | u32 privacy; | ||
148 | |||
149 | /* receive signal strength in dBm */ | ||
150 | long rssi; | ||
151 | |||
152 | u32 channel; | ||
153 | |||
154 | u16 beaconperiod; | ||
155 | |||
156 | u32 atimwindow; | ||
157 | |||
158 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; | ||
159 | u8 libertas_supported_rates[WLAN_SUPPORTED_RATES]; | ||
160 | |||
161 | int extra_ie; | ||
162 | |||
163 | u8 timestamp[8]; //!< TSF value included in the beacon/probe response | ||
164 | union ieeetypes_phyparamset phyparamset; | ||
165 | union IEEEtypes_ssparamset ssparamset; | ||
166 | struct ieeetypes_capinfo cap; | ||
167 | u8 datarates[WLAN_SUPPORTED_RATES]; | ||
168 | |||
169 | __le64 networktsf; //!< TSF timestamp from the current firmware TSF | ||
170 | |||
171 | struct ieeetypes_countryinfofullset countryinfo; | ||
172 | |||
173 | struct WPA_SUPPLICANT wpa_supplicant; | ||
174 | struct WPA_SUPPLICANT wpa2_supplicant; | ||
175 | |||
176 | }; | ||
177 | |||
178 | extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, | ||
179 | struct WLAN_802_11_SSID *ssid2); | ||
180 | extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid, | ||
181 | u8 * bssid, int mode); | ||
182 | int libertas_find_best_SSID_in_list(wlan_adapter * adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode); | ||
183 | extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode); | ||
184 | |||
185 | int libertas_find_best_network_SSID(wlan_private * priv, | ||
186 | struct WLAN_802_11_SSID *pSSID, | ||
187 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, | ||
188 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode); | ||
189 | |||
190 | extern int libertas_send_specific_SSID_scan(wlan_private * priv, | ||
191 | struct WLAN_802_11_SSID *prequestedssid, | ||
192 | u8 keeppreviousscan); | ||
193 | extern int libertas_send_specific_BSSID_scan(wlan_private * priv, | ||
194 | u8 * bssid, u8 keeppreviousscan); | ||
195 | |||
196 | extern int libertas_cmd_80211_scan(wlan_private * priv, | ||
197 | struct cmd_ds_command *cmd, | ||
198 | void *pdata_buf); | ||
199 | |||
200 | extern int libertas_ret_80211_scan(wlan_private * priv, | ||
201 | struct cmd_ds_command *resp); | ||
202 | |||
203 | int wlan_scan_networks(wlan_private * priv, | ||
204 | const struct wlan_ioctl_user_scan_cfg * puserscanin); | ||
205 | |||
206 | struct ifreq; | ||
207 | |||
208 | struct iw_point; | ||
209 | struct iw_param; | ||
210 | struct iw_request_info; | ||
211 | extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, | ||
212 | struct iw_point *dwrq, char *extra); | ||
213 | extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, | ||
214 | struct iw_param *vwrq, char *extra); | ||
215 | |||
216 | #endif /* _WLAN_SCAN_H */ | ||
diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h new file mode 100644 index 000000000000..207b8a6cc33d --- /dev/null +++ b/drivers/net/wireless/libertas/thread.h | |||
@@ -0,0 +1,52 @@ | |||
1 | #ifndef __WLAN_THREAD_H_ | ||
2 | #define __WLAN_THREAD_H_ | ||
3 | |||
4 | #include <linux/kthread.h> | ||
5 | |||
6 | struct wlan_thread { | ||
7 | struct task_struct *task; | ||
8 | wait_queue_head_t waitq; | ||
9 | pid_t pid; | ||
10 | void *priv; | ||
11 | }; | ||
12 | |||
13 | static inline void wlan_activate_thread(struct wlan_thread * thr) | ||
14 | { | ||
15 | /** Record the thread pid */ | ||
16 | thr->pid = current->pid; | ||
17 | |||
18 | /** Initialize the wait queue */ | ||
19 | init_waitqueue_head(&thr->waitq); | ||
20 | } | ||
21 | |||
22 | static inline void wlan_deactivate_thread(struct wlan_thread * thr) | ||
23 | { | ||
24 | ENTER(); | ||
25 | |||
26 | thr->pid = 0; | ||
27 | |||
28 | LEAVE(); | ||
29 | } | ||
30 | |||
31 | static inline void wlan_create_thread(int (*wlanfunc) (void *), | ||
32 | struct wlan_thread * thr, char *name) | ||
33 | { | ||
34 | thr->task = kthread_run(wlanfunc, thr, "%s", name); | ||
35 | } | ||
36 | |||
37 | static inline int wlan_terminate_thread(struct wlan_thread * thr) | ||
38 | { | ||
39 | ENTER(); | ||
40 | |||
41 | /* Check if the thread is active or not */ | ||
42 | if (!thr->pid) { | ||
43 | printk(KERN_ERR "Thread does not exist\n"); | ||
44 | return -1; | ||
45 | } | ||
46 | kthread_stop(thr->task); | ||
47 | |||
48 | LEAVE(); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | #endif | ||
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c new file mode 100644 index 000000000000..82d06223043e --- /dev/null +++ b/drivers/net/wireless/libertas/tx.c | |||
@@ -0,0 +1,285 @@ | |||
1 | /** | ||
2 | * This file contains the handling of TX in wlan driver. | ||
3 | */ | ||
4 | #include <linux/netdevice.h> | ||
5 | |||
6 | #include "hostcmd.h" | ||
7 | #include "radiotap.h" | ||
8 | #include "sbi.h" | ||
9 | #include "decl.h" | ||
10 | #include "defs.h" | ||
11 | #include "dev.h" | ||
12 | #include "wext.h" | ||
13 | |||
14 | /** | ||
15 | * @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE | ||
16 | * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) | ||
17 | * | ||
18 | * @param rate Input rate | ||
19 | * @return Output Rate (0 if invalid) | ||
20 | */ | ||
21 | static u32 convert_radiotap_rate_to_mv(u8 rate) | ||
22 | { | ||
23 | switch (rate) { | ||
24 | case 2: /* 1 Mbps */ | ||
25 | return 0 | (1 << 4); | ||
26 | case 4: /* 2 Mbps */ | ||
27 | return 1 | (1 << 4); | ||
28 | case 11: /* 5.5 Mbps */ | ||
29 | return 2 | (1 << 4); | ||
30 | case 22: /* 11 Mbps */ | ||
31 | return 3 | (1 << 4); | ||
32 | case 12: /* 6 Mbps */ | ||
33 | return 4 | (1 << 4); | ||
34 | case 18: /* 9 Mbps */ | ||
35 | return 5 | (1 << 4); | ||
36 | case 24: /* 12 Mbps */ | ||
37 | return 6 | (1 << 4); | ||
38 | case 36: /* 18 Mbps */ | ||
39 | return 7 | (1 << 4); | ||
40 | case 48: /* 24 Mbps */ | ||
41 | return 8 | (1 << 4); | ||
42 | case 72: /* 36 Mbps */ | ||
43 | return 9 | (1 << 4); | ||
44 | case 96: /* 48 Mbps */ | ||
45 | return 10 | (1 << 4); | ||
46 | case 108: /* 54 Mbps */ | ||
47 | return 11 | (1 << 4); | ||
48 | } | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * @brief This function processes a single packet and sends | ||
54 | * to IF layer | ||
55 | * | ||
56 | * @param priv A pointer to wlan_private structure | ||
57 | * @param skb A pointer to skb which includes TX packet | ||
58 | * @return 0 or -1 | ||
59 | */ | ||
60 | static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb) | ||
61 | { | ||
62 | wlan_adapter *adapter = priv->adapter; | ||
63 | int ret = 0; | ||
64 | struct txpd localtxpd; | ||
65 | struct txpd *plocaltxpd = &localtxpd; | ||
66 | u8 *p802x_hdr; | ||
67 | struct tx_radiotap_hdr *pradiotap_hdr; | ||
68 | u32 new_rate; | ||
69 | u8 *ptr = priv->adapter->tmptxbuf; | ||
70 | |||
71 | ENTER(); | ||
72 | |||
73 | if (priv->adapter->surpriseremoved) | ||
74 | return -1; | ||
75 | |||
76 | if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) | ||
77 | lbs_dbg_hex("TX packet: ", skb->data, | ||
78 | min_t(unsigned int, skb->len, 100)); | ||
79 | |||
80 | if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { | ||
81 | lbs_pr_debug(1, "Tx error: Bad skb length %d : %d\n", | ||
82 | skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); | ||
83 | ret = -1; | ||
84 | goto done; | ||
85 | } | ||
86 | |||
87 | memset(plocaltxpd, 0, sizeof(struct txpd)); | ||
88 | |||
89 | plocaltxpd->tx_packet_length = skb->len; | ||
90 | |||
91 | /* offset of actual data */ | ||
92 | plocaltxpd->tx_packet_location = sizeof(struct txpd); | ||
93 | |||
94 | /* TxCtrl set by user or default */ | ||
95 | plocaltxpd->tx_control = adapter->pkttxctrl; | ||
96 | |||
97 | p802x_hdr = skb->data; | ||
98 | if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { | ||
99 | |||
100 | /* locate radiotap header */ | ||
101 | pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data; | ||
102 | |||
103 | /* set txpd fields from the radiotap header */ | ||
104 | new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate); | ||
105 | if (new_rate != 0) { | ||
106 | /* erase tx_control[4:0] */ | ||
107 | plocaltxpd->tx_control &= ~0x1f; | ||
108 | /* write new tx_control[4:0] */ | ||
109 | plocaltxpd->tx_control |= new_rate; | ||
110 | } | ||
111 | |||
112 | /* skip the radiotap header */ | ||
113 | p802x_hdr += sizeof(struct tx_radiotap_hdr); | ||
114 | plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr); | ||
115 | |||
116 | } | ||
117 | /* copy destination address from 802.3 or 802.11 header */ | ||
118 | if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) | ||
119 | memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); | ||
120 | else | ||
121 | memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); | ||
122 | |||
123 | lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd)); | ||
124 | |||
125 | if (IS_MESH_FRAME(skb)) { | ||
126 | plocaltxpd->tx_control |= TxPD_MESH_FRAME; | ||
127 | } | ||
128 | |||
129 | memcpy(ptr, plocaltxpd, sizeof(struct txpd)); | ||
130 | |||
131 | ptr += sizeof(struct txpd); | ||
132 | |||
133 | lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length); | ||
134 | memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length); | ||
135 | ret = libertas_sbi_host_to_card(priv, MVMS_DAT, | ||
136 | priv->adapter->tmptxbuf, | ||
137 | plocaltxpd->tx_packet_length + | ||
138 | sizeof(struct txpd)); | ||
139 | |||
140 | if (ret) { | ||
141 | lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret); | ||
142 | goto done; | ||
143 | } | ||
144 | |||
145 | lbs_pr_debug(1, "SendSinglePacket succeeds\n"); | ||
146 | |||
147 | done: | ||
148 | if (!ret) { | ||
149 | priv->stats.tx_packets++; | ||
150 | priv->stats.tx_bytes += skb->len; | ||
151 | } else { | ||
152 | priv->stats.tx_dropped++; | ||
153 | priv->stats.tx_errors++; | ||
154 | } | ||
155 | |||
156 | if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { | ||
157 | /* Keep the skb to echo it back once Tx feedback is | ||
158 | received from FW */ | ||
159 | skb_orphan(skb); | ||
160 | /* stop processing outgoing pkts */ | ||
161 | netif_stop_queue(priv->wlan_dev.netdev); | ||
162 | /* freeze any packets already in our queues */ | ||
163 | priv->adapter->TxLockFlag = 1; | ||
164 | } else { | ||
165 | dev_kfree_skb_any(skb); | ||
166 | priv->adapter->currenttxskb = NULL; | ||
167 | } | ||
168 | |||
169 | LEAVE(); | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | |||
174 | void libertas_tx_runqueue(wlan_private *priv) | ||
175 | { | ||
176 | wlan_adapter *adapter = priv->adapter; | ||
177 | int i; | ||
178 | |||
179 | spin_lock(&adapter->txqueue_lock); | ||
180 | for (i = 0; i < adapter->tx_queue_idx; i++) { | ||
181 | struct sk_buff *skb = adapter->tx_queue_ps[i]; | ||
182 | spin_unlock(&adapter->txqueue_lock); | ||
183 | SendSinglePacket(priv, skb); | ||
184 | spin_lock(&adapter->txqueue_lock); | ||
185 | } | ||
186 | adapter->tx_queue_idx = 0; | ||
187 | spin_unlock(&adapter->txqueue_lock); | ||
188 | } | ||
189 | |||
190 | static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb) | ||
191 | { | ||
192 | wlan_adapter *adapter = priv->adapter; | ||
193 | |||
194 | spin_lock(&adapter->txqueue_lock); | ||
195 | |||
196 | WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE); | ||
197 | adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb; | ||
198 | if (adapter->tx_queue_idx == NR_TX_QUEUE) | ||
199 | netif_stop_queue(priv->wlan_dev.netdev); | ||
200 | else | ||
201 | netif_start_queue(priv->wlan_dev.netdev); | ||
202 | |||
203 | spin_unlock(&adapter->txqueue_lock); | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * @brief This function checks the conditions and sends packet to IF | ||
208 | * layer if everything is ok. | ||
209 | * | ||
210 | * @param priv A pointer to wlan_private structure | ||
211 | * @return n/a | ||
212 | */ | ||
213 | int libertas_process_tx(wlan_private * priv, struct sk_buff *skb) | ||
214 | { | ||
215 | int ret = -1; | ||
216 | |||
217 | ENTER(); | ||
218 | |||
219 | lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100)); | ||
220 | |||
221 | if (priv->wlan_dev.dnld_sent) { | ||
222 | lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n", | ||
223 | priv->wlan_dev.dnld_sent); | ||
224 | goto done; | ||
225 | } | ||
226 | |||
227 | if ((priv->adapter->psstate == PS_STATE_SLEEP) || | ||
228 | (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) { | ||
229 | wlan_tx_queue(priv, skb); | ||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | priv->adapter->currenttxskb = skb; | ||
234 | |||
235 | ret = SendSinglePacket(priv, skb); | ||
236 | done: | ||
237 | LEAVE(); | ||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * @brief This function sends to the host the last transmitted packet, | ||
243 | * filling the radiotap headers with transmission information. | ||
244 | * | ||
245 | * @param priv A pointer to wlan_private structure | ||
246 | * @param status A 32 bit value containing transmission status. | ||
247 | * | ||
248 | * @returns void | ||
249 | */ | ||
250 | void libertas_send_tx_feedback(wlan_private * priv) | ||
251 | { | ||
252 | wlan_adapter *adapter = priv->adapter; | ||
253 | struct tx_radiotap_hdr *radiotap_hdr; | ||
254 | u32 status = adapter->eventcause; | ||
255 | int txfail; | ||
256 | int try_count; | ||
257 | |||
258 | if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP || | ||
259 | adapter->currenttxskb == NULL) | ||
260 | return; | ||
261 | |||
262 | radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data; | ||
263 | |||
264 | if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) | ||
265 | lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr, | ||
266 | min_t(unsigned int, adapter->currenttxskb->len, 100)); | ||
267 | |||
268 | txfail = (status >> 24); | ||
269 | |||
270 | #if 0 | ||
271 | /* The version of roofnet that we've tested does not use this yet | ||
272 | * But it may be used in the future. | ||
273 | */ | ||
274 | if (txfail) | ||
275 | radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL; | ||
276 | #endif | ||
277 | try_count = (status >> 16) & 0xff; | ||
278 | radiotap_hdr->data_retries = (try_count) ? | ||
279 | (1 + adapter->txretrycount - try_count) : 0; | ||
280 | libertas_upload_rx_packet(priv, adapter->currenttxskb); | ||
281 | adapter->currenttxskb = NULL; | ||
282 | priv->adapter->TxLockFlag = 0; | ||
283 | if (priv->adapter->connect_status == libertas_connected) | ||
284 | netif_wake_queue(priv->wlan_dev.netdev); | ||
285 | } | ||
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h new file mode 100644 index 000000000000..09d62f8b1a16 --- /dev/null +++ b/drivers/net/wireless/libertas/types.h | |||
@@ -0,0 +1,289 @@ | |||
1 | /** | ||
2 | * This header file contains definition for global types | ||
3 | */ | ||
4 | #ifndef _WLAN_TYPES_ | ||
5 | #define _WLAN_TYPES_ | ||
6 | |||
7 | #include <linux/if_ether.h> | ||
8 | |||
9 | /** IEEE type definitions */ | ||
10 | enum ieeetypes_elementid { | ||
11 | SSID = 0, | ||
12 | SUPPORTED_RATES, | ||
13 | FH_PARAM_SET, | ||
14 | DS_PARAM_SET, | ||
15 | CF_PARAM_SET, | ||
16 | TIM, | ||
17 | IBSS_PARAM_SET, | ||
18 | COUNTRY_INFO = 7, | ||
19 | |||
20 | CHALLENGE_TEXT = 16, | ||
21 | |||
22 | EXTENDED_SUPPORTED_RATES = 50, | ||
23 | |||
24 | VENDOR_SPECIFIC_221 = 221, | ||
25 | |||
26 | WPA_IE = 221, | ||
27 | WPA2_IE = 48, | ||
28 | |||
29 | EXTRA_IE = 133, | ||
30 | } __attribute__ ((packed)); | ||
31 | |||
32 | #define CAPINFO_MASK (~(0xda00)) | ||
33 | |||
34 | struct ieeetypes_capinfo { | ||
35 | u8 ess:1; | ||
36 | u8 ibss:1; | ||
37 | u8 cfpollable:1; | ||
38 | u8 cfpollrqst:1; | ||
39 | u8 privacy:1; | ||
40 | u8 shortpreamble:1; | ||
41 | u8 pbcc:1; | ||
42 | u8 chanagility:1; | ||
43 | u8 spectrummgmt:1; | ||
44 | u8 rsrvd3:1; | ||
45 | u8 shortslottime:1; | ||
46 | u8 apsd:1; | ||
47 | u8 rsvrd2:1; | ||
48 | u8 dsssofdm:1; | ||
49 | u8 rsrvd1:2; | ||
50 | } __attribute__ ((packed)); | ||
51 | |||
52 | struct ieeetypes_cfparamset { | ||
53 | u8 elementid; | ||
54 | u8 len; | ||
55 | u8 cfpcnt; | ||
56 | u8 cfpperiod; | ||
57 | u16 cfpmaxduration; | ||
58 | u16 cfpdurationremaining; | ||
59 | } __attribute__ ((packed)); | ||
60 | |||
61 | |||
62 | struct ieeetypes_ibssparamset { | ||
63 | u8 elementid; | ||
64 | u8 len; | ||
65 | u16 atimwindow; | ||
66 | } __attribute__ ((packed)); | ||
67 | |||
68 | union IEEEtypes_ssparamset { | ||
69 | struct ieeetypes_cfparamset cfparamset; | ||
70 | struct ieeetypes_ibssparamset ibssparamset; | ||
71 | } __attribute__ ((packed)); | ||
72 | |||
73 | struct ieeetypes_fhparamset { | ||
74 | u8 elementid; | ||
75 | u8 len; | ||
76 | u16 dwelltime; | ||
77 | u8 hopset; | ||
78 | u8 hoppattern; | ||
79 | u8 hopindex; | ||
80 | } __attribute__ ((packed)); | ||
81 | |||
82 | struct ieeetypes_dsparamset { | ||
83 | u8 elementid; | ||
84 | u8 len; | ||
85 | u8 currentchan; | ||
86 | } __attribute__ ((packed)); | ||
87 | |||
88 | union ieeetypes_phyparamset { | ||
89 | struct ieeetypes_fhparamset fhparamset; | ||
90 | struct ieeetypes_dsparamset dsparamset; | ||
91 | } __attribute__ ((packed)); | ||
92 | |||
93 | struct ieeetypes_assocrsp { | ||
94 | struct ieeetypes_capinfo capability; | ||
95 | u16 statuscode; | ||
96 | u16 aid; | ||
97 | u8 iebuffer[1]; | ||
98 | } __attribute__ ((packed)); | ||
99 | |||
100 | /** TLV type ID definition */ | ||
101 | #define PROPRIETARY_TLV_BASE_ID 0x0100 | ||
102 | |||
103 | /* Terminating TLV type */ | ||
104 | #define MRVL_TERMINATE_TLV_ID 0xffff | ||
105 | |||
106 | #define TLV_TYPE_SSID 0x0000 | ||
107 | #define TLV_TYPE_RATES 0x0001 | ||
108 | #define TLV_TYPE_PHY_FH 0x0002 | ||
109 | #define TLV_TYPE_PHY_DS 0x0003 | ||
110 | #define TLV_TYPE_CF 0x0004 | ||
111 | #define TLV_TYPE_IBSS 0x0006 | ||
112 | |||
113 | #define TLV_TYPE_DOMAIN 0x0007 | ||
114 | |||
115 | #define TLV_TYPE_POWER_CAPABILITY 0x0021 | ||
116 | |||
117 | #define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) | ||
118 | #define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) | ||
119 | #define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) | ||
120 | #define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) | ||
121 | #define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) | ||
122 | #define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) | ||
123 | #define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) | ||
124 | #define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) | ||
125 | #define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) | ||
126 | #define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) | ||
127 | #define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) | ||
128 | #define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) | ||
129 | #define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) | ||
130 | #define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) | ||
131 | #define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) | ||
132 | #define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) | ||
133 | #define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) | ||
134 | #define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) | ||
135 | #define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) | ||
136 | #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) | ||
137 | #define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) | ||
138 | |||
139 | /** TLV related data structures*/ | ||
140 | struct mrvlietypesheader { | ||
141 | u16 type; | ||
142 | u16 len; | ||
143 | } __attribute__ ((packed)); | ||
144 | |||
145 | struct mrvlietypes_data { | ||
146 | struct mrvlietypesheader header; | ||
147 | u8 Data[1]; | ||
148 | } __attribute__ ((packed)); | ||
149 | |||
150 | struct mrvlietypes_ratesparamset { | ||
151 | struct mrvlietypesheader header; | ||
152 | u8 rates[1]; | ||
153 | } __attribute__ ((packed)); | ||
154 | |||
155 | struct mrvlietypes_ssidparamset { | ||
156 | struct mrvlietypesheader header; | ||
157 | u8 ssid[1]; | ||
158 | } __attribute__ ((packed)); | ||
159 | |||
160 | struct mrvlietypes_wildcardssidparamset { | ||
161 | struct mrvlietypesheader header; | ||
162 | u8 MaxSsidlength; | ||
163 | u8 ssid[1]; | ||
164 | } __attribute__ ((packed)); | ||
165 | |||
166 | struct chanscanmode { | ||
167 | u8 passivescan:1; | ||
168 | u8 disablechanfilt:1; | ||
169 | u8 reserved_2_7:6; | ||
170 | } __attribute__ ((packed)); | ||
171 | |||
172 | struct chanscanparamset { | ||
173 | u8 radiotype; | ||
174 | u8 channumber; | ||
175 | struct chanscanmode chanscanmode; | ||
176 | u16 minscantime; | ||
177 | u16 maxscantime; | ||
178 | } __attribute__ ((packed)); | ||
179 | |||
180 | struct mrvlietypes_chanlistparamset { | ||
181 | struct mrvlietypesheader header; | ||
182 | struct chanscanparamset chanscanparam[1]; | ||
183 | } __attribute__ ((packed)); | ||
184 | |||
185 | struct cfparamset { | ||
186 | u8 cfpcnt; | ||
187 | u8 cfpperiod; | ||
188 | u16 cfpmaxduration; | ||
189 | u16 cfpdurationremaining; | ||
190 | } __attribute__ ((packed)); | ||
191 | |||
192 | struct ibssparamset { | ||
193 | u16 atimwindow; | ||
194 | } __attribute__ ((packed)); | ||
195 | |||
196 | struct mrvlietypes_ssparamset { | ||
197 | struct mrvlietypesheader header; | ||
198 | union { | ||
199 | struct cfparamset cfparamset[1]; | ||
200 | struct ibssparamset ibssparamset[1]; | ||
201 | } cf_ibss; | ||
202 | } __attribute__ ((packed)); | ||
203 | |||
204 | struct fhparamset { | ||
205 | u16 dwelltime; | ||
206 | u8 hopset; | ||
207 | u8 hoppattern; | ||
208 | u8 hopindex; | ||
209 | } __attribute__ ((packed)); | ||
210 | |||
211 | struct dsparamset { | ||
212 | u8 currentchan; | ||
213 | } __attribute__ ((packed)); | ||
214 | |||
215 | struct mrvlietypes_phyparamset { | ||
216 | struct mrvlietypesheader header; | ||
217 | union { | ||
218 | struct fhparamset fhparamset[1]; | ||
219 | struct dsparamset dsparamset[1]; | ||
220 | } fh_ds; | ||
221 | } __attribute__ ((packed)); | ||
222 | |||
223 | struct mrvlietypes_rsnparamset { | ||
224 | struct mrvlietypesheader header; | ||
225 | u8 rsnie[1]; | ||
226 | } __attribute__ ((packed)); | ||
227 | |||
228 | struct mrvlietypes_tsftimestamp { | ||
229 | struct mrvlietypesheader header; | ||
230 | __le64 tsftable[1]; | ||
231 | } __attribute__ ((packed)); | ||
232 | |||
233 | /** Local Power capability */ | ||
234 | struct mrvlietypes_powercapability { | ||
235 | struct mrvlietypesheader header; | ||
236 | s8 minpower; | ||
237 | s8 maxpower; | ||
238 | } __attribute__ ((packed)); | ||
239 | |||
240 | struct mrvlietypes_rssithreshold { | ||
241 | struct mrvlietypesheader header; | ||
242 | u8 rssivalue; | ||
243 | u8 rssifreq; | ||
244 | } __attribute__ ((packed)); | ||
245 | |||
246 | struct mrvlietypes_snrthreshold { | ||
247 | struct mrvlietypesheader header; | ||
248 | u8 snrvalue; | ||
249 | u8 snrfreq; | ||
250 | } __attribute__ ((packed)); | ||
251 | |||
252 | struct mrvlietypes_failurecount { | ||
253 | struct mrvlietypesheader header; | ||
254 | u8 failvalue; | ||
255 | u8 Failfreq; | ||
256 | } __attribute__ ((packed)); | ||
257 | |||
258 | struct mrvlietypes_beaconsmissed { | ||
259 | struct mrvlietypesheader header; | ||
260 | u8 beaconmissed; | ||
261 | u8 reserved; | ||
262 | } __attribute__ ((packed)); | ||
263 | |||
264 | struct mrvlietypes_numprobes { | ||
265 | struct mrvlietypesheader header; | ||
266 | u16 numprobes; | ||
267 | } __attribute__ ((packed)); | ||
268 | |||
269 | struct mrvlietypes_bcastprobe { | ||
270 | struct mrvlietypesheader header; | ||
271 | u16 bcastprobe; | ||
272 | } __attribute__ ((packed)); | ||
273 | |||
274 | struct mrvlietypes_numssidprobe { | ||
275 | struct mrvlietypesheader header; | ||
276 | u16 numssidprobe; | ||
277 | } __attribute__ ((packed)); | ||
278 | |||
279 | struct led_pin { | ||
280 | u8 led; | ||
281 | u8 pin; | ||
282 | } __attribute__ ((packed)); | ||
283 | |||
284 | struct mrvlietypes_ledgpio { | ||
285 | struct mrvlietypesheader header; | ||
286 | struct led_pin ledpin[1]; | ||
287 | } __attribute__ ((packed)); | ||
288 | |||
289 | #endif /* _WLAN_TYPES_ */ | ||
diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h new file mode 100644 index 000000000000..e86f65ae79b8 --- /dev/null +++ b/drivers/net/wireless/libertas/version.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #define DRIVER_RELEASE_VERSION "320.p0" | ||
2 | const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION | ||
3 | #ifdef DEBUG | ||
4 | "-dbg" | ||
5 | #endif | ||
6 | ""; | ||
7 | |||
8 | |||
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c new file mode 100644 index 000000000000..4a52336bc0f6 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.c | |||
@@ -0,0 +1,2769 @@ | |||
1 | /** | ||
2 | * This file contains ioctl functions | ||
3 | */ | ||
4 | #include <linux/ctype.h> | ||
5 | #include <linux/delay.h> | ||
6 | #include <linux/if.h> | ||
7 | #include <linux/if_arp.h> | ||
8 | #include <linux/wireless.h> | ||
9 | #include <linux/bitops.h> | ||
10 | |||
11 | #include <net/ieee80211.h> | ||
12 | #include <net/iw_handler.h> | ||
13 | |||
14 | #include "host.h" | ||
15 | #include "radiotap.h" | ||
16 | #include "decl.h" | ||
17 | #include "defs.h" | ||
18 | #include "dev.h" | ||
19 | #include "join.h" | ||
20 | #include "version.h" | ||
21 | #include "wext.h" | ||
22 | #include "assoc.h" | ||
23 | |||
24 | |||
25 | /** | ||
26 | * @brief Convert mw value to dbm value | ||
27 | * | ||
28 | * @param mw the value of mw | ||
29 | * @return the value of dbm | ||
30 | */ | ||
31 | static int mw_to_dbm(int mw) | ||
32 | { | ||
33 | if (mw < 2) | ||
34 | return 0; | ||
35 | else if (mw < 3) | ||
36 | return 3; | ||
37 | else if (mw < 4) | ||
38 | return 5; | ||
39 | else if (mw < 6) | ||
40 | return 7; | ||
41 | else if (mw < 7) | ||
42 | return 8; | ||
43 | else if (mw < 8) | ||
44 | return 9; | ||
45 | else if (mw < 10) | ||
46 | return 10; | ||
47 | else if (mw < 13) | ||
48 | return 11; | ||
49 | else if (mw < 16) | ||
50 | return 12; | ||
51 | else if (mw < 20) | ||
52 | return 13; | ||
53 | else if (mw < 25) | ||
54 | return 14; | ||
55 | else if (mw < 32) | ||
56 | return 15; | ||
57 | else if (mw < 40) | ||
58 | return 16; | ||
59 | else if (mw < 50) | ||
60 | return 17; | ||
61 | else if (mw < 63) | ||
62 | return 18; | ||
63 | else if (mw < 79) | ||
64 | return 19; | ||
65 | else if (mw < 100) | ||
66 | return 20; | ||
67 | else | ||
68 | return 21; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * @brief Find the channel frequency power info with specific channel | ||
73 | * | ||
74 | * @param adapter A pointer to wlan_adapter structure | ||
75 | * @param band it can be BAND_A, BAND_G or BAND_B | ||
76 | * @param channel the channel for looking | ||
77 | * @return A pointer to struct chan_freq_power structure or NULL if not find. | ||
78 | */ | ||
79 | struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter, | ||
80 | u8 band, u16 channel) | ||
81 | { | ||
82 | struct chan_freq_power *cfp = NULL; | ||
83 | struct region_channel *rc; | ||
84 | int count = sizeof(adapter->region_channel) / | ||
85 | sizeof(adapter->region_channel[0]); | ||
86 | int i, j; | ||
87 | |||
88 | for (j = 0; !cfp && (j < count); j++) { | ||
89 | rc = &adapter->region_channel[j]; | ||
90 | |||
91 | if (adapter->enable11d) | ||
92 | rc = &adapter->universal_channel[j]; | ||
93 | if (!rc->valid || !rc->CFP) | ||
94 | continue; | ||
95 | if (rc->band != band) | ||
96 | continue; | ||
97 | for (i = 0; i < rc->nrcfp; i++) { | ||
98 | if (rc->CFP[i].channel == channel) { | ||
99 | cfp = &rc->CFP[i]; | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | if (!cfp && channel) | ||
106 | lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find " | ||
107 | "cfp by band %d & channel %d\n", band, channel); | ||
108 | |||
109 | return cfp; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @brief Find the channel frequency power info with specific frequency | ||
114 | * | ||
115 | * @param adapter A pointer to wlan_adapter structure | ||
116 | * @param band it can be BAND_A, BAND_G or BAND_B | ||
117 | * @param freq the frequency for looking | ||
118 | * @return A pointer to struct chan_freq_power structure or NULL if not find. | ||
119 | */ | ||
120 | static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter, | ||
121 | u8 band, u32 freq) | ||
122 | { | ||
123 | struct chan_freq_power *cfp = NULL; | ||
124 | struct region_channel *rc; | ||
125 | int count = sizeof(adapter->region_channel) / | ||
126 | sizeof(adapter->region_channel[0]); | ||
127 | int i, j; | ||
128 | |||
129 | for (j = 0; !cfp && (j < count); j++) { | ||
130 | rc = &adapter->region_channel[j]; | ||
131 | |||
132 | if (adapter->enable11d) | ||
133 | rc = &adapter->universal_channel[j]; | ||
134 | if (!rc->valid || !rc->CFP) | ||
135 | continue; | ||
136 | if (rc->band != band) | ||
137 | continue; | ||
138 | for (i = 0; i < rc->nrcfp; i++) { | ||
139 | if (rc->CFP[i].freq == freq) { | ||
140 | cfp = &rc->CFP[i]; | ||
141 | break; | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | if (!cfp && freq) | ||
147 | lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by " | ||
148 | "band %d & freq %d\n", band, freq); | ||
149 | |||
150 | return cfp; | ||
151 | } | ||
152 | |||
153 | static int updatecurrentchannel(wlan_private * priv) | ||
154 | { | ||
155 | int ret; | ||
156 | |||
157 | /* | ||
158 | ** the channel in f/w could be out of sync, get the current channel | ||
159 | */ | ||
160 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, | ||
161 | cmd_opt_802_11_rf_channel_get, | ||
162 | cmd_option_waitforrsp, 0, NULL); | ||
163 | |||
164 | lbs_pr_debug(1, "Current channel = %d\n", | ||
165 | priv->adapter->curbssparams.channel); | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static int setcurrentchannel(wlan_private * priv, int channel) | ||
171 | { | ||
172 | lbs_pr_debug(1, "Set channel = %d\n", channel); | ||
173 | |||
174 | /* | ||
175 | ** Current channel is not set to adhocchannel requested, set channel | ||
176 | */ | ||
177 | return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, | ||
178 | cmd_opt_802_11_rf_channel_set, | ||
179 | cmd_option_waitforrsp, 0, &channel)); | ||
180 | } | ||
181 | |||
182 | static int changeadhocchannel(wlan_private * priv, int channel) | ||
183 | { | ||
184 | int ret = 0; | ||
185 | wlan_adapter *adapter = priv->adapter; | ||
186 | |||
187 | adapter->adhocchannel = channel; | ||
188 | |||
189 | updatecurrentchannel(priv); | ||
190 | |||
191 | if (adapter->curbssparams.channel == adapter->adhocchannel) { | ||
192 | /* adhocchannel is set to the current channel already */ | ||
193 | LEAVE(); | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | lbs_pr_debug(1, "Updating channel from %d to %d\n", | ||
198 | adapter->curbssparams.channel, adapter->adhocchannel); | ||
199 | |||
200 | setcurrentchannel(priv, adapter->adhocchannel); | ||
201 | |||
202 | updatecurrentchannel(priv); | ||
203 | |||
204 | if (adapter->curbssparams.channel != adapter->adhocchannel) { | ||
205 | lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n", | ||
206 | adapter->adhocchannel, adapter->curbssparams.channel); | ||
207 | LEAVE(); | ||
208 | return -1; | ||
209 | } | ||
210 | |||
211 | if (adapter->connect_status == libertas_connected) { | ||
212 | int i; | ||
213 | struct WLAN_802_11_SSID curadhocssid; | ||
214 | |||
215 | lbs_pr_debug(1, "channel Changed while in an IBSS\n"); | ||
216 | |||
217 | /* Copy the current ssid */ | ||
218 | memcpy(&curadhocssid, &adapter->curbssparams.ssid, | ||
219 | sizeof(struct WLAN_802_11_SSID)); | ||
220 | |||
221 | /* Exit Adhoc mode */ | ||
222 | lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n"); | ||
223 | ret = libertas_stop_adhoc_network(priv); | ||
224 | |||
225 | if (ret) { | ||
226 | LEAVE(); | ||
227 | return ret; | ||
228 | } | ||
229 | /* Scan for the network, do not save previous results. Stale | ||
230 | * scan data will cause us to join a non-existant adhoc network | ||
231 | */ | ||
232 | libertas_send_specific_SSID_scan(priv, &curadhocssid, 0); | ||
233 | |||
234 | // find out the BSSID that matches the current SSID | ||
235 | i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL, | ||
236 | wlan802_11ibss); | ||
237 | |||
238 | if (i >= 0) { | ||
239 | lbs_pr_debug(1, "SSID found at %d in List," | ||
240 | "so join\n", i); | ||
241 | libertas_join_adhoc_network(priv, &adapter->scantable[i]); | ||
242 | } else { | ||
243 | // else send START command | ||
244 | lbs_pr_debug(1, "SSID not found in list, " | ||
245 | "so creating adhoc with ssid = %s\n", | ||
246 | curadhocssid.ssid); | ||
247 | libertas_start_adhoc_network(priv, &curadhocssid); | ||
248 | } // end of else (START command) | ||
249 | } | ||
250 | |||
251 | LEAVE(); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * @brief Set Radio On/OFF | ||
257 | * | ||
258 | * @param priv A pointer to wlan_private structure | ||
259 | * @option Radio Option | ||
260 | * @return 0 --success, otherwise fail | ||
261 | */ | ||
262 | int wlan_radio_ioctl(wlan_private * priv, u8 option) | ||
263 | { | ||
264 | int ret = 0; | ||
265 | wlan_adapter *adapter = priv->adapter; | ||
266 | |||
267 | ENTER(); | ||
268 | |||
269 | if (adapter->radioon != option) { | ||
270 | lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off"); | ||
271 | adapter->radioon = option; | ||
272 | |||
273 | ret = libertas_prepare_and_send_command(priv, | ||
274 | cmd_802_11_radio_control, | ||
275 | cmd_act_set, | ||
276 | cmd_option_waitforrsp, 0, NULL); | ||
277 | } | ||
278 | |||
279 | LEAVE(); | ||
280 | return ret; | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * @brief Copy rates | ||
285 | * | ||
286 | * @param dest A pointer to Dest Buf | ||
287 | * @param src A pointer to Src Buf | ||
288 | * @param len The len of Src Buf | ||
289 | * @return Number of rates copyed | ||
290 | */ | ||
291 | static inline int copyrates(u8 * dest, int pos, u8 * src, int len) | ||
292 | { | ||
293 | int i; | ||
294 | |||
295 | for (i = 0; i < len && src[i]; i++, pos++) { | ||
296 | if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES) | ||
297 | break; | ||
298 | dest[pos] = src[i]; | ||
299 | } | ||
300 | |||
301 | return pos; | ||
302 | } | ||
303 | |||
304 | /** | ||
305 | * @brief Get active data rates | ||
306 | * | ||
307 | * @param adapter A pointer to wlan_adapter structure | ||
308 | * @param rate The buf to return the active rates | ||
309 | * @return The number of rates | ||
310 | */ | ||
311 | static int get_active_data_rates(wlan_adapter * adapter, | ||
312 | u8* rates) | ||
313 | { | ||
314 | int k = 0; | ||
315 | |||
316 | ENTER(); | ||
317 | |||
318 | if (adapter->connect_status != libertas_connected) { | ||
319 | if (adapter->inframode == wlan802_11infrastructure) { | ||
320 | //Infra. mode | ||
321 | lbs_pr_debug(1, "Infra\n"); | ||
322 | k = copyrates(rates, k, libertas_supported_rates, | ||
323 | sizeof(libertas_supported_rates)); | ||
324 | } else { | ||
325 | //ad-hoc mode | ||
326 | lbs_pr_debug(1, "Adhoc G\n"); | ||
327 | k = copyrates(rates, k, libertas_adhoc_rates_g, | ||
328 | sizeof(libertas_adhoc_rates_g)); | ||
329 | } | ||
330 | } else { | ||
331 | k = copyrates(rates, 0, adapter->curbssparams.datarates, | ||
332 | adapter->curbssparams.numofrates); | ||
333 | } | ||
334 | |||
335 | LEAVE(); | ||
336 | |||
337 | return k; | ||
338 | } | ||
339 | |||
340 | static int wlan_get_name(struct net_device *dev, struct iw_request_info *info, | ||
341 | char *cwrq, char *extra) | ||
342 | { | ||
343 | const char *cp; | ||
344 | char comm[6] = { "COMM-" }; | ||
345 | char mrvl[6] = { "MRVL-" }; | ||
346 | int cnt; | ||
347 | |||
348 | ENTER(); | ||
349 | |||
350 | strcpy(cwrq, mrvl); | ||
351 | |||
352 | cp = strstr(libertas_driver_version, comm); | ||
353 | if (cp == libertas_driver_version) //skip leading "COMM-" | ||
354 | cp = libertas_driver_version + strlen(comm); | ||
355 | else | ||
356 | cp = libertas_driver_version; | ||
357 | |||
358 | cnt = strlen(mrvl); | ||
359 | cwrq += cnt; | ||
360 | while (cnt < 16 && (*cp != '-')) { | ||
361 | *cwrq++ = toupper(*cp++); | ||
362 | cnt++; | ||
363 | } | ||
364 | *cwrq = '\0'; | ||
365 | |||
366 | LEAVE(); | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info, | ||
372 | struct iw_freq *fwrq, char *extra) | ||
373 | { | ||
374 | wlan_private *priv = dev->priv; | ||
375 | wlan_adapter *adapter = priv->adapter; | ||
376 | struct chan_freq_power *cfp; | ||
377 | |||
378 | ENTER(); | ||
379 | |||
380 | cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, | ||
381 | adapter->curbssparams.channel); | ||
382 | |||
383 | if (!cfp) { | ||
384 | if (adapter->curbssparams.channel) | ||
385 | lbs_pr_debug(1, "Invalid channel=%d\n", | ||
386 | adapter->curbssparams.channel); | ||
387 | return -EINVAL; | ||
388 | } | ||
389 | |||
390 | fwrq->m = (long)cfp->freq * 100000; | ||
391 | fwrq->e = 1; | ||
392 | |||
393 | lbs_pr_debug(1, "freq=%u\n", fwrq->m); | ||
394 | |||
395 | LEAVE(); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info, | ||
400 | struct sockaddr *awrq, char *extra) | ||
401 | { | ||
402 | wlan_private *priv = dev->priv; | ||
403 | wlan_adapter *adapter = priv->adapter; | ||
404 | |||
405 | ENTER(); | ||
406 | |||
407 | if (adapter->connect_status == libertas_connected) { | ||
408 | memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN); | ||
409 | } else { | ||
410 | memset(awrq->sa_data, 0, ETH_ALEN); | ||
411 | } | ||
412 | awrq->sa_family = ARPHRD_ETHER; | ||
413 | |||
414 | LEAVE(); | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info, | ||
419 | struct iw_point *dwrq, char *extra) | ||
420 | { | ||
421 | wlan_private *priv = dev->priv; | ||
422 | wlan_adapter *adapter = priv->adapter; | ||
423 | |||
424 | ENTER(); | ||
425 | |||
426 | /* | ||
427 | * Check the size of the string | ||
428 | */ | ||
429 | |||
430 | if (dwrq->length > 16) { | ||
431 | return -E2BIG; | ||
432 | } | ||
433 | |||
434 | mutex_lock(&adapter->lock); | ||
435 | memset(adapter->nodename, 0, sizeof(adapter->nodename)); | ||
436 | memcpy(adapter->nodename, extra, dwrq->length); | ||
437 | mutex_unlock(&adapter->lock); | ||
438 | |||
439 | LEAVE(); | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info, | ||
444 | struct iw_point *dwrq, char *extra) | ||
445 | { | ||
446 | wlan_private *priv = dev->priv; | ||
447 | wlan_adapter *adapter = priv->adapter; | ||
448 | |||
449 | ENTER(); | ||
450 | |||
451 | /* | ||
452 | * Get the Nick Name saved | ||
453 | */ | ||
454 | |||
455 | mutex_lock(&adapter->lock); | ||
456 | strncpy(extra, adapter->nodename, 16); | ||
457 | mutex_unlock(&adapter->lock); | ||
458 | |||
459 | extra[16] = '\0'; | ||
460 | |||
461 | /* | ||
462 | * If none, we may want to get the one that was set | ||
463 | */ | ||
464 | |||
465 | /* | ||
466 | * Push it out ! | ||
467 | */ | ||
468 | dwrq->length = strlen(extra) + 1; | ||
469 | |||
470 | LEAVE(); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info, | ||
475 | struct iw_param *vwrq, char *extra) | ||
476 | { | ||
477 | int ret = 0; | ||
478 | wlan_private *priv = dev->priv; | ||
479 | wlan_adapter *adapter = priv->adapter; | ||
480 | int rthr = vwrq->value; | ||
481 | |||
482 | ENTER(); | ||
483 | |||
484 | if (vwrq->disabled) { | ||
485 | adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE; | ||
486 | } else { | ||
487 | if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE) | ||
488 | return -EINVAL; | ||
489 | adapter->rtsthsd = rthr; | ||
490 | } | ||
491 | |||
492 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, | ||
493 | cmd_act_set, cmd_option_waitforrsp, | ||
494 | OID_802_11_RTS_THRESHOLD, &rthr); | ||
495 | |||
496 | LEAVE(); | ||
497 | return ret; | ||
498 | } | ||
499 | |||
500 | static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info, | ||
501 | struct iw_param *vwrq, char *extra) | ||
502 | { | ||
503 | int ret = 0; | ||
504 | wlan_private *priv = dev->priv; | ||
505 | wlan_adapter *adapter = priv->adapter; | ||
506 | |||
507 | ENTER(); | ||
508 | |||
509 | adapter->rtsthsd = 0; | ||
510 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, | ||
511 | cmd_act_get, cmd_option_waitforrsp, | ||
512 | OID_802_11_RTS_THRESHOLD, NULL); | ||
513 | if (ret) { | ||
514 | LEAVE(); | ||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | vwrq->value = adapter->rtsthsd; | ||
519 | vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE) | ||
520 | || (vwrq->value > MRVDRV_RTS_MAX_VALUE)); | ||
521 | vwrq->fixed = 1; | ||
522 | |||
523 | LEAVE(); | ||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info, | ||
528 | struct iw_param *vwrq, char *extra) | ||
529 | { | ||
530 | int ret = 0; | ||
531 | int fthr = vwrq->value; | ||
532 | wlan_private *priv = dev->priv; | ||
533 | wlan_adapter *adapter = priv->adapter; | ||
534 | |||
535 | ENTER(); | ||
536 | |||
537 | if (vwrq->disabled) { | ||
538 | adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE; | ||
539 | } else { | ||
540 | if (fthr < MRVDRV_FRAG_MIN_VALUE | ||
541 | || fthr > MRVDRV_FRAG_MAX_VALUE) | ||
542 | return -EINVAL; | ||
543 | adapter->fragthsd = fthr; | ||
544 | } | ||
545 | |||
546 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, | ||
547 | cmd_act_set, cmd_option_waitforrsp, | ||
548 | OID_802_11_FRAGMENTATION_THRESHOLD, &fthr); | ||
549 | LEAVE(); | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info, | ||
554 | struct iw_param *vwrq, char *extra) | ||
555 | { | ||
556 | int ret = 0; | ||
557 | wlan_private *priv = dev->priv; | ||
558 | wlan_adapter *adapter = priv->adapter; | ||
559 | |||
560 | ENTER(); | ||
561 | |||
562 | adapter->fragthsd = 0; | ||
563 | ret = libertas_prepare_and_send_command(priv, | ||
564 | cmd_802_11_snmp_mib, | ||
565 | cmd_act_get, cmd_option_waitforrsp, | ||
566 | OID_802_11_FRAGMENTATION_THRESHOLD, NULL); | ||
567 | if (ret) { | ||
568 | LEAVE(); | ||
569 | return ret; | ||
570 | } | ||
571 | |||
572 | vwrq->value = adapter->fragthsd; | ||
573 | vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE) | ||
574 | || (vwrq->value > MRVDRV_FRAG_MAX_VALUE)); | ||
575 | vwrq->fixed = 1; | ||
576 | |||
577 | LEAVE(); | ||
578 | return ret; | ||
579 | } | ||
580 | |||
581 | static int wlan_get_mode(struct net_device *dev, | ||
582 | struct iw_request_info *info, u32 * uwrq, char *extra) | ||
583 | { | ||
584 | wlan_private *priv = dev->priv; | ||
585 | wlan_adapter *adapter = priv->adapter; | ||
586 | |||
587 | ENTER(); | ||
588 | |||
589 | switch (adapter->inframode) { | ||
590 | case wlan802_11ibss: | ||
591 | *uwrq = IW_MODE_ADHOC; | ||
592 | break; | ||
593 | |||
594 | case wlan802_11infrastructure: | ||
595 | *uwrq = IW_MODE_INFRA; | ||
596 | break; | ||
597 | |||
598 | default: | ||
599 | case wlan802_11autounknown: | ||
600 | *uwrq = IW_MODE_AUTO; | ||
601 | break; | ||
602 | } | ||
603 | |||
604 | LEAVE(); | ||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static int wlan_get_txpow(struct net_device *dev, | ||
609 | struct iw_request_info *info, | ||
610 | struct iw_param *vwrq, char *extra) | ||
611 | { | ||
612 | int ret = 0; | ||
613 | wlan_private *priv = dev->priv; | ||
614 | wlan_adapter *adapter = priv->adapter; | ||
615 | |||
616 | ENTER(); | ||
617 | |||
618 | ret = libertas_prepare_and_send_command(priv, | ||
619 | cmd_802_11_rf_tx_power, | ||
620 | cmd_act_tx_power_opt_get, | ||
621 | cmd_option_waitforrsp, 0, NULL); | ||
622 | |||
623 | if (ret) { | ||
624 | LEAVE(); | ||
625 | return ret; | ||
626 | } | ||
627 | |||
628 | lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel); | ||
629 | vwrq->value = adapter->txpowerlevel; | ||
630 | vwrq->fixed = 1; | ||
631 | if (adapter->radioon) { | ||
632 | vwrq->disabled = 0; | ||
633 | vwrq->flags = IW_TXPOW_DBM; | ||
634 | } else { | ||
635 | vwrq->disabled = 1; | ||
636 | } | ||
637 | |||
638 | LEAVE(); | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info, | ||
643 | struct iw_param *vwrq, char *extra) | ||
644 | { | ||
645 | int ret = 0; | ||
646 | wlan_private *priv = dev->priv; | ||
647 | wlan_adapter *adapter = priv->adapter; | ||
648 | |||
649 | ENTER(); | ||
650 | |||
651 | if (vwrq->flags == IW_RETRY_LIMIT) { | ||
652 | /* The MAC has a 4-bit Total_Tx_Count register | ||
653 | Total_Tx_Count = 1 + Tx_Retry_Count */ | ||
654 | #define TX_RETRY_MIN 0 | ||
655 | #define TX_RETRY_MAX 14 | ||
656 | if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX) | ||
657 | return -EINVAL; | ||
658 | |||
659 | /* Adding 1 to convert retry count to try count */ | ||
660 | adapter->txretrycount = vwrq->value + 1; | ||
661 | |||
662 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, | ||
663 | cmd_act_set, | ||
664 | cmd_option_waitforrsp, | ||
665 | OID_802_11_TX_RETRYCOUNT, NULL); | ||
666 | |||
667 | if (ret) { | ||
668 | LEAVE(); | ||
669 | return ret; | ||
670 | } | ||
671 | } else { | ||
672 | return -EOPNOTSUPP; | ||
673 | } | ||
674 | |||
675 | LEAVE(); | ||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info, | ||
680 | struct iw_param *vwrq, char *extra) | ||
681 | { | ||
682 | wlan_private *priv = dev->priv; | ||
683 | wlan_adapter *adapter = priv->adapter; | ||
684 | int ret = 0; | ||
685 | |||
686 | ENTER(); | ||
687 | adapter->txretrycount = 0; | ||
688 | ret = libertas_prepare_and_send_command(priv, | ||
689 | cmd_802_11_snmp_mib, | ||
690 | cmd_act_get, cmd_option_waitforrsp, | ||
691 | OID_802_11_TX_RETRYCOUNT, NULL); | ||
692 | if (ret) { | ||
693 | LEAVE(); | ||
694 | return ret; | ||
695 | } | ||
696 | vwrq->disabled = 0; | ||
697 | if (!vwrq->flags) { | ||
698 | vwrq->flags = IW_RETRY_LIMIT; | ||
699 | /* Subtract 1 to convert try count to retry count */ | ||
700 | vwrq->value = adapter->txretrycount - 1; | ||
701 | } | ||
702 | |||
703 | LEAVE(); | ||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static inline void sort_channels(struct iw_freq *freq, int num) | ||
708 | { | ||
709 | int i, j; | ||
710 | struct iw_freq temp; | ||
711 | |||
712 | for (i = 0; i < num; i++) | ||
713 | for (j = i + 1; j < num; j++) | ||
714 | if (freq[i].i > freq[j].i) { | ||
715 | temp.i = freq[i].i; | ||
716 | temp.m = freq[i].m; | ||
717 | |||
718 | freq[i].i = freq[j].i; | ||
719 | freq[i].m = freq[j].m; | ||
720 | |||
721 | freq[j].i = temp.i; | ||
722 | freq[j].m = temp.m; | ||
723 | } | ||
724 | } | ||
725 | |||
726 | /* data rate listing | ||
727 | MULTI_BANDS: | ||
728 | abg a b b/g | ||
729 | Infra G(12) A(8) B(4) G(12) | ||
730 | Adhoc A+B(12) A(8) B(4) B(4) | ||
731 | |||
732 | non-MULTI_BANDS: | ||
733 | b b/g | ||
734 | Infra B(4) G(12) | ||
735 | Adhoc B(4) B(4) | ||
736 | */ | ||
737 | /** | ||
738 | * @brief Get Range Info | ||
739 | * | ||
740 | * @param dev A pointer to net_device structure | ||
741 | * @param info A pointer to iw_request_info structure | ||
742 | * @param vwrq A pointer to iw_param structure | ||
743 | * @param extra A pointer to extra data buf | ||
744 | * @return 0 --success, otherwise fail | ||
745 | */ | ||
746 | static int wlan_get_range(struct net_device *dev, struct iw_request_info *info, | ||
747 | struct iw_point *dwrq, char *extra) | ||
748 | { | ||
749 | int i, j; | ||
750 | wlan_private *priv = dev->priv; | ||
751 | wlan_adapter *adapter = priv->adapter; | ||
752 | struct iw_range *range = (struct iw_range *)extra; | ||
753 | struct chan_freq_power *cfp; | ||
754 | u8 rates[WLAN_SUPPORTED_RATES]; | ||
755 | |||
756 | u8 flag = 0; | ||
757 | |||
758 | ENTER(); | ||
759 | |||
760 | dwrq->length = sizeof(struct iw_range); | ||
761 | memset(range, 0, sizeof(struct iw_range)); | ||
762 | |||
763 | range->min_nwid = 0; | ||
764 | range->max_nwid = 0; | ||
765 | |||
766 | memset(rates, 0, sizeof(rates)); | ||
767 | range->num_bitrates = get_active_data_rates(adapter, rates); | ||
768 | |||
769 | for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i]; | ||
770 | i++) { | ||
771 | range->bitrate[i] = (rates[i] & 0x7f) * 500000; | ||
772 | } | ||
773 | range->num_bitrates = i; | ||
774 | lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, | ||
775 | range->num_bitrates); | ||
776 | |||
777 | range->num_frequency = 0; | ||
778 | if (priv->adapter->enable11d && | ||
779 | adapter->connect_status == libertas_connected) { | ||
780 | u8 chan_no; | ||
781 | u8 band; | ||
782 | |||
783 | struct parsed_region_chan_11d *parsed_region_chan = | ||
784 | &adapter->parsed_region_chan; | ||
785 | |||
786 | if (parsed_region_chan == NULL) { | ||
787 | lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n"); | ||
788 | LEAVE(); | ||
789 | return 0; | ||
790 | } | ||
791 | band = parsed_region_chan->band; | ||
792 | lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band, | ||
793 | parsed_region_chan->nr_chan); | ||
794 | |||
795 | for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) | ||
796 | && (i < parsed_region_chan->nr_chan); i++) { | ||
797 | chan_no = parsed_region_chan->chanpwr[i].chan; | ||
798 | lbs_pr_debug(1, "chan_no=%d\n", chan_no); | ||
799 | range->freq[range->num_frequency].i = (long)chan_no; | ||
800 | range->freq[range->num_frequency].m = | ||
801 | (long)libertas_chan_2_freq(chan_no, band) * 100000; | ||
802 | range->freq[range->num_frequency].e = 1; | ||
803 | range->num_frequency++; | ||
804 | } | ||
805 | flag = 1; | ||
806 | } | ||
807 | if (!flag) { | ||
808 | for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) | ||
809 | && (j < sizeof(adapter->region_channel) | ||
810 | / sizeof(adapter->region_channel[0])); j++) { | ||
811 | cfp = adapter->region_channel[j].CFP; | ||
812 | for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) | ||
813 | && adapter->region_channel[j].valid | ||
814 | && cfp | ||
815 | && (i < adapter->region_channel[j].nrcfp); i++) { | ||
816 | range->freq[range->num_frequency].i = | ||
817 | (long)cfp->channel; | ||
818 | range->freq[range->num_frequency].m = | ||
819 | (long)cfp->freq * 100000; | ||
820 | range->freq[range->num_frequency].e = 1; | ||
821 | cfp++; | ||
822 | range->num_frequency++; | ||
823 | } | ||
824 | } | ||
825 | } | ||
826 | |||
827 | lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", | ||
828 | IW_MAX_FREQUENCIES, range->num_frequency); | ||
829 | |||
830 | range->num_channels = range->num_frequency; | ||
831 | |||
832 | sort_channels(&range->freq[0], range->num_frequency); | ||
833 | |||
834 | /* | ||
835 | * Set an indication of the max TCP throughput in bit/s that we can | ||
836 | * expect using this interface | ||
837 | */ | ||
838 | if (i > 2) | ||
839 | range->throughput = 5000 * 1000; | ||
840 | else | ||
841 | range->throughput = 1500 * 1000; | ||
842 | |||
843 | range->min_rts = MRVDRV_RTS_MIN_VALUE; | ||
844 | range->max_rts = MRVDRV_RTS_MAX_VALUE; | ||
845 | range->min_frag = MRVDRV_FRAG_MIN_VALUE; | ||
846 | range->max_frag = MRVDRV_FRAG_MAX_VALUE; | ||
847 | |||
848 | range->encoding_size[0] = 5; | ||
849 | range->encoding_size[1] = 13; | ||
850 | range->num_encoding_sizes = 2; | ||
851 | range->max_encoding_tokens = 4; | ||
852 | |||
853 | range->min_pmp = 1000000; | ||
854 | range->max_pmp = 120000000; | ||
855 | range->min_pmt = 1000; | ||
856 | range->max_pmt = 1000000; | ||
857 | range->pmp_flags = IW_POWER_PERIOD; | ||
858 | range->pmt_flags = IW_POWER_TIMEOUT; | ||
859 | range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; | ||
860 | |||
861 | /* | ||
862 | * Minimum version we recommend | ||
863 | */ | ||
864 | range->we_version_source = 15; | ||
865 | |||
866 | /* | ||
867 | * Version we are compiled with | ||
868 | */ | ||
869 | range->we_version_compiled = WIRELESS_EXT; | ||
870 | |||
871 | range->retry_capa = IW_RETRY_LIMIT; | ||
872 | range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; | ||
873 | |||
874 | range->min_retry = TX_RETRY_MIN; | ||
875 | range->max_retry = TX_RETRY_MAX; | ||
876 | |||
877 | /* | ||
878 | * Set the qual, level and noise range values | ||
879 | */ | ||
880 | range->max_qual.qual = 100; | ||
881 | range->max_qual.level = 0; | ||
882 | range->max_qual.noise = 0; | ||
883 | range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
884 | |||
885 | range->avg_qual.qual = 70; | ||
886 | /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ | ||
887 | range->avg_qual.level = 0; | ||
888 | range->avg_qual.noise = 0; | ||
889 | range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
890 | |||
891 | range->sensitivity = 0; | ||
892 | |||
893 | /* | ||
894 | * Setup the supported power level ranges | ||
895 | */ | ||
896 | memset(range->txpower, 0, sizeof(range->txpower)); | ||
897 | range->txpower[0] = 5; | ||
898 | range->txpower[1] = 7; | ||
899 | range->txpower[2] = 9; | ||
900 | range->txpower[3] = 11; | ||
901 | range->txpower[4] = 13; | ||
902 | range->txpower[5] = 15; | ||
903 | range->txpower[6] = 17; | ||
904 | range->txpower[7] = 19; | ||
905 | |||
906 | range->num_txpower = 8; | ||
907 | range->txpower_capa = IW_TXPOW_DBM; | ||
908 | range->txpower_capa |= IW_TXPOW_RANGE; | ||
909 | |||
910 | range->event_capa[0] = (IW_EVENT_CAPA_K_0 | | ||
911 | IW_EVENT_CAPA_MASK(SIOCGIWAP) | | ||
912 | IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); | ||
913 | range->event_capa[1] = IW_EVENT_CAPA_K_1; | ||
914 | |||
915 | if (adapter->fwcapinfo & FW_CAPINFO_WPA) { | ||
916 | range->enc_capa = IW_ENC_CAPA_WPA | ||
917 | | IW_ENC_CAPA_WPA2 | ||
918 | | IW_ENC_CAPA_CIPHER_TKIP | ||
919 | | IW_ENC_CAPA_CIPHER_CCMP; | ||
920 | } | ||
921 | |||
922 | LEAVE(); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | static int wlan_set_power(struct net_device *dev, struct iw_request_info *info, | ||
927 | struct iw_param *vwrq, char *extra) | ||
928 | { | ||
929 | wlan_private *priv = dev->priv; | ||
930 | wlan_adapter *adapter = priv->adapter; | ||
931 | |||
932 | ENTER(); | ||
933 | |||
934 | /* PS is currently supported only in Infrastructure mode | ||
935 | * Remove this check if it is to be supported in IBSS mode also | ||
936 | */ | ||
937 | |||
938 | if (vwrq->disabled) { | ||
939 | adapter->psmode = wlan802_11powermodecam; | ||
940 | if (adapter->psstate != PS_STATE_FULL_POWER) { | ||
941 | libertas_ps_wakeup(priv, cmd_option_waitforrsp); | ||
942 | } | ||
943 | |||
944 | return 0; | ||
945 | } | ||
946 | |||
947 | if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | ||
948 | lbs_pr_debug(1, | ||
949 | "Setting power timeout command is not supported\n"); | ||
950 | return -EINVAL; | ||
951 | } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { | ||
952 | lbs_pr_debug(1, "Setting power period command is not supported\n"); | ||
953 | return -EINVAL; | ||
954 | } | ||
955 | |||
956 | if (adapter->psmode != wlan802_11powermodecam) { | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | adapter->psmode = wlan802_11powermodemax_psp; | ||
961 | |||
962 | if (adapter->connect_status == libertas_connected) { | ||
963 | libertas_ps_sleep(priv, cmd_option_waitforrsp); | ||
964 | } | ||
965 | |||
966 | LEAVE(); | ||
967 | return 0; | ||
968 | } | ||
969 | |||
970 | static int wlan_get_power(struct net_device *dev, struct iw_request_info *info, | ||
971 | struct iw_param *vwrq, char *extra) | ||
972 | { | ||
973 | wlan_private *priv = dev->priv; | ||
974 | wlan_adapter *adapter = priv->adapter; | ||
975 | int mode; | ||
976 | |||
977 | ENTER(); | ||
978 | |||
979 | mode = adapter->psmode; | ||
980 | |||
981 | if ((vwrq->disabled = (mode == wlan802_11powermodecam)) | ||
982 | || adapter->connect_status == libertas_disconnected) { | ||
983 | LEAVE(); | ||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | vwrq->value = 0; | ||
988 | |||
989 | LEAVE(); | ||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | /* | ||
994 | * iwpriv settable callbacks | ||
995 | */ | ||
996 | |||
997 | static const iw_handler wlan_private_handler[] = { | ||
998 | NULL, /* SIOCIWFIRSTPRIV */ | ||
999 | }; | ||
1000 | |||
1001 | static const struct iw_priv_args wlan_private_args[] = { | ||
1002 | /* | ||
1003 | * { cmd, set_args, get_args, name } | ||
1004 | */ | ||
1005 | { | ||
1006 | WLANSCAN_TYPE, | ||
1007 | IW_PRIV_TYPE_CHAR | 8, | ||
1008 | IW_PRIV_TYPE_CHAR | 8, | ||
1009 | "scantype"}, | ||
1010 | |||
1011 | { | ||
1012 | WLAN_SETINT_GETINT, | ||
1013 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1014 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1015 | ""}, | ||
1016 | { | ||
1017 | WLANNF, | ||
1018 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1019 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1020 | "getNF"}, | ||
1021 | { | ||
1022 | WLANRSSI, | ||
1023 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1024 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1025 | "getRSSI"}, | ||
1026 | { | ||
1027 | WLANENABLE11D, | ||
1028 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1029 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1030 | "enable11d"}, | ||
1031 | { | ||
1032 | WLANADHOCGRATE, | ||
1033 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1034 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1035 | "adhocgrate"}, | ||
1036 | |||
1037 | { | ||
1038 | WLAN_SUBCMD_SET_PRESCAN, | ||
1039 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1040 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1041 | "prescan"}, | ||
1042 | { | ||
1043 | WLAN_SETONEINT_GETONEINT, | ||
1044 | IW_PRIV_TYPE_INT | 1, | ||
1045 | IW_PRIV_TYPE_INT | 1, | ||
1046 | ""}, | ||
1047 | { | ||
1048 | WLAN_BEACON_INTERVAL, | ||
1049 | IW_PRIV_TYPE_INT | 1, | ||
1050 | IW_PRIV_TYPE_INT | 1, | ||
1051 | "bcninterval"}, | ||
1052 | { | ||
1053 | WLAN_LISTENINTRVL, | ||
1054 | IW_PRIV_TYPE_INT | 1, | ||
1055 | IW_PRIV_TYPE_INT | 1, | ||
1056 | "lolisteninter"}, | ||
1057 | { | ||
1058 | WLAN_TXCONTROL, | ||
1059 | IW_PRIV_TYPE_INT | 1, | ||
1060 | IW_PRIV_TYPE_INT | 1, | ||
1061 | "txcontrol"}, | ||
1062 | { | ||
1063 | WLAN_NULLPKTINTERVAL, | ||
1064 | IW_PRIV_TYPE_INT | 1, | ||
1065 | IW_PRIV_TYPE_INT | 1, | ||
1066 | "psnullinterval"}, | ||
1067 | /* Using iwpriv sub-command feature */ | ||
1068 | { | ||
1069 | WLAN_SETONEINT_GETNONE, /* IOCTL: 24 */ | ||
1070 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1071 | IW_PRIV_TYPE_NONE, | ||
1072 | ""}, | ||
1073 | |||
1074 | { | ||
1075 | WLAN_SUBCMD_SETRXANTENNA, | ||
1076 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1077 | IW_PRIV_TYPE_NONE, | ||
1078 | "setrxant"}, | ||
1079 | { | ||
1080 | WLAN_SUBCMD_SETTXANTENNA, | ||
1081 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1082 | IW_PRIV_TYPE_NONE, | ||
1083 | "settxant"}, | ||
1084 | { | ||
1085 | WLANSETAUTHALG, | ||
1086 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1087 | IW_PRIV_TYPE_NONE, | ||
1088 | "authalgs", | ||
1089 | }, | ||
1090 | { | ||
1091 | WLANSET8021XAUTHALG, | ||
1092 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1093 | IW_PRIV_TYPE_NONE, | ||
1094 | "8021xauthalgs", | ||
1095 | }, | ||
1096 | { | ||
1097 | WLANSETENCRYPTIONMODE, | ||
1098 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1099 | IW_PRIV_TYPE_NONE, | ||
1100 | "encryptionmode", | ||
1101 | }, | ||
1102 | { | ||
1103 | WLANSETREGION, | ||
1104 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1105 | IW_PRIV_TYPE_NONE, | ||
1106 | "setregioncode"}, | ||
1107 | { | ||
1108 | WLAN_SET_LISTEN_INTERVAL, | ||
1109 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1110 | IW_PRIV_TYPE_NONE, | ||
1111 | "setlisteninter"}, | ||
1112 | { | ||
1113 | WLAN_SET_MULTIPLE_DTIM, | ||
1114 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1115 | IW_PRIV_TYPE_NONE, | ||
1116 | "setmultipledtim"}, | ||
1117 | { | ||
1118 | WLAN_SET_ATIM_WINDOW, | ||
1119 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1120 | IW_PRIV_TYPE_NONE, | ||
1121 | "atimwindow"}, | ||
1122 | { | ||
1123 | WLANSETBCNAVG, | ||
1124 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1125 | IW_PRIV_TYPE_NONE, | ||
1126 | "setbcnavg"}, | ||
1127 | { | ||
1128 | WLANSETDATAAVG, | ||
1129 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1130 | IW_PRIV_TYPE_NONE, | ||
1131 | "setdataavg"}, | ||
1132 | { | ||
1133 | WLAN_SET_LINKMODE, | ||
1134 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1135 | IW_PRIV_TYPE_NONE, | ||
1136 | "linkmode"}, | ||
1137 | { | ||
1138 | WLAN_SET_RADIOMODE, | ||
1139 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1140 | IW_PRIV_TYPE_NONE, | ||
1141 | "radiomode"}, | ||
1142 | { | ||
1143 | WLAN_SET_DEBUGMODE, | ||
1144 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1145 | IW_PRIV_TYPE_NONE, | ||
1146 | "debugmode"}, | ||
1147 | { | ||
1148 | WLAN_SUBCMD_MESH_SET_TTL, | ||
1149 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1150 | IW_PRIV_TYPE_NONE, | ||
1151 | "mesh_set_ttl"}, | ||
1152 | { | ||
1153 | WLAN_SETNONE_GETONEINT, | ||
1154 | IW_PRIV_TYPE_NONE, | ||
1155 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1156 | ""}, | ||
1157 | { | ||
1158 | WLANGETREGION, | ||
1159 | IW_PRIV_TYPE_NONE, | ||
1160 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1161 | "getregioncode"}, | ||
1162 | { | ||
1163 | WLAN_GET_LISTEN_INTERVAL, | ||
1164 | IW_PRIV_TYPE_NONE, | ||
1165 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1166 | "getlisteninter"}, | ||
1167 | { | ||
1168 | WLAN_GET_MULTIPLE_DTIM, | ||
1169 | IW_PRIV_TYPE_NONE, | ||
1170 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1171 | "getmultipledtim"}, | ||
1172 | { | ||
1173 | WLAN_GET_TX_RATE, | ||
1174 | IW_PRIV_TYPE_NONE, | ||
1175 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1176 | "gettxrate"}, | ||
1177 | { | ||
1178 | WLANGETBCNAVG, | ||
1179 | IW_PRIV_TYPE_NONE, | ||
1180 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1181 | "getbcnavg"}, | ||
1182 | { | ||
1183 | WLAN_GET_LINKMODE, | ||
1184 | IW_PRIV_TYPE_NONE, | ||
1185 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1186 | "get_linkmode"}, | ||
1187 | { | ||
1188 | WLAN_GET_RADIOMODE, | ||
1189 | IW_PRIV_TYPE_NONE, | ||
1190 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1191 | "get_radiomode"}, | ||
1192 | { | ||
1193 | WLAN_GET_DEBUGMODE, | ||
1194 | IW_PRIV_TYPE_NONE, | ||
1195 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1196 | "get_debugmode"}, | ||
1197 | { | ||
1198 | WLAN_SUBCMD_FWT_CLEANUP, | ||
1199 | IW_PRIV_TYPE_NONE, | ||
1200 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1201 | "fwt_cleanup"}, | ||
1202 | { | ||
1203 | WLAN_SUBCMD_FWT_TIME, | ||
1204 | IW_PRIV_TYPE_NONE, | ||
1205 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1206 | "fwt_time"}, | ||
1207 | { | ||
1208 | WLAN_SUBCMD_MESH_GET_TTL, | ||
1209 | IW_PRIV_TYPE_NONE, | ||
1210 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
1211 | "mesh_get_ttl"}, | ||
1212 | { | ||
1213 | WLAN_SETNONE_GETTWELVE_CHAR, | ||
1214 | IW_PRIV_TYPE_NONE, | ||
1215 | IW_PRIV_TYPE_CHAR | 12, | ||
1216 | ""}, | ||
1217 | { | ||
1218 | WLAN_SUBCMD_GETRXANTENNA, | ||
1219 | IW_PRIV_TYPE_NONE, | ||
1220 | IW_PRIV_TYPE_CHAR | 12, | ||
1221 | "getrxant"}, | ||
1222 | { | ||
1223 | WLAN_SUBCMD_GETTXANTENNA, | ||
1224 | IW_PRIV_TYPE_NONE, | ||
1225 | IW_PRIV_TYPE_CHAR | 12, | ||
1226 | "gettxant"}, | ||
1227 | { | ||
1228 | WLAN_GET_TSF, | ||
1229 | IW_PRIV_TYPE_NONE, | ||
1230 | IW_PRIV_TYPE_CHAR | 12, | ||
1231 | "gettsf"}, | ||
1232 | { | ||
1233 | WLAN_SETNONE_GETNONE, | ||
1234 | IW_PRIV_TYPE_NONE, | ||
1235 | IW_PRIV_TYPE_NONE, | ||
1236 | ""}, | ||
1237 | { | ||
1238 | WLANDEAUTH, | ||
1239 | IW_PRIV_TYPE_NONE, | ||
1240 | IW_PRIV_TYPE_NONE, | ||
1241 | "deauth"}, | ||
1242 | { | ||
1243 | WLANADHOCSTOP, | ||
1244 | IW_PRIV_TYPE_NONE, | ||
1245 | IW_PRIV_TYPE_NONE, | ||
1246 | "adhocstop"}, | ||
1247 | { | ||
1248 | WLANRADIOON, | ||
1249 | IW_PRIV_TYPE_NONE, | ||
1250 | IW_PRIV_TYPE_NONE, | ||
1251 | "radioon"}, | ||
1252 | { | ||
1253 | WLANRADIOOFF, | ||
1254 | IW_PRIV_TYPE_NONE, | ||
1255 | IW_PRIV_TYPE_NONE, | ||
1256 | "radiooff"}, | ||
1257 | { | ||
1258 | WLANWLANIDLEON, | ||
1259 | IW_PRIV_TYPE_NONE, | ||
1260 | IW_PRIV_TYPE_NONE, | ||
1261 | "wlanidle-on"}, | ||
1262 | { | ||
1263 | WLANWLANIDLEOFF, | ||
1264 | IW_PRIV_TYPE_NONE, | ||
1265 | IW_PRIV_TYPE_NONE, | ||
1266 | "wlanidle-off"}, | ||
1267 | { | ||
1268 | WLAN_SUBCMD_FWT_RESET, | ||
1269 | IW_PRIV_TYPE_NONE, | ||
1270 | IW_PRIV_TYPE_NONE, | ||
1271 | "fwt_reset"}, | ||
1272 | { | ||
1273 | WLAN_SUBCMD_BT_RESET, | ||
1274 | IW_PRIV_TYPE_NONE, | ||
1275 | IW_PRIV_TYPE_NONE, | ||
1276 | "bt_reset"}, | ||
1277 | { | ||
1278 | WLAN_SET128CHAR_GET128CHAR, | ||
1279 | IW_PRIV_TYPE_CHAR | 128, | ||
1280 | IW_PRIV_TYPE_CHAR | 128, | ||
1281 | ""}, | ||
1282 | /* BT Management */ | ||
1283 | { | ||
1284 | WLAN_SUBCMD_BT_ADD, | ||
1285 | IW_PRIV_TYPE_CHAR | 128, | ||
1286 | IW_PRIV_TYPE_CHAR | 128, | ||
1287 | "bt_add"}, | ||
1288 | { | ||
1289 | WLAN_SUBCMD_BT_DEL, | ||
1290 | IW_PRIV_TYPE_CHAR | 128, | ||
1291 | IW_PRIV_TYPE_CHAR | 128, | ||
1292 | "bt_del"}, | ||
1293 | { | ||
1294 | WLAN_SUBCMD_BT_LIST, | ||
1295 | IW_PRIV_TYPE_CHAR | 128, | ||
1296 | IW_PRIV_TYPE_CHAR | 128, | ||
1297 | "bt_list"}, | ||
1298 | /* FWT Management */ | ||
1299 | { | ||
1300 | WLAN_SUBCMD_FWT_ADD, | ||
1301 | IW_PRIV_TYPE_CHAR | 128, | ||
1302 | IW_PRIV_TYPE_CHAR | 128, | ||
1303 | "fwt_add"}, | ||
1304 | { | ||
1305 | WLAN_SUBCMD_FWT_DEL, | ||
1306 | IW_PRIV_TYPE_CHAR | 128, | ||
1307 | IW_PRIV_TYPE_CHAR | 128, | ||
1308 | "fwt_del"}, | ||
1309 | { | ||
1310 | WLAN_SUBCMD_FWT_LOOKUP, | ||
1311 | IW_PRIV_TYPE_CHAR | 128, | ||
1312 | IW_PRIV_TYPE_CHAR | 128, | ||
1313 | "fwt_lookup"}, | ||
1314 | { | ||
1315 | WLAN_SUBCMD_FWT_LIST_NEIGHBOR, | ||
1316 | IW_PRIV_TYPE_CHAR | 128, | ||
1317 | IW_PRIV_TYPE_CHAR | 128, | ||
1318 | "fwt_list_neigh"}, | ||
1319 | { | ||
1320 | WLAN_SUBCMD_FWT_LIST, | ||
1321 | IW_PRIV_TYPE_CHAR | 128, | ||
1322 | IW_PRIV_TYPE_CHAR | 128, | ||
1323 | "fwt_list"}, | ||
1324 | { | ||
1325 | WLAN_SUBCMD_FWT_LIST_ROUTE, | ||
1326 | IW_PRIV_TYPE_CHAR | 128, | ||
1327 | IW_PRIV_TYPE_CHAR | 128, | ||
1328 | "fwt_list_route"}, | ||
1329 | { | ||
1330 | WLANSCAN_MODE, | ||
1331 | IW_PRIV_TYPE_CHAR | 128, | ||
1332 | IW_PRIV_TYPE_CHAR | 128, | ||
1333 | "scanmode"}, | ||
1334 | { | ||
1335 | WLAN_GET_ADHOC_STATUS, | ||
1336 | IW_PRIV_TYPE_CHAR | 128, | ||
1337 | IW_PRIV_TYPE_CHAR | 128, | ||
1338 | "getadhocstatus"}, | ||
1339 | { | ||
1340 | WLAN_SETNONE_GETWORDCHAR, | ||
1341 | IW_PRIV_TYPE_NONE, | ||
1342 | IW_PRIV_TYPE_CHAR | 128, | ||
1343 | ""}, | ||
1344 | { | ||
1345 | WLANSETWPAIE, | ||
1346 | IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 24, | ||
1347 | IW_PRIV_TYPE_NONE, | ||
1348 | "setwpaie"}, | ||
1349 | { | ||
1350 | WLANGETLOG, | ||
1351 | IW_PRIV_TYPE_NONE, | ||
1352 | IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE, | ||
1353 | "getlog"}, | ||
1354 | { | ||
1355 | WLAN_SET_GET_SIXTEEN_INT, | ||
1356 | IW_PRIV_TYPE_INT | 16, | ||
1357 | IW_PRIV_TYPE_INT | 16, | ||
1358 | ""}, | ||
1359 | { | ||
1360 | WLAN_TPCCFG, | ||
1361 | IW_PRIV_TYPE_INT | 16, | ||
1362 | IW_PRIV_TYPE_INT | 16, | ||
1363 | "tpccfg"}, | ||
1364 | { | ||
1365 | WLAN_POWERCFG, | ||
1366 | IW_PRIV_TYPE_INT | 16, | ||
1367 | IW_PRIV_TYPE_INT | 16, | ||
1368 | "powercfg"}, | ||
1369 | { | ||
1370 | WLAN_AUTO_FREQ_SET, | ||
1371 | IW_PRIV_TYPE_INT | 16, | ||
1372 | IW_PRIV_TYPE_INT | 16, | ||
1373 | "setafc"}, | ||
1374 | { | ||
1375 | WLAN_AUTO_FREQ_GET, | ||
1376 | IW_PRIV_TYPE_INT | 16, | ||
1377 | IW_PRIV_TYPE_INT | 16, | ||
1378 | "getafc"}, | ||
1379 | { | ||
1380 | WLAN_SCANPROBES, | ||
1381 | IW_PRIV_TYPE_INT | 16, | ||
1382 | IW_PRIV_TYPE_INT | 16, | ||
1383 | "scanprobes"}, | ||
1384 | { | ||
1385 | WLAN_LED_GPIO_CTRL, | ||
1386 | IW_PRIV_TYPE_INT | 16, | ||
1387 | IW_PRIV_TYPE_INT | 16, | ||
1388 | "ledgpio"}, | ||
1389 | { | ||
1390 | WLAN_ADAPT_RATESET, | ||
1391 | IW_PRIV_TYPE_INT | 16, | ||
1392 | IW_PRIV_TYPE_INT | 16, | ||
1393 | "rateadapt"}, | ||
1394 | { | ||
1395 | WLAN_INACTIVITY_TIMEOUT, | ||
1396 | IW_PRIV_TYPE_INT | 16, | ||
1397 | IW_PRIV_TYPE_INT | 16, | ||
1398 | "inactivityto"}, | ||
1399 | { | ||
1400 | WLANSNR, | ||
1401 | IW_PRIV_TYPE_INT | 16, | ||
1402 | IW_PRIV_TYPE_INT | 16, | ||
1403 | "getSNR"}, | ||
1404 | { | ||
1405 | WLAN_GET_RATE, | ||
1406 | IW_PRIV_TYPE_INT | 16, | ||
1407 | IW_PRIV_TYPE_INT | 16, | ||
1408 | "getrate"}, | ||
1409 | { | ||
1410 | WLAN_GET_RXINFO, | ||
1411 | IW_PRIV_TYPE_INT | 16, | ||
1412 | IW_PRIV_TYPE_INT | 16, | ||
1413 | "getrxinfo"}, | ||
1414 | }; | ||
1415 | |||
1416 | static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev) | ||
1417 | { | ||
1418 | enum { | ||
1419 | POOR = 30, | ||
1420 | FAIR = 60, | ||
1421 | GOOD = 80, | ||
1422 | VERY_GOOD = 90, | ||
1423 | EXCELLENT = 95, | ||
1424 | PERFECT = 100 | ||
1425 | }; | ||
1426 | wlan_private *priv = dev->priv; | ||
1427 | wlan_adapter *adapter = priv->adapter; | ||
1428 | u32 rssi_qual; | ||
1429 | u32 tx_qual; | ||
1430 | u32 quality = 0; | ||
1431 | int stats_valid = 0; | ||
1432 | u8 rssi; | ||
1433 | u32 tx_retries; | ||
1434 | |||
1435 | ENTER(); | ||
1436 | |||
1437 | priv->wstats.status = adapter->inframode; | ||
1438 | |||
1439 | /* If we're not associated, all quality values are meaningless */ | ||
1440 | if (adapter->connect_status != libertas_connected) | ||
1441 | goto out; | ||
1442 | |||
1443 | /* Quality by RSSI */ | ||
1444 | priv->wstats.qual.level = | ||
1445 | CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], | ||
1446 | adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | ||
1447 | |||
1448 | if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { | ||
1449 | priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; | ||
1450 | } else { | ||
1451 | priv->wstats.qual.noise = | ||
1452 | CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | ||
1453 | } | ||
1454 | |||
1455 | lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level); | ||
1456 | lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise); | ||
1457 | |||
1458 | rssi = priv->wstats.qual.level - priv->wstats.qual.noise; | ||
1459 | if (rssi < 15) | ||
1460 | rssi_qual = rssi * POOR / 10; | ||
1461 | else if (rssi < 20) | ||
1462 | rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR; | ||
1463 | else if (rssi < 30) | ||
1464 | rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR; | ||
1465 | else if (rssi < 40) | ||
1466 | rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) / | ||
1467 | 10 + GOOD; | ||
1468 | else | ||
1469 | rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) / | ||
1470 | 10 + VERY_GOOD; | ||
1471 | quality = rssi_qual; | ||
1472 | |||
1473 | /* Quality by TX errors */ | ||
1474 | priv->wstats.discard.retries = priv->stats.tx_errors; | ||
1475 | |||
1476 | tx_retries = adapter->logmsg.retry; | ||
1477 | |||
1478 | if (tx_retries > 75) | ||
1479 | tx_qual = (90 - tx_retries) * POOR / 15; | ||
1480 | else if (tx_retries > 70) | ||
1481 | tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; | ||
1482 | else if (tx_retries > 65) | ||
1483 | tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; | ||
1484 | else if (tx_retries > 50) | ||
1485 | tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / | ||
1486 | 15 + GOOD; | ||
1487 | else | ||
1488 | tx_qual = (50 - tx_retries) * | ||
1489 | (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; | ||
1490 | quality = min(quality, tx_qual); | ||
1491 | |||
1492 | priv->wstats.discard.code = adapter->logmsg.wepundecryptable; | ||
1493 | priv->wstats.discard.fragment = adapter->logmsg.fcserror; | ||
1494 | priv->wstats.discard.retries = tx_retries; | ||
1495 | priv->wstats.discard.misc = adapter->logmsg.ackfailure; | ||
1496 | |||
1497 | /* Calculate quality */ | ||
1498 | priv->wstats.qual.qual = max(quality, (u32)100); | ||
1499 | priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
1500 | stats_valid = 1; | ||
1501 | |||
1502 | /* update stats asynchronously for future calls */ | ||
1503 | libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0, | ||
1504 | 0, 0, NULL); | ||
1505 | libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0, | ||
1506 | 0, 0, NULL); | ||
1507 | out: | ||
1508 | if (!stats_valid) { | ||
1509 | priv->wstats.miss.beacon = 0; | ||
1510 | priv->wstats.discard.retries = 0; | ||
1511 | priv->wstats.qual.qual = 0; | ||
1512 | priv->wstats.qual.level = 0; | ||
1513 | priv->wstats.qual.noise = 0; | ||
1514 | priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED; | ||
1515 | priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID | | ||
1516 | IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; | ||
1517 | } | ||
1518 | |||
1519 | LEAVE (); | ||
1520 | return &priv->wstats; | ||
1521 | |||
1522 | |||
1523 | } | ||
1524 | |||
1525 | static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info, | ||
1526 | struct iw_freq *fwrq, char *extra) | ||
1527 | { | ||
1528 | int ret = 0; | ||
1529 | wlan_private *priv = dev->priv; | ||
1530 | wlan_adapter *adapter = priv->adapter; | ||
1531 | int rc = -EINPROGRESS; /* Call commit handler */ | ||
1532 | struct chan_freq_power *cfp; | ||
1533 | |||
1534 | ENTER(); | ||
1535 | |||
1536 | /* | ||
1537 | * If setting by frequency, convert to a channel | ||
1538 | */ | ||
1539 | if (fwrq->e == 1) { | ||
1540 | |||
1541 | long f = fwrq->m / 100000; | ||
1542 | int c = 0; | ||
1543 | |||
1544 | cfp = find_cfp_by_band_and_freq(adapter, 0, f); | ||
1545 | if (!cfp) { | ||
1546 | lbs_pr_debug(1, "Invalid freq=%ld\n", f); | ||
1547 | return -EINVAL; | ||
1548 | } | ||
1549 | |||
1550 | c = (int)cfp->channel; | ||
1551 | |||
1552 | if (c < 0) | ||
1553 | return -EINVAL; | ||
1554 | |||
1555 | fwrq->e = 0; | ||
1556 | fwrq->m = c; | ||
1557 | } | ||
1558 | |||
1559 | /* | ||
1560 | * Setting by channel number | ||
1561 | */ | ||
1562 | if (fwrq->m > 1000 || fwrq->e > 0) { | ||
1563 | rc = -EOPNOTSUPP; | ||
1564 | } else { | ||
1565 | int channel = fwrq->m; | ||
1566 | |||
1567 | cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel); | ||
1568 | if (!cfp) { | ||
1569 | rc = -EINVAL; | ||
1570 | } else { | ||
1571 | if (adapter->inframode == wlan802_11ibss) { | ||
1572 | rc = changeadhocchannel(priv, channel); | ||
1573 | /* If station is WEP enabled, send the | ||
1574 | * command to set WEP in firmware | ||
1575 | */ | ||
1576 | if (adapter->secinfo.WEPstatus == | ||
1577 | wlan802_11WEPenabled) { | ||
1578 | lbs_pr_debug(1, "set_freq: WEP enabled\n"); | ||
1579 | ret = libertas_prepare_and_send_command(priv, | ||
1580 | cmd_802_11_set_wep, | ||
1581 | cmd_act_add, | ||
1582 | cmd_option_waitforrsp, | ||
1583 | 0, | ||
1584 | NULL); | ||
1585 | |||
1586 | if (ret) { | ||
1587 | LEAVE(); | ||
1588 | return ret; | ||
1589 | } | ||
1590 | |||
1591 | adapter->currentpacketfilter |= | ||
1592 | cmd_act_mac_wep_enable; | ||
1593 | |||
1594 | libertas_set_mac_packet_filter(priv); | ||
1595 | } | ||
1596 | } else { | ||
1597 | rc = -EOPNOTSUPP; | ||
1598 | } | ||
1599 | } | ||
1600 | } | ||
1601 | |||
1602 | LEAVE(); | ||
1603 | return rc; | ||
1604 | } | ||
1605 | |||
1606 | /** | ||
1607 | * @brief use index to get the data rate | ||
1608 | * | ||
1609 | * @param index The index of data rate | ||
1610 | * @return data rate or 0 | ||
1611 | */ | ||
1612 | u32 libertas_index_to_data_rate(u8 index) | ||
1613 | { | ||
1614 | if (index >= sizeof(libertas_wlan_data_rates)) | ||
1615 | index = 0; | ||
1616 | |||
1617 | return libertas_wlan_data_rates[index]; | ||
1618 | } | ||
1619 | |||
1620 | /** | ||
1621 | * @brief use rate to get the index | ||
1622 | * | ||
1623 | * @param rate data rate | ||
1624 | * @return index or 0 | ||
1625 | */ | ||
1626 | u8 libertas_data_rate_to_index(u32 rate) | ||
1627 | { | ||
1628 | u8 *ptr; | ||
1629 | |||
1630 | if (rate) | ||
1631 | if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate, | ||
1632 | sizeof(libertas_wlan_data_rates)))) | ||
1633 | return (ptr - libertas_wlan_data_rates); | ||
1634 | |||
1635 | return 0; | ||
1636 | } | ||
1637 | |||
1638 | static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info, | ||
1639 | struct iw_param *vwrq, char *extra) | ||
1640 | { | ||
1641 | wlan_private *priv = dev->priv; | ||
1642 | wlan_adapter *adapter = priv->adapter; | ||
1643 | u32 data_rate; | ||
1644 | u16 action; | ||
1645 | int ret = 0; | ||
1646 | u8 rates[WLAN_SUPPORTED_RATES]; | ||
1647 | u8 *rate; | ||
1648 | |||
1649 | ENTER(); | ||
1650 | |||
1651 | lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value); | ||
1652 | |||
1653 | if (vwrq->value == -1) { | ||
1654 | action = cmd_act_set_tx_auto; // Auto | ||
1655 | adapter->is_datarate_auto = 1; | ||
1656 | adapter->datarate = 0; | ||
1657 | } else { | ||
1658 | if (vwrq->value % 100000) { | ||
1659 | return -EINVAL; | ||
1660 | } | ||
1661 | |||
1662 | data_rate = vwrq->value / 500000; | ||
1663 | |||
1664 | memset(rates, 0, sizeof(rates)); | ||
1665 | get_active_data_rates(adapter, rates); | ||
1666 | rate = rates; | ||
1667 | while (*rate) { | ||
1668 | lbs_pr_debug(1, "Rate=0x%X Wanted=0x%X\n", *rate, | ||
1669 | data_rate); | ||
1670 | if ((*rate & 0x7f) == (data_rate & 0x7f)) | ||
1671 | break; | ||
1672 | rate++; | ||
1673 | } | ||
1674 | if (!*rate) { | ||
1675 | lbs_pr_alert( "The fixed data rate 0x%X is out " | ||
1676 | "of range.\n", data_rate); | ||
1677 | return -EINVAL; | ||
1678 | } | ||
1679 | |||
1680 | adapter->datarate = data_rate; | ||
1681 | action = cmd_act_set_tx_fix_rate; | ||
1682 | adapter->is_datarate_auto = 0; | ||
1683 | } | ||
1684 | |||
1685 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, | ||
1686 | action, cmd_option_waitforrsp, 0, NULL); | ||
1687 | |||
1688 | LEAVE(); | ||
1689 | return ret; | ||
1690 | } | ||
1691 | |||
1692 | static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info, | ||
1693 | struct iw_param *vwrq, char *extra) | ||
1694 | { | ||
1695 | wlan_private *priv = dev->priv; | ||
1696 | wlan_adapter *adapter = priv->adapter; | ||
1697 | |||
1698 | ENTER(); | ||
1699 | |||
1700 | if (adapter->is_datarate_auto) { | ||
1701 | vwrq->fixed = 0; | ||
1702 | } else { | ||
1703 | vwrq->fixed = 1; | ||
1704 | } | ||
1705 | |||
1706 | vwrq->value = adapter->datarate * 500000; | ||
1707 | |||
1708 | LEAVE(); | ||
1709 | return 0; | ||
1710 | } | ||
1711 | |||
1712 | static int wlan_set_mode(struct net_device *dev, | ||
1713 | struct iw_request_info *info, u32 * uwrq, char *extra) | ||
1714 | { | ||
1715 | int ret = 0; | ||
1716 | wlan_private *priv = dev->priv; | ||
1717 | wlan_adapter *adapter = priv->adapter; | ||
1718 | struct assoc_request * assoc_req; | ||
1719 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; | ||
1720 | |||
1721 | ENTER(); | ||
1722 | |||
1723 | switch (*uwrq) { | ||
1724 | case IW_MODE_ADHOC: | ||
1725 | lbs_pr_debug(1, "Wanted mode is ad-hoc: current datarate=%#x\n", | ||
1726 | adapter->datarate); | ||
1727 | new_mode = wlan802_11ibss; | ||
1728 | adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; | ||
1729 | break; | ||
1730 | |||
1731 | case IW_MODE_INFRA: | ||
1732 | lbs_pr_debug(1, "Wanted mode is Infrastructure\n"); | ||
1733 | new_mode = wlan802_11infrastructure; | ||
1734 | break; | ||
1735 | |||
1736 | case IW_MODE_AUTO: | ||
1737 | lbs_pr_debug(1, "Wanted mode is Auto\n"); | ||
1738 | new_mode = wlan802_11autounknown; | ||
1739 | break; | ||
1740 | |||
1741 | default: | ||
1742 | lbs_pr_debug(1, "Wanted mode is Unknown: 0x%x\n", *uwrq); | ||
1743 | return -EINVAL; | ||
1744 | } | ||
1745 | |||
1746 | mutex_lock(&adapter->lock); | ||
1747 | assoc_req = wlan_get_association_request(adapter); | ||
1748 | if (!assoc_req) { | ||
1749 | ret = -ENOMEM; | ||
1750 | } else { | ||
1751 | assoc_req->mode = new_mode; | ||
1752 | } | ||
1753 | |||
1754 | if (ret == 0) { | ||
1755 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); | ||
1756 | wlan_postpone_association_work(priv); | ||
1757 | } else { | ||
1758 | wlan_cancel_association_work(priv); | ||
1759 | } | ||
1760 | mutex_unlock(&adapter->lock); | ||
1761 | |||
1762 | LEAVE(); | ||
1763 | return ret; | ||
1764 | } | ||
1765 | |||
1766 | |||
1767 | /** | ||
1768 | * @brief Get Encryption key | ||
1769 | * | ||
1770 | * @param dev A pointer to net_device structure | ||
1771 | * @param info A pointer to iw_request_info structure | ||
1772 | * @param vwrq A pointer to iw_param structure | ||
1773 | * @param extra A pointer to extra data buf | ||
1774 | * @return 0 --success, otherwise fail | ||
1775 | */ | ||
1776 | static int wlan_get_encode(struct net_device *dev, | ||
1777 | struct iw_request_info *info, | ||
1778 | struct iw_point *dwrq, u8 * extra) | ||
1779 | { | ||
1780 | wlan_private *priv = dev->priv; | ||
1781 | wlan_adapter *adapter = priv->adapter; | ||
1782 | int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
1783 | |||
1784 | ENTER(); | ||
1785 | |||
1786 | lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n", | ||
1787 | dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx); | ||
1788 | |||
1789 | dwrq->flags = 0; | ||
1790 | |||
1791 | /* Authentication method */ | ||
1792 | switch (adapter->secinfo.authmode) { | ||
1793 | case wlan802_11authmodeopen: | ||
1794 | dwrq->flags = IW_ENCODE_OPEN; | ||
1795 | break; | ||
1796 | |||
1797 | case wlan802_11authmodeshared: | ||
1798 | case wlan802_11authmodenetworkEAP: | ||
1799 | dwrq->flags = IW_ENCODE_RESTRICTED; | ||
1800 | break; | ||
1801 | default: | ||
1802 | dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; | ||
1803 | break; | ||
1804 | } | ||
1805 | |||
1806 | if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) | ||
1807 | || adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { | ||
1808 | dwrq->flags &= ~IW_ENCODE_DISABLED; | ||
1809 | } else { | ||
1810 | dwrq->flags |= IW_ENCODE_DISABLED; | ||
1811 | } | ||
1812 | |||
1813 | memset(extra, 0, 16); | ||
1814 | |||
1815 | mutex_lock(&adapter->lock); | ||
1816 | |||
1817 | /* Default to returning current transmit key */ | ||
1818 | if (index < 0) | ||
1819 | index = adapter->wep_tx_keyidx; | ||
1820 | |||
1821 | if ((adapter->wep_keys[index].len) && | ||
1822 | (adapter->secinfo.WEPstatus == wlan802_11WEPenabled)) { | ||
1823 | memcpy(extra, adapter->wep_keys[index].key, | ||
1824 | adapter->wep_keys[index].len); | ||
1825 | dwrq->length = adapter->wep_keys[index].len; | ||
1826 | |||
1827 | dwrq->flags |= (index + 1); | ||
1828 | /* Return WEP enabled */ | ||
1829 | dwrq->flags &= ~IW_ENCODE_DISABLED; | ||
1830 | } else if ((adapter->secinfo.WPAenabled) | ||
1831 | || (adapter->secinfo.WPA2enabled)) { | ||
1832 | /* return WPA enabled */ | ||
1833 | dwrq->flags &= ~IW_ENCODE_DISABLED; | ||
1834 | } else { | ||
1835 | dwrq->flags |= IW_ENCODE_DISABLED; | ||
1836 | } | ||
1837 | |||
1838 | mutex_unlock(&adapter->lock); | ||
1839 | |||
1840 | dwrq->flags |= IW_ENCODE_NOKEY; | ||
1841 | |||
1842 | lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n", | ||
1843 | extra[0], extra[1], extra[2], | ||
1844 | extra[3], extra[4], extra[5], dwrq->length); | ||
1845 | |||
1846 | lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags); | ||
1847 | |||
1848 | LEAVE(); | ||
1849 | return 0; | ||
1850 | } | ||
1851 | |||
1852 | /** | ||
1853 | * @brief Set Encryption key (internal) | ||
1854 | * | ||
1855 | * @param priv A pointer to private card structure | ||
1856 | * @param key_material A pointer to key material | ||
1857 | * @param key_length length of key material | ||
1858 | * @param index key index to set | ||
1859 | * @param set_tx_key Force set TX key (1 = yes, 0 = no) | ||
1860 | * @return 0 --success, otherwise fail | ||
1861 | */ | ||
1862 | static int wlan_set_wep_key(struct assoc_request *assoc_req, | ||
1863 | const char *key_material, | ||
1864 | u16 key_length, | ||
1865 | u16 index, | ||
1866 | int set_tx_key) | ||
1867 | { | ||
1868 | struct WLAN_802_11_KEY *pkey; | ||
1869 | |||
1870 | ENTER(); | ||
1871 | |||
1872 | /* Paranoid validation of key index */ | ||
1873 | if (index > 3) { | ||
1874 | LEAVE(); | ||
1875 | return -EINVAL; | ||
1876 | } | ||
1877 | |||
1878 | /* validate max key length */ | ||
1879 | if (key_length > KEY_LEN_WEP_104) { | ||
1880 | LEAVE(); | ||
1881 | return -EINVAL; | ||
1882 | } | ||
1883 | |||
1884 | pkey = &assoc_req->wep_keys[index]; | ||
1885 | |||
1886 | if (key_length > 0) { | ||
1887 | memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); | ||
1888 | pkey->type = KEY_TYPE_ID_WEP; | ||
1889 | |||
1890 | /* Standardize the key length */ | ||
1891 | pkey->len = (key_length > KEY_LEN_WEP_40) ? | ||
1892 | KEY_LEN_WEP_104 : KEY_LEN_WEP_40; | ||
1893 | memcpy(pkey->key, key_material, key_length); | ||
1894 | } | ||
1895 | |||
1896 | if (set_tx_key) { | ||
1897 | /* Ensure the chosen key is valid */ | ||
1898 | if (!pkey->len) { | ||
1899 | lbs_pr_debug(1, "key not set, so cannot enable it\n"); | ||
1900 | LEAVE(); | ||
1901 | return -EINVAL; | ||
1902 | } | ||
1903 | assoc_req->wep_tx_keyidx = index; | ||
1904 | } | ||
1905 | |||
1906 | assoc_req->secinfo.WEPstatus = wlan802_11WEPenabled; | ||
1907 | |||
1908 | LEAVE(); | ||
1909 | return 0; | ||
1910 | } | ||
1911 | |||
1912 | static int validate_key_index(u16 def_index, u16 raw_index, | ||
1913 | u16 *out_index, u16 *is_default) | ||
1914 | { | ||
1915 | if (!out_index || !is_default) | ||
1916 | return -EINVAL; | ||
1917 | |||
1918 | /* Verify index if present, otherwise use default TX key index */ | ||
1919 | if (raw_index > 0) { | ||
1920 | if (raw_index > 4) | ||
1921 | return -EINVAL; | ||
1922 | *out_index = raw_index - 1; | ||
1923 | } else { | ||
1924 | *out_index = def_index; | ||
1925 | *is_default = 1; | ||
1926 | } | ||
1927 | return 0; | ||
1928 | } | ||
1929 | |||
1930 | static void disable_wep(struct assoc_request *assoc_req) | ||
1931 | { | ||
1932 | int i; | ||
1933 | |||
1934 | /* Set Open System auth mode */ | ||
1935 | assoc_req->secinfo.authmode = wlan802_11authmodeopen; | ||
1936 | |||
1937 | /* Clear WEP keys and mark WEP as disabled */ | ||
1938 | assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; | ||
1939 | for (i = 0; i < 4; i++) | ||
1940 | assoc_req->wep_keys[i].len = 0; | ||
1941 | |||
1942 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | ||
1943 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | ||
1944 | } | ||
1945 | |||
1946 | /** | ||
1947 | * @brief Set Encryption key | ||
1948 | * | ||
1949 | * @param dev A pointer to net_device structure | ||
1950 | * @param info A pointer to iw_request_info structure | ||
1951 | * @param vwrq A pointer to iw_param structure | ||
1952 | * @param extra A pointer to extra data buf | ||
1953 | * @return 0 --success, otherwise fail | ||
1954 | */ | ||
1955 | static int wlan_set_encode(struct net_device *dev, | ||
1956 | struct iw_request_info *info, | ||
1957 | struct iw_point *dwrq, char *extra) | ||
1958 | { | ||
1959 | int ret = 0; | ||
1960 | wlan_private *priv = dev->priv; | ||
1961 | wlan_adapter *adapter = priv->adapter; | ||
1962 | struct assoc_request * assoc_req; | ||
1963 | u16 is_default = 0, index = 0, set_tx_key = 0; | ||
1964 | |||
1965 | ENTER(); | ||
1966 | |||
1967 | mutex_lock(&adapter->lock); | ||
1968 | assoc_req = wlan_get_association_request(adapter); | ||
1969 | if (!assoc_req) { | ||
1970 | ret = -ENOMEM; | ||
1971 | goto out; | ||
1972 | } | ||
1973 | |||
1974 | if (dwrq->flags & IW_ENCODE_DISABLED) { | ||
1975 | disable_wep (assoc_req); | ||
1976 | goto out; | ||
1977 | } | ||
1978 | |||
1979 | ret = validate_key_index(assoc_req->wep_tx_keyidx, | ||
1980 | (dwrq->flags & IW_ENCODE_INDEX), | ||
1981 | &index, &is_default); | ||
1982 | if (ret) { | ||
1983 | ret = -EINVAL; | ||
1984 | goto out; | ||
1985 | } | ||
1986 | |||
1987 | /* If WEP isn't enabled, or if there is no key data but a valid | ||
1988 | * index, set the TX key. | ||
1989 | */ | ||
1990 | if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) | ||
1991 | || (dwrq->length == 0 && !is_default)) | ||
1992 | set_tx_key = 1; | ||
1993 | |||
1994 | ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key); | ||
1995 | if (ret) | ||
1996 | goto out; | ||
1997 | |||
1998 | if (dwrq->length) | ||
1999 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | ||
2000 | if (set_tx_key) | ||
2001 | set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); | ||
2002 | |||
2003 | if (dwrq->flags & IW_ENCODE_RESTRICTED) { | ||
2004 | assoc_req->secinfo.authmode = wlan802_11authmodeshared; | ||
2005 | } else if (dwrq->flags & IW_ENCODE_OPEN) { | ||
2006 | assoc_req->secinfo.authmode = wlan802_11authmodeopen; | ||
2007 | } | ||
2008 | |||
2009 | out: | ||
2010 | if (ret == 0) { | ||
2011 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | ||
2012 | wlan_postpone_association_work(priv); | ||
2013 | } else { | ||
2014 | wlan_cancel_association_work(priv); | ||
2015 | } | ||
2016 | mutex_unlock(&adapter->lock); | ||
2017 | |||
2018 | LEAVE(); | ||
2019 | return ret; | ||
2020 | } | ||
2021 | |||
2022 | /** | ||
2023 | * @brief Get Extended Encryption key (WPA/802.1x and WEP) | ||
2024 | * | ||
2025 | * @param dev A pointer to net_device structure | ||
2026 | * @param info A pointer to iw_request_info structure | ||
2027 | * @param vwrq A pointer to iw_param structure | ||
2028 | * @param extra A pointer to extra data buf | ||
2029 | * @return 0 on success, otherwise failure | ||
2030 | */ | ||
2031 | static int wlan_get_encodeext(struct net_device *dev, | ||
2032 | struct iw_request_info *info, | ||
2033 | struct iw_point *dwrq, | ||
2034 | char *extra) | ||
2035 | { | ||
2036 | int ret = -EINVAL; | ||
2037 | wlan_private *priv = dev->priv; | ||
2038 | wlan_adapter *adapter = priv->adapter; | ||
2039 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
2040 | int index, max_key_len; | ||
2041 | |||
2042 | ENTER(); | ||
2043 | |||
2044 | max_key_len = dwrq->length - sizeof(*ext); | ||
2045 | if (max_key_len < 0) | ||
2046 | goto out; | ||
2047 | |||
2048 | index = dwrq->flags & IW_ENCODE_INDEX; | ||
2049 | if (index) { | ||
2050 | if (index < 1 || index > 4) | ||
2051 | goto out; | ||
2052 | index--; | ||
2053 | } else { | ||
2054 | index = adapter->wep_tx_keyidx; | ||
2055 | } | ||
2056 | |||
2057 | if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY && | ||
2058 | ext->alg != IW_ENCODE_ALG_WEP) { | ||
2059 | if (index != 0 || adapter->inframode != wlan802_11infrastructure) | ||
2060 | goto out; | ||
2061 | } | ||
2062 | |||
2063 | dwrq->flags = index + 1; | ||
2064 | memset(ext, 0, sizeof(*ext)); | ||
2065 | |||
2066 | if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) | ||
2067 | && !adapter->secinfo.WPAenabled && !adapter->secinfo.WPA2enabled) { | ||
2068 | ext->alg = IW_ENCODE_ALG_NONE; | ||
2069 | ext->key_len = 0; | ||
2070 | dwrq->flags |= IW_ENCODE_DISABLED; | ||
2071 | } else { | ||
2072 | u8 *key = NULL; | ||
2073 | |||
2074 | if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) | ||
2075 | && !adapter->secinfo.WPAenabled | ||
2076 | && !adapter->secinfo.WPA2enabled) { | ||
2077 | ext->alg = IW_ENCODE_ALG_WEP; | ||
2078 | ext->key_len = adapter->wep_keys[index].len; | ||
2079 | key = &adapter->wep_keys[index].key[0]; | ||
2080 | } else if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) && | ||
2081 | (adapter->secinfo.WPAenabled || | ||
2082 | adapter->secinfo.WPA2enabled)) { | ||
2083 | /* WPA */ | ||
2084 | ext->alg = IW_ENCODE_ALG_TKIP; | ||
2085 | ext->key_len = 0; | ||
2086 | } else { | ||
2087 | goto out; | ||
2088 | } | ||
2089 | |||
2090 | if (ext->key_len > max_key_len) { | ||
2091 | ret = -E2BIG; | ||
2092 | goto out; | ||
2093 | } | ||
2094 | |||
2095 | if (ext->key_len) | ||
2096 | memcpy(ext->key, key, ext->key_len); | ||
2097 | else | ||
2098 | dwrq->flags |= IW_ENCODE_NOKEY; | ||
2099 | dwrq->flags |= IW_ENCODE_ENABLED; | ||
2100 | } | ||
2101 | ret = 0; | ||
2102 | |||
2103 | out: | ||
2104 | LEAVE(); | ||
2105 | return ret; | ||
2106 | } | ||
2107 | |||
2108 | /** | ||
2109 | * @brief Set Encryption key Extended (WPA/802.1x and WEP) | ||
2110 | * | ||
2111 | * @param dev A pointer to net_device structure | ||
2112 | * @param info A pointer to iw_request_info structure | ||
2113 | * @param vwrq A pointer to iw_param structure | ||
2114 | * @param extra A pointer to extra data buf | ||
2115 | * @return 0 --success, otherwise fail | ||
2116 | */ | ||
2117 | static int wlan_set_encodeext(struct net_device *dev, | ||
2118 | struct iw_request_info *info, | ||
2119 | struct iw_point *dwrq, | ||
2120 | char *extra) | ||
2121 | { | ||
2122 | int ret = 0; | ||
2123 | wlan_private *priv = dev->priv; | ||
2124 | wlan_adapter *adapter = priv->adapter; | ||
2125 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
2126 | int alg = ext->alg; | ||
2127 | struct assoc_request * assoc_req; | ||
2128 | |||
2129 | ENTER(); | ||
2130 | |||
2131 | mutex_lock(&adapter->lock); | ||
2132 | assoc_req = wlan_get_association_request(adapter); | ||
2133 | if (!assoc_req) { | ||
2134 | ret = -ENOMEM; | ||
2135 | goto out; | ||
2136 | } | ||
2137 | |||
2138 | if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) { | ||
2139 | disable_wep (assoc_req); | ||
2140 | } else if (alg == IW_ENCODE_ALG_WEP) { | ||
2141 | u16 is_default = 0, index, set_tx_key = 0; | ||
2142 | |||
2143 | ret = validate_key_index(assoc_req->wep_tx_keyidx, | ||
2144 | (dwrq->flags & IW_ENCODE_INDEX), | ||
2145 | &index, &is_default); | ||
2146 | if (ret) | ||
2147 | goto out; | ||
2148 | |||
2149 | /* If WEP isn't enabled, or if there is no key data but a valid | ||
2150 | * index, or if the set-TX-key flag was passed, set the TX key. | ||
2151 | */ | ||
2152 | if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) | ||
2153 | || (dwrq->length == 0 && !is_default) | ||
2154 | || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) | ||
2155 | set_tx_key = 1; | ||
2156 | |||
2157 | /* Copy key to driver */ | ||
2158 | ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index, | ||
2159 | set_tx_key); | ||
2160 | if (ret) | ||
2161 | goto out; | ||
2162 | |||
2163 | if (dwrq->flags & IW_ENCODE_RESTRICTED) { | ||
2164 | assoc_req->secinfo.authmode = | ||
2165 | wlan802_11authmodeshared; | ||
2166 | } else if (dwrq->flags & IW_ENCODE_OPEN) { | ||
2167 | assoc_req->secinfo.authmode = | ||
2168 | wlan802_11authmodeopen; | ||
2169 | } | ||
2170 | |||
2171 | /* Mark the various WEP bits as modified */ | ||
2172 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | ||
2173 | if (dwrq->length) | ||
2174 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | ||
2175 | if (set_tx_key) | ||
2176 | set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); | ||
2177 | |||
2178 | } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { | ||
2179 | struct WLAN_802_11_KEY * pkey; | ||
2180 | |||
2181 | /* validate key length */ | ||
2182 | if (((alg == IW_ENCODE_ALG_TKIP) | ||
2183 | && (ext->key_len != KEY_LEN_WPA_TKIP)) | ||
2184 | || ((alg == IW_ENCODE_ALG_CCMP) | ||
2185 | && (ext->key_len != KEY_LEN_WPA_AES))) { | ||
2186 | lbs_pr_debug(1, "Invalid size %d for key of alg" | ||
2187 | "type %d.\n", | ||
2188 | ext->key_len, | ||
2189 | alg); | ||
2190 | ret = -EINVAL; | ||
2191 | goto out; | ||
2192 | } | ||
2193 | |||
2194 | if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) | ||
2195 | pkey = &assoc_req->wpa_mcast_key; | ||
2196 | else | ||
2197 | pkey = &assoc_req->wpa_unicast_key; | ||
2198 | |||
2199 | memset(pkey, 0, sizeof (struct WLAN_802_11_KEY)); | ||
2200 | memcpy(pkey->key, ext->key, ext->key_len); | ||
2201 | pkey->len = ext->key_len; | ||
2202 | pkey->flags = KEY_INFO_WPA_ENABLED; | ||
2203 | |||
2204 | if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { | ||
2205 | pkey->flags |= KEY_INFO_WPA_MCAST; | ||
2206 | set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); | ||
2207 | } else { | ||
2208 | pkey->flags |= KEY_INFO_WPA_UNICAST; | ||
2209 | set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); | ||
2210 | } | ||
2211 | |||
2212 | if (alg == IW_ENCODE_ALG_TKIP) | ||
2213 | pkey->type = KEY_TYPE_ID_TKIP; | ||
2214 | else if (alg == IW_ENCODE_ALG_CCMP) | ||
2215 | pkey->type = KEY_TYPE_ID_AES; | ||
2216 | |||
2217 | /* If WPA isn't enabled yet, do that now */ | ||
2218 | if ( assoc_req->secinfo.WPAenabled == 0 | ||
2219 | && assoc_req->secinfo.WPA2enabled == 0) { | ||
2220 | assoc_req->secinfo.WPAenabled = 1; | ||
2221 | assoc_req->secinfo.WPA2enabled = 1; | ||
2222 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | ||
2223 | } | ||
2224 | |||
2225 | disable_wep (assoc_req); | ||
2226 | } | ||
2227 | |||
2228 | out: | ||
2229 | if (ret == 0) { | ||
2230 | wlan_postpone_association_work(priv); | ||
2231 | } else { | ||
2232 | wlan_cancel_association_work(priv); | ||
2233 | } | ||
2234 | mutex_unlock(&adapter->lock); | ||
2235 | |||
2236 | LEAVE(); | ||
2237 | return ret; | ||
2238 | } | ||
2239 | |||
2240 | |||
2241 | static int wlan_set_genie(struct net_device *dev, | ||
2242 | struct iw_request_info *info, | ||
2243 | struct iw_point *dwrq, | ||
2244 | char *extra) | ||
2245 | { | ||
2246 | wlan_private *priv = dev->priv; | ||
2247 | wlan_adapter *adapter = priv->adapter; | ||
2248 | int ret = 0; | ||
2249 | struct assoc_request * assoc_req; | ||
2250 | |||
2251 | ENTER(); | ||
2252 | |||
2253 | mutex_lock(&adapter->lock); | ||
2254 | assoc_req = wlan_get_association_request(adapter); | ||
2255 | if (!assoc_req) { | ||
2256 | ret = -ENOMEM; | ||
2257 | goto out; | ||
2258 | } | ||
2259 | |||
2260 | if (dwrq->length > MAX_WPA_IE_LEN || | ||
2261 | (dwrq->length && extra == NULL)) { | ||
2262 | ret = -EINVAL; | ||
2263 | goto out; | ||
2264 | } | ||
2265 | |||
2266 | if (dwrq->length) { | ||
2267 | memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length); | ||
2268 | assoc_req->wpa_ie_len = dwrq->length; | ||
2269 | } else { | ||
2270 | memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie)); | ||
2271 | assoc_req->wpa_ie_len = 0; | ||
2272 | } | ||
2273 | |||
2274 | out: | ||
2275 | if (ret == 0) { | ||
2276 | set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags); | ||
2277 | wlan_postpone_association_work(priv); | ||
2278 | } else { | ||
2279 | wlan_cancel_association_work(priv); | ||
2280 | } | ||
2281 | mutex_unlock(&adapter->lock); | ||
2282 | |||
2283 | LEAVE(); | ||
2284 | return ret; | ||
2285 | } | ||
2286 | |||
2287 | static int wlan_get_genie(struct net_device *dev, | ||
2288 | struct iw_request_info *info, | ||
2289 | struct iw_point *dwrq, | ||
2290 | char *extra) | ||
2291 | { | ||
2292 | wlan_private *priv = dev->priv; | ||
2293 | wlan_adapter *adapter = priv->adapter; | ||
2294 | |||
2295 | ENTER(); | ||
2296 | |||
2297 | if (adapter->wpa_ie_len == 0) { | ||
2298 | dwrq->length = 0; | ||
2299 | LEAVE(); | ||
2300 | return 0; | ||
2301 | } | ||
2302 | |||
2303 | if (dwrq->length < adapter->wpa_ie_len) { | ||
2304 | LEAVE(); | ||
2305 | return -E2BIG; | ||
2306 | } | ||
2307 | |||
2308 | dwrq->length = adapter->wpa_ie_len; | ||
2309 | memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len); | ||
2310 | |||
2311 | LEAVE(); | ||
2312 | return 0; | ||
2313 | } | ||
2314 | |||
2315 | |||
2316 | static int wlan_set_auth(struct net_device *dev, | ||
2317 | struct iw_request_info *info, | ||
2318 | struct iw_param *dwrq, | ||
2319 | char *extra) | ||
2320 | { | ||
2321 | wlan_private *priv = dev->priv; | ||
2322 | wlan_adapter *adapter = priv->adapter; | ||
2323 | struct assoc_request * assoc_req; | ||
2324 | int ret = 0; | ||
2325 | int updated = 0; | ||
2326 | |||
2327 | ENTER(); | ||
2328 | |||
2329 | mutex_lock(&adapter->lock); | ||
2330 | assoc_req = wlan_get_association_request(adapter); | ||
2331 | if (!assoc_req) { | ||
2332 | ret = -ENOMEM; | ||
2333 | goto out; | ||
2334 | } | ||
2335 | |||
2336 | switch (dwrq->flags & IW_AUTH_INDEX) { | ||
2337 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
2338 | case IW_AUTH_CIPHER_PAIRWISE: | ||
2339 | case IW_AUTH_CIPHER_GROUP: | ||
2340 | case IW_AUTH_KEY_MGMT: | ||
2341 | /* | ||
2342 | * libertas does not use these parameters | ||
2343 | */ | ||
2344 | break; | ||
2345 | |||
2346 | case IW_AUTH_WPA_VERSION: | ||
2347 | if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) { | ||
2348 | assoc_req->secinfo.WPAenabled = 0; | ||
2349 | assoc_req->secinfo.WPA2enabled = 0; | ||
2350 | } | ||
2351 | if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) { | ||
2352 | assoc_req->secinfo.WPAenabled = 1; | ||
2353 | assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; | ||
2354 | assoc_req->secinfo.authmode = | ||
2355 | wlan802_11authmodeopen; | ||
2356 | } | ||
2357 | if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) { | ||
2358 | assoc_req->secinfo.WPA2enabled = 1; | ||
2359 | assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; | ||
2360 | assoc_req->secinfo.authmode = | ||
2361 | wlan802_11authmodeopen; | ||
2362 | } | ||
2363 | updated = 1; | ||
2364 | break; | ||
2365 | |||
2366 | case IW_AUTH_DROP_UNENCRYPTED: | ||
2367 | if (dwrq->value) { | ||
2368 | adapter->currentpacketfilter |= | ||
2369 | cmd_act_mac_strict_protection_enable; | ||
2370 | } else { | ||
2371 | adapter->currentpacketfilter &= | ||
2372 | ~cmd_act_mac_strict_protection_enable; | ||
2373 | } | ||
2374 | updated = 1; | ||
2375 | break; | ||
2376 | |||
2377 | case IW_AUTH_80211_AUTH_ALG: | ||
2378 | if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) { | ||
2379 | assoc_req->secinfo.authmode = | ||
2380 | wlan802_11authmodeshared; | ||
2381 | } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) { | ||
2382 | assoc_req->secinfo.authmode = | ||
2383 | wlan802_11authmodeopen; | ||
2384 | } else if (dwrq->value & IW_AUTH_ALG_LEAP) { | ||
2385 | assoc_req->secinfo.authmode = | ||
2386 | wlan802_11authmodenetworkEAP; | ||
2387 | } else { | ||
2388 | ret = -EINVAL; | ||
2389 | } | ||
2390 | updated = 1; | ||
2391 | break; | ||
2392 | |||
2393 | case IW_AUTH_WPA_ENABLED: | ||
2394 | if (dwrq->value) { | ||
2395 | if (!assoc_req->secinfo.WPAenabled && | ||
2396 | !assoc_req->secinfo.WPA2enabled) { | ||
2397 | assoc_req->secinfo.WPAenabled = 1; | ||
2398 | assoc_req->secinfo.WPA2enabled = 1; | ||
2399 | assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; | ||
2400 | assoc_req->secinfo.authmode = | ||
2401 | wlan802_11authmodeopen; | ||
2402 | } | ||
2403 | } else { | ||
2404 | assoc_req->secinfo.WPAenabled = 0; | ||
2405 | assoc_req->secinfo.WPA2enabled = 0; | ||
2406 | } | ||
2407 | updated = 1; | ||
2408 | break; | ||
2409 | |||
2410 | default: | ||
2411 | ret = -EOPNOTSUPP; | ||
2412 | break; | ||
2413 | } | ||
2414 | |||
2415 | out: | ||
2416 | if (ret == 0) { | ||
2417 | if (updated) | ||
2418 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | ||
2419 | wlan_postpone_association_work(priv); | ||
2420 | } else if (ret != -EOPNOTSUPP) { | ||
2421 | wlan_cancel_association_work(priv); | ||
2422 | } | ||
2423 | mutex_unlock(&adapter->lock); | ||
2424 | |||
2425 | LEAVE(); | ||
2426 | return ret; | ||
2427 | } | ||
2428 | |||
2429 | static int wlan_get_auth(struct net_device *dev, | ||
2430 | struct iw_request_info *info, | ||
2431 | struct iw_param *dwrq, | ||
2432 | char *extra) | ||
2433 | { | ||
2434 | wlan_private *priv = dev->priv; | ||
2435 | wlan_adapter *adapter = priv->adapter; | ||
2436 | |||
2437 | ENTER(); | ||
2438 | |||
2439 | switch (dwrq->flags & IW_AUTH_INDEX) { | ||
2440 | case IW_AUTH_WPA_VERSION: | ||
2441 | dwrq->value = 0; | ||
2442 | if (adapter->secinfo.WPAenabled) | ||
2443 | dwrq->value |= IW_AUTH_WPA_VERSION_WPA; | ||
2444 | if (adapter->secinfo.WPA2enabled) | ||
2445 | dwrq->value |= IW_AUTH_WPA_VERSION_WPA2; | ||
2446 | if (!dwrq->value) | ||
2447 | dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED; | ||
2448 | break; | ||
2449 | |||
2450 | case IW_AUTH_DROP_UNENCRYPTED: | ||
2451 | dwrq->value = 0; | ||
2452 | if (adapter->currentpacketfilter & | ||
2453 | cmd_act_mac_strict_protection_enable) | ||
2454 | dwrq->value = 1; | ||
2455 | break; | ||
2456 | |||
2457 | case IW_AUTH_80211_AUTH_ALG: | ||
2458 | switch (adapter->secinfo.authmode) { | ||
2459 | case wlan802_11authmodeshared: | ||
2460 | dwrq->value = IW_AUTH_ALG_SHARED_KEY; | ||
2461 | break; | ||
2462 | case wlan802_11authmodeopen: | ||
2463 | dwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; | ||
2464 | break; | ||
2465 | case wlan802_11authmodenetworkEAP: | ||
2466 | dwrq->value = IW_AUTH_ALG_LEAP; | ||
2467 | break; | ||
2468 | default: | ||
2469 | break; | ||
2470 | } | ||
2471 | break; | ||
2472 | |||
2473 | case IW_AUTH_WPA_ENABLED: | ||
2474 | if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled) | ||
2475 | dwrq->value = 1; | ||
2476 | break; | ||
2477 | |||
2478 | default: | ||
2479 | LEAVE(); | ||
2480 | return -EOPNOTSUPP; | ||
2481 | } | ||
2482 | |||
2483 | LEAVE(); | ||
2484 | return 0; | ||
2485 | } | ||
2486 | |||
2487 | |||
2488 | static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info, | ||
2489 | struct iw_param *vwrq, char *extra) | ||
2490 | { | ||
2491 | int ret = 0; | ||
2492 | wlan_private *priv = dev->priv; | ||
2493 | wlan_adapter *adapter = priv->adapter; | ||
2494 | |||
2495 | u16 dbm; | ||
2496 | |||
2497 | ENTER(); | ||
2498 | |||
2499 | if (vwrq->disabled) { | ||
2500 | wlan_radio_ioctl(priv, RADIO_OFF); | ||
2501 | return 0; | ||
2502 | } | ||
2503 | |||
2504 | adapter->preamble = cmd_type_auto_preamble; | ||
2505 | |||
2506 | wlan_radio_ioctl(priv, RADIO_ON); | ||
2507 | |||
2508 | if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) { | ||
2509 | dbm = (u16) mw_to_dbm(vwrq->value); | ||
2510 | } else | ||
2511 | dbm = (u16) vwrq->value; | ||
2512 | |||
2513 | /* auto tx power control */ | ||
2514 | |||
2515 | if (vwrq->fixed == 0) | ||
2516 | dbm = 0xffff; | ||
2517 | |||
2518 | lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm); | ||
2519 | |||
2520 | ret = libertas_prepare_and_send_command(priv, | ||
2521 | cmd_802_11_rf_tx_power, | ||
2522 | cmd_act_tx_power_opt_set_low, | ||
2523 | cmd_option_waitforrsp, 0, (void *)&dbm); | ||
2524 | |||
2525 | LEAVE(); | ||
2526 | return ret; | ||
2527 | } | ||
2528 | |||
2529 | static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info, | ||
2530 | struct iw_point *dwrq, char *extra) | ||
2531 | { | ||
2532 | wlan_private *priv = dev->priv; | ||
2533 | wlan_adapter *adapter = priv->adapter; | ||
2534 | |||
2535 | ENTER(); | ||
2536 | /* | ||
2537 | * Note : if dwrq->flags != 0, we should get the relevant SSID from | ||
2538 | * the SSID list... | ||
2539 | */ | ||
2540 | |||
2541 | /* | ||
2542 | * Get the current SSID | ||
2543 | */ | ||
2544 | if (adapter->connect_status == libertas_connected) { | ||
2545 | memcpy(extra, adapter->curbssparams.ssid.ssid, | ||
2546 | adapter->curbssparams.ssid.ssidlength); | ||
2547 | extra[adapter->curbssparams.ssid.ssidlength] = '\0'; | ||
2548 | } else { | ||
2549 | memset(extra, 0, 32); | ||
2550 | extra[adapter->curbssparams.ssid.ssidlength] = '\0'; | ||
2551 | } | ||
2552 | /* | ||
2553 | * If none, we may want to get the one that was set | ||
2554 | */ | ||
2555 | |||
2556 | /* To make the driver backward compatible with WPA supplicant v0.2.4 */ | ||
2557 | if (dwrq->length == 32) /* check with WPA supplicant buffer size */ | ||
2558 | dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength, | ||
2559 | IW_ESSID_MAX_SIZE); | ||
2560 | else | ||
2561 | dwrq->length = adapter->curbssparams.ssid.ssidlength + 1; | ||
2562 | |||
2563 | dwrq->flags = 1; /* active */ | ||
2564 | |||
2565 | LEAVE(); | ||
2566 | return 0; | ||
2567 | } | ||
2568 | |||
2569 | static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info, | ||
2570 | struct iw_point *dwrq, char *extra) | ||
2571 | { | ||
2572 | wlan_private *priv = dev->priv; | ||
2573 | wlan_adapter *adapter = priv->adapter; | ||
2574 | int ret = 0; | ||
2575 | struct WLAN_802_11_SSID ssid; | ||
2576 | struct assoc_request * assoc_req; | ||
2577 | int ssid_len = dwrq->length; | ||
2578 | |||
2579 | ENTER(); | ||
2580 | |||
2581 | /* | ||
2582 | * WE-20 and earlier NULL pad the end of the SSID and increment | ||
2583 | * SSID length so it can be used like a string. WE-21 and later don't, | ||
2584 | * but some userspace tools aren't able to cope with the change. | ||
2585 | */ | ||
2586 | if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0')) | ||
2587 | ssid_len--; | ||
2588 | |||
2589 | /* Check the size of the string */ | ||
2590 | if (ssid_len > IW_ESSID_MAX_SIZE) { | ||
2591 | ret = -E2BIG; | ||
2592 | goto out; | ||
2593 | } | ||
2594 | |||
2595 | memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID)); | ||
2596 | |||
2597 | if (!dwrq->flags || !ssid_len) { | ||
2598 | /* "any" SSID requested; leave SSID blank */ | ||
2599 | } else { | ||
2600 | /* Specific SSID requested */ | ||
2601 | memcpy(&ssid.ssid, extra, ssid_len); | ||
2602 | ssid.ssidlength = ssid_len; | ||
2603 | } | ||
2604 | |||
2605 | lbs_pr_debug(1, "Requested new SSID = %s\n", | ||
2606 | (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any"); | ||
2607 | |||
2608 | out: | ||
2609 | mutex_lock(&adapter->lock); | ||
2610 | if (ret == 0) { | ||
2611 | /* Get or create the current association request */ | ||
2612 | assoc_req = wlan_get_association_request(adapter); | ||
2613 | if (!assoc_req) { | ||
2614 | ret = -ENOMEM; | ||
2615 | } else { | ||
2616 | /* Copy the SSID to the association request */ | ||
2617 | memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID)); | ||
2618 | set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); | ||
2619 | wlan_postpone_association_work(priv); | ||
2620 | } | ||
2621 | } | ||
2622 | |||
2623 | /* Cancel the association request if there was an error */ | ||
2624 | if (ret != 0) { | ||
2625 | wlan_cancel_association_work(priv); | ||
2626 | } | ||
2627 | |||
2628 | mutex_unlock(&adapter->lock); | ||
2629 | |||
2630 | LEAVE(); | ||
2631 | return ret; | ||
2632 | } | ||
2633 | |||
2634 | /** | ||
2635 | * @brief Connect to the AP or Ad-hoc Network with specific bssid | ||
2636 | * | ||
2637 | * @param dev A pointer to net_device structure | ||
2638 | * @param info A pointer to iw_request_info structure | ||
2639 | * @param awrq A pointer to iw_param structure | ||
2640 | * @param extra A pointer to extra data buf | ||
2641 | * @return 0 --success, otherwise fail | ||
2642 | */ | ||
2643 | static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info, | ||
2644 | struct sockaddr *awrq, char *extra) | ||
2645 | { | ||
2646 | wlan_private *priv = dev->priv; | ||
2647 | wlan_adapter *adapter = priv->adapter; | ||
2648 | struct assoc_request * assoc_req; | ||
2649 | int ret = 0; | ||
2650 | |||
2651 | ENTER(); | ||
2652 | |||
2653 | if (awrq->sa_family != ARPHRD_ETHER) | ||
2654 | return -EINVAL; | ||
2655 | |||
2656 | lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data)); | ||
2657 | |||
2658 | mutex_lock(&adapter->lock); | ||
2659 | |||
2660 | /* Get or create the current association request */ | ||
2661 | assoc_req = wlan_get_association_request(adapter); | ||
2662 | if (!assoc_req) { | ||
2663 | wlan_cancel_association_work(priv); | ||
2664 | ret = -ENOMEM; | ||
2665 | } else { | ||
2666 | /* Copy the BSSID to the association request */ | ||
2667 | memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN); | ||
2668 | set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags); | ||
2669 | wlan_postpone_association_work(priv); | ||
2670 | } | ||
2671 | |||
2672 | mutex_unlock(&adapter->lock); | ||
2673 | |||
2674 | return ret; | ||
2675 | } | ||
2676 | |||
2677 | void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen) | ||
2678 | { | ||
2679 | union { | ||
2680 | u32 l; | ||
2681 | u8 c[4]; | ||
2682 | } ver; | ||
2683 | char fwver[32]; | ||
2684 | |||
2685 | mutex_lock(&adapter->lock); | ||
2686 | ver.l = adapter->fwreleasenumber; | ||
2687 | mutex_unlock(&adapter->lock); | ||
2688 | |||
2689 | if (ver.c[3] == 0) | ||
2690 | sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]); | ||
2691 | else | ||
2692 | sprintf(fwver, "%u.%u.%u.p%u", | ||
2693 | ver.c[2], ver.c[1], ver.c[0], ver.c[3]); | ||
2694 | |||
2695 | snprintf(fwversion, maxlen, fwver); | ||
2696 | } | ||
2697 | |||
2698 | |||
2699 | /* | ||
2700 | * iwconfig settable callbacks | ||
2701 | */ | ||
2702 | static const iw_handler wlan_handler[] = { | ||
2703 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ | ||
2704 | (iw_handler) wlan_get_name, /* SIOCGIWNAME */ | ||
2705 | (iw_handler) NULL, /* SIOCSIWNWID */ | ||
2706 | (iw_handler) NULL, /* SIOCGIWNWID */ | ||
2707 | (iw_handler) wlan_set_freq, /* SIOCSIWFREQ */ | ||
2708 | (iw_handler) wlan_get_freq, /* SIOCGIWFREQ */ | ||
2709 | (iw_handler) wlan_set_mode, /* SIOCSIWMODE */ | ||
2710 | (iw_handler) wlan_get_mode, /* SIOCGIWMODE */ | ||
2711 | (iw_handler) NULL, /* SIOCSIWSENS */ | ||
2712 | (iw_handler) NULL, /* SIOCGIWSENS */ | ||
2713 | (iw_handler) NULL, /* SIOCSIWRANGE */ | ||
2714 | (iw_handler) wlan_get_range, /* SIOCGIWRANGE */ | ||
2715 | (iw_handler) NULL, /* SIOCSIWPRIV */ | ||
2716 | (iw_handler) NULL, /* SIOCGIWPRIV */ | ||
2717 | (iw_handler) NULL, /* SIOCSIWSTATS */ | ||
2718 | (iw_handler) NULL, /* SIOCGIWSTATS */ | ||
2719 | iw_handler_set_spy, /* SIOCSIWSPY */ | ||
2720 | iw_handler_get_spy, /* SIOCGIWSPY */ | ||
2721 | iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ | ||
2722 | iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ | ||
2723 | (iw_handler) wlan_set_wap, /* SIOCSIWAP */ | ||
2724 | (iw_handler) wlan_get_wap, /* SIOCGIWAP */ | ||
2725 | (iw_handler) NULL, /* SIOCSIWMLME */ | ||
2726 | (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ | ||
2727 | (iw_handler) libertas_set_scan, /* SIOCSIWSCAN */ | ||
2728 | (iw_handler) libertas_get_scan, /* SIOCGIWSCAN */ | ||
2729 | (iw_handler) wlan_set_essid, /* SIOCSIWESSID */ | ||
2730 | (iw_handler) wlan_get_essid, /* SIOCGIWESSID */ | ||
2731 | (iw_handler) wlan_set_nick, /* SIOCSIWNICKN */ | ||
2732 | (iw_handler) wlan_get_nick, /* SIOCGIWNICKN */ | ||
2733 | (iw_handler) NULL, /* -- hole -- */ | ||
2734 | (iw_handler) NULL, /* -- hole -- */ | ||
2735 | (iw_handler) wlan_set_rate, /* SIOCSIWRATE */ | ||
2736 | (iw_handler) wlan_get_rate, /* SIOCGIWRATE */ | ||
2737 | (iw_handler) wlan_set_rts, /* SIOCSIWRTS */ | ||
2738 | (iw_handler) wlan_get_rts, /* SIOCGIWRTS */ | ||
2739 | (iw_handler) wlan_set_frag, /* SIOCSIWFRAG */ | ||
2740 | (iw_handler) wlan_get_frag, /* SIOCGIWFRAG */ | ||
2741 | (iw_handler) wlan_set_txpow, /* SIOCSIWTXPOW */ | ||
2742 | (iw_handler) wlan_get_txpow, /* SIOCGIWTXPOW */ | ||
2743 | (iw_handler) wlan_set_retry, /* SIOCSIWRETRY */ | ||
2744 | (iw_handler) wlan_get_retry, /* SIOCGIWRETRY */ | ||
2745 | (iw_handler) wlan_set_encode, /* SIOCSIWENCODE */ | ||
2746 | (iw_handler) wlan_get_encode, /* SIOCGIWENCODE */ | ||
2747 | (iw_handler) wlan_set_power, /* SIOCSIWPOWER */ | ||
2748 | (iw_handler) wlan_get_power, /* SIOCGIWPOWER */ | ||
2749 | (iw_handler) NULL, /* -- hole -- */ | ||
2750 | (iw_handler) NULL, /* -- hole -- */ | ||
2751 | (iw_handler) wlan_set_genie, /* SIOCSIWGENIE */ | ||
2752 | (iw_handler) wlan_get_genie, /* SIOCGIWGENIE */ | ||
2753 | (iw_handler) wlan_set_auth, /* SIOCSIWAUTH */ | ||
2754 | (iw_handler) wlan_get_auth, /* SIOCGIWAUTH */ | ||
2755 | (iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */ | ||
2756 | (iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */ | ||
2757 | (iw_handler) NULL, /* SIOCSIWPMKSA */ | ||
2758 | }; | ||
2759 | |||
2760 | struct iw_handler_def libertas_handler_def = { | ||
2761 | .num_standard = sizeof(wlan_handler) / sizeof(iw_handler), | ||
2762 | .num_private = sizeof(wlan_private_handler) / sizeof(iw_handler), | ||
2763 | .num_private_args = sizeof(wlan_private_args) / | ||
2764 | sizeof(struct iw_priv_args), | ||
2765 | .standard = (iw_handler *) wlan_handler, | ||
2766 | .private = (iw_handler *) wlan_private_handler, | ||
2767 | .private_args = (struct iw_priv_args *)wlan_private_args, | ||
2768 | .get_wireless_stats = wlan_get_wireless_stats, | ||
2769 | }; | ||
diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h new file mode 100644 index 000000000000..39f367c38d90 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.h | |||
@@ -0,0 +1,147 @@ | |||
1 | /** | ||
2 | * This file contains definition for IOCTL call. | ||
3 | */ | ||
4 | #ifndef _WLAN_WEXT_H_ | ||
5 | #define _WLAN_WEXT_H_ | ||
6 | |||
7 | #define SUBCMD_OFFSET 4 | ||
8 | #define SUBCMD_DATA(x) *((int *)(x->u.name + SUBCMD_OFFSET)) | ||
9 | |||
10 | /** PRIVATE CMD ID */ | ||
11 | #define WLANIOCTL SIOCIWFIRSTPRIV | ||
12 | |||
13 | #define WLANSETWPAIE (WLANIOCTL + 0) | ||
14 | |||
15 | #define WLAN_SETINT_GETINT (WLANIOCTL + 7) | ||
16 | #define WLANNF 1 | ||
17 | #define WLANRSSI 2 | ||
18 | #define WLANENABLE11D 5 | ||
19 | #define WLANADHOCGRATE 6 | ||
20 | #define WLAN_SUBCMD_SET_PRESCAN 11 | ||
21 | |||
22 | #define WLAN_SETNONE_GETNONE (WLANIOCTL + 8) | ||
23 | #define WLANDEAUTH 1 | ||
24 | #define WLANRADIOON 2 | ||
25 | #define WLANRADIOOFF 3 | ||
26 | #define WLANREMOVEADHOCAES 4 | ||
27 | #define WLANADHOCSTOP 5 | ||
28 | #define WLANCIPHERTEST 6 | ||
29 | #define WLANCRYPTOTEST 7 | ||
30 | |||
31 | #define WLANWLANIDLEON 10 | ||
32 | #define WLANWLANIDLEOFF 11 | ||
33 | #define WLAN_SUBCMD_BT_RESET 13 | ||
34 | #define WLAN_SUBCMD_FWT_RESET 14 | ||
35 | |||
36 | #define WLANGETLOG (WLANIOCTL + 9) | ||
37 | #define GETLOG_BUFSIZE 300 | ||
38 | |||
39 | #define WLANSCAN_TYPE (WLANIOCTL + 11) | ||
40 | |||
41 | #define WLAN_SETNONE_GETONEINT (WLANIOCTL + 15) | ||
42 | #define WLANGETREGION 1 | ||
43 | #define WLAN_GET_LISTEN_INTERVAL 2 | ||
44 | #define WLAN_GET_MULTIPLE_DTIM 3 | ||
45 | #define WLAN_GET_TX_RATE 4 | ||
46 | #define WLANGETBCNAVG 5 | ||
47 | |||
48 | #define WLAN_GET_LINKMODE 6 | ||
49 | #define WLAN_GET_RADIOMODE 7 | ||
50 | #define WLAN_GET_DEBUGMODE 8 | ||
51 | #define WLAN_SUBCMD_FWT_CLEANUP 15 | ||
52 | #define WLAN_SUBCMD_FWT_TIME 16 | ||
53 | #define WLAN_SUBCMD_MESH_GET_TTL 17 | ||
54 | |||
55 | #define WLANREGCFRDWR (WLANIOCTL + 18) | ||
56 | |||
57 | #define WLAN_SETNONE_GETTWELVE_CHAR (WLANIOCTL + 19) | ||
58 | #define WLAN_SUBCMD_GETRXANTENNA 1 | ||
59 | #define WLAN_SUBCMD_GETTXANTENNA 2 | ||
60 | #define WLAN_GET_TSF 3 | ||
61 | |||
62 | #define WLAN_SETNONE_GETWORDCHAR (WLANIOCTL + 21) | ||
63 | #define WLANGETADHOCAES 1 | ||
64 | |||
65 | #define WLAN_SETONEINT_GETONEINT (WLANIOCTL + 23) | ||
66 | #define WLAN_BEACON_INTERVAL 1 | ||
67 | #define WLAN_LISTENINTRVL 4 | ||
68 | |||
69 | #define WLAN_TXCONTROL 6 | ||
70 | #define WLAN_NULLPKTINTERVAL 7 | ||
71 | |||
72 | #define WLAN_SETONEINT_GETNONE (WLANIOCTL + 24) | ||
73 | #define WLAN_SUBCMD_SETRXANTENNA 1 | ||
74 | #define WLAN_SUBCMD_SETTXANTENNA 2 | ||
75 | #define WLANSETAUTHALG 5 | ||
76 | #define WLANSET8021XAUTHALG 6 | ||
77 | #define WLANSETENCRYPTIONMODE 7 | ||
78 | #define WLANSETREGION 8 | ||
79 | #define WLAN_SET_LISTEN_INTERVAL 9 | ||
80 | |||
81 | #define WLAN_SET_MULTIPLE_DTIM 10 | ||
82 | #define WLAN_SET_ATIM_WINDOW 11 | ||
83 | #define WLANSETBCNAVG 13 | ||
84 | #define WLANSETDATAAVG 14 | ||
85 | #define WLAN_SET_LINKMODE 15 | ||
86 | #define WLAN_SET_RADIOMODE 16 | ||
87 | #define WLAN_SET_DEBUGMODE 17 | ||
88 | #define WLAN_SUBCMD_MESH_SET_TTL 18 | ||
89 | |||
90 | #define WLAN_SET128CHAR_GET128CHAR (WLANIOCTL + 25) | ||
91 | #define WLANSCAN_MODE 6 | ||
92 | |||
93 | #define WLAN_GET_ADHOC_STATUS 9 | ||
94 | |||
95 | #define WLAN_SUBCMD_BT_ADD 18 | ||
96 | #define WLAN_SUBCMD_BT_DEL 19 | ||
97 | #define WLAN_SUBCMD_BT_LIST 20 | ||
98 | #define WLAN_SUBCMD_FWT_ADD 21 | ||
99 | #define WLAN_SUBCMD_FWT_DEL 22 | ||
100 | #define WLAN_SUBCMD_FWT_LOOKUP 23 | ||
101 | #define WLAN_SUBCMD_FWT_LIST_NEIGHBOR 24 | ||
102 | #define WLAN_SUBCMD_FWT_LIST 25 | ||
103 | #define WLAN_SUBCMD_FWT_LIST_ROUTE 26 | ||
104 | |||
105 | #define WLAN_SET_GET_SIXTEEN_INT (WLANIOCTL + 29) | ||
106 | #define WLAN_TPCCFG 1 | ||
107 | #define WLAN_POWERCFG 2 | ||
108 | |||
109 | #define WLAN_AUTO_FREQ_SET 3 | ||
110 | #define WLAN_AUTO_FREQ_GET 4 | ||
111 | #define WLAN_LED_GPIO_CTRL 5 | ||
112 | #define WLAN_SCANPROBES 6 | ||
113 | #define WLAN_ADAPT_RATESET 8 | ||
114 | #define WLAN_INACTIVITY_TIMEOUT 9 | ||
115 | #define WLANSNR 10 | ||
116 | #define WLAN_GET_RATE 11 | ||
117 | #define WLAN_GET_RXINFO 12 | ||
118 | |||
119 | #define WLANCMD52RDWR (WLANIOCTL + 30) | ||
120 | #define WLANCMD53RDWR (WLANIOCTL + 31) | ||
121 | #define CMD53BUFLEN 32 | ||
122 | |||
123 | #define REG_MAC 0x19 | ||
124 | #define REG_BBP 0x1a | ||
125 | #define REG_RF 0x1b | ||
126 | #define REG_EEPROM 0x59 | ||
127 | #define WLAN_LINKMODE_802_3 0 | ||
128 | #define WLAN_LINKMODE_802_11 2 | ||
129 | #define WLAN_RADIOMODE_NONE 0 | ||
130 | #define WLAN_RADIOMODE_RADIOTAP 2 | ||
131 | |||
132 | /** wlan_ioctl_regrdwr */ | ||
133 | struct wlan_ioctl_regrdwr { | ||
134 | /** Which register to access */ | ||
135 | u16 whichreg; | ||
136 | /** Read or Write */ | ||
137 | u16 action; | ||
138 | u32 offset; | ||
139 | u16 NOB; | ||
140 | u32 value; | ||
141 | }; | ||
142 | |||
143 | extern struct iw_handler_def libertas_handler_def; | ||
144 | int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i); | ||
145 | int wlan_radio_ioctl(wlan_private * priv, u8 option); | ||
146 | |||
147 | #endif /* _WLAN_WEXT_H_ */ | ||