diff options
author | Marcelo Tosatti <marcelo@kvack.org> | 2007-02-10 09:25:27 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-04-28 11:00:54 -0400 |
commit | 876c9d3aeb989cf1961f2c228d309ba5dcfb1172 (patch) | |
tree | 239e9db92d13abc799c1ffc5304d8ec1503dbc61 /drivers/net/wireless/libertas/11d.c | |
parent | 35c3404efa7407811b706453f83d39b2539dcbd0 (diff) |
[PATCH] Marvell Libertas 8388 802.11b/g USB driver
Add the Marvell Libertas 8388 802.11 USB driver.
Signed-off-by: Marcelo Tosatti <marcelo@kvack.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/11d.c')
-rw-r--r-- | drivers/net/wireless/libertas/11d.c | 754 |
1 files changed, 754 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 | } | ||