]>
Commit | Line | Data |
---|---|---|
876c9d3a MT |
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) { | |
9012b28a | 98 | lbs_deb_11d("firstchan found\n"); |
876c9d3a MT |
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) { | |
9012b28a | 132 | lbs_deb_11d("11D: Found Chan:%d\n", chan); |
876c9d3a MT |
133 | return 1; |
134 | } | |
135 | } | |
136 | ||
9012b28a | 137 | lbs_deb_11d("11D: Not Find Chan:%d\n", chan); |
876c9d3a MT |
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 | ||
9012b28a | 177 | lbs_deb_11d("11D:nrchan=%d\n", nr_chan); |
876c9d3a MT |
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 | ||
9012b28a | 215 | lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband); |
876c9d3a MT |
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) { | |
9012b28a | 236 | lbs_deb_11d("11D: region_chan is NULL\n"); |
876c9d3a MT |
237 | return; |
238 | } | |
239 | ||
240 | cfp = region_chan->CFP; | |
241 | if (cfp == NULL) { | |
9012b28a | 242 | lbs_deb_11d("11D: cfp equal NULL \n"); |
876c9d3a MT |
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 | ||
9012b28a | 251 | lbs_deb_11d("11D: region[0x%x] band[%d]\n", parsed_region_chan->region, |
876c9d3a MT |
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; | |
9012b28a | 257 | lbs_deb_11d("11D: Chan[%d] Pwr[%d]\n", |
876c9d3a MT |
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 | ||
9012b28a | 263 | lbs_deb_11d("11D: nrchan[%d]\n", parsed_region_chan->nr_chan); |
876c9d3a MT |
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; | |
9012b28a | 280 | int ret = 0; |
876c9d3a | 281 | |
9012b28a | 282 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
283 | |
284 | cfp = libertas_get_region_cfp_table(region, band, &cfp_no); | |
285 | if (cfp == NULL) | |
286 | return 0; | |
287 | ||
288 | for (idx = 0; idx < cfp_no; idx++) { | |
289 | if (chan == (cfp + idx)->channel) { | |
290 | /* If Mrvl Chip Supported? */ | |
291 | if ((cfp + idx)->unsupported) { | |
9012b28a | 292 | ret = 0; |
876c9d3a | 293 | } else { |
9012b28a | 294 | ret = 1; |
876c9d3a | 295 | } |
9012b28a | 296 | goto done; |
876c9d3a MT |
297 | } |
298 | } | |
299 | ||
300 | /*chan is not in the region table */ | |
9012b28a HS |
301 | |
302 | done: | |
303 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | |
304 | return ret; | |
876c9d3a MT |
305 | } |
306 | ||
307 | /** | |
308 | * @brief This function checks if chan txpwr is learned from AP/IBSS | |
309 | * @param chan chan number | |
310 | * @param parsed_region_chan pointer to parsed_region_chan_11d | |
311 | * @return 0 | |
312 | */ | |
313 | static int parse_domain_info_11d(struct ieeetypes_countryinfofullset* | |
314 | countryinfo, | |
315 | u8 band, | |
316 | struct parsed_region_chan_11d * | |
317 | parsed_region_chan) | |
318 | { | |
319 | u8 nr_subband, nrchan; | |
320 | u8 lastchan, firstchan; | |
321 | u8 region; | |
322 | u8 curchan = 0; | |
323 | ||
324 | u8 idx = 0; /*chan index in parsed_region_chan */ | |
325 | ||
326 | u8 j, i; | |
327 | ||
9012b28a | 328 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
329 | |
330 | /*validation Rules: | |
331 | 1. valid region Code | |
332 | 2. First Chan increment | |
333 | 3. channel range no overlap | |
334 | 4. channel is valid? | |
335 | 5. channel is supported by region? | |
336 | 6. Others | |
337 | */ | |
338 | ||
339 | lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30); | |
340 | ||
341 | if ((*(countryinfo->countrycode)) == 0 | |
342 | || (countryinfo->len <= COUNTRY_CODE_LEN)) { | |
343 | /* No region Info or Wrong region info: treat as No 11D info */ | |
9012b28a | 344 | goto done; |
876c9d3a MT |
345 | } |
346 | ||
347 | /*Step1: check region_code */ | |
348 | parsed_region_chan->region = region = | |
349 | wlan_region_2_code(countryinfo->countrycode); | |
350 | ||
9012b28a | 351 | lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region); |
876c9d3a MT |
352 | lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode, |
353 | COUNTRY_CODE_LEN); | |
354 | ||
355 | parsed_region_chan->band = band; | |
356 | ||
357 | memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, | |
358 | COUNTRY_CODE_LEN); | |
359 | ||
360 | nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) / | |
361 | sizeof(struct ieeetypes_subbandset); | |
362 | ||
363 | for (j = 0, lastchan = 0; j < nr_subband; j++) { | |
364 | ||
365 | if (countryinfo->subband[j].firstchan <= lastchan) { | |
366 | /*Step2&3. Check First Chan Num increment and no overlap */ | |
9012b28a | 367 | lbs_deb_11d("11D: Chan[%d>%d] Overlap\n", |
876c9d3a MT |
368 | countryinfo->subband[j].firstchan, lastchan); |
369 | continue; | |
370 | } | |
371 | ||
372 | firstchan = countryinfo->subband[j].firstchan; | |
373 | nrchan = countryinfo->subband[j].nrchan; | |
374 | ||
375 | for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { | |
376 | /*step4: channel is supported? */ | |
377 | ||
378 | if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) { | |
379 | /* Chan is not found in UN table */ | |
9012b28a | 380 | lbs_deb_11d("chan is not supported: %d \n", i); |
876c9d3a MT |
381 | break; |
382 | } | |
383 | ||
384 | lastchan = curchan; | |
385 | ||
386 | if (wlan_region_chan_supported_11d | |
387 | (region, band, curchan)) { | |
388 | /*step5: Check if curchan is supported by mrvl in region */ | |
389 | parsed_region_chan->chanpwr[idx].chan = curchan; | |
390 | parsed_region_chan->chanpwr[idx].pwr = | |
391 | countryinfo->subband[j].maxtxpwr; | |
392 | idx++; | |
393 | } else { | |
394 | /*not supported and ignore the chan */ | |
9012b28a | 395 | lbs_deb_11d( |
876c9d3a MT |
396 | "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n", |
397 | i, curchan, region, band); | |
398 | } | |
399 | } | |
400 | ||
401 | /*Step6: Add other checking if any */ | |
402 | ||
403 | } | |
404 | ||
405 | parsed_region_chan->nr_chan = idx; | |
406 | ||
9012b28a | 407 | lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan); |
876c9d3a MT |
408 | lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan, |
409 | 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); | |
410 | ||
9012b28a HS |
411 | done: |
412 | lbs_deb_enter(LBS_DEB_11D); | |
876c9d3a MT |
413 | return 0; |
414 | } | |
415 | ||
416 | /** | |
417 | * @brief This function calculates the scan type for channels | |
418 | * @param chan chan number | |
419 | * @param parsed_region_chan pointer to parsed_region_chan_11d | |
420 | * @return PASSIVE if chan is unknown; ACTIVE if chan is known | |
421 | */ | |
422 | u8 libertas_get_scan_type_11d(u8 chan, | |
423 | struct parsed_region_chan_11d * parsed_region_chan) | |
424 | { | |
425 | u8 scan_type = cmd_scan_type_passive; | |
426 | ||
9012b28a | 427 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
428 | |
429 | if (wlan_channel_known_11d(chan, parsed_region_chan)) { | |
9012b28a | 430 | lbs_deb_11d("11D: Found and do Active Scan\n"); |
876c9d3a MT |
431 | scan_type = cmd_scan_type_active; |
432 | } else { | |
9012b28a | 433 | lbs_deb_11d("11D: Not Find and do Passive Scan\n"); |
876c9d3a MT |
434 | } |
435 | ||
9012b28a | 436 | lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type); |
876c9d3a MT |
437 | return scan_type; |
438 | ||
439 | } | |
440 | ||
441 | void libertas_init_11d(wlan_private * priv) | |
442 | { | |
443 | priv->adapter->enable11d = 0; | |
444 | memset(&(priv->adapter->parsed_region_chan), 0, | |
445 | sizeof(struct parsed_region_chan_11d)); | |
446 | return; | |
447 | } | |
448 | ||
449 | static int wlan_enable_11d(wlan_private * priv, u8 flag) | |
450 | { | |
451 | int ret; | |
452 | ||
453 | priv->adapter->enable11d = flag; | |
454 | ||
455 | /* send cmd to FW to enable/disable 11D function in FW */ | |
456 | ret = libertas_prepare_and_send_command(priv, | |
457 | cmd_802_11_snmp_mib, | |
458 | cmd_act_set, | |
459 | cmd_option_waitforrsp, | |
460 | OID_802_11D_ENABLE, | |
461 | &priv->adapter->enable11d); | |
462 | if (ret) | |
9012b28a | 463 | lbs_deb_11d("11D: Fail to enable 11D \n"); |
876c9d3a MT |
464 | |
465 | return 0; | |
466 | } | |
467 | ||
468 | /** | |
469 | * @brief This function sets DOMAIN INFO to FW | |
470 | * @param priv pointer to wlan_private | |
471 | * @return 0; -1 | |
472 | */ | |
473 | static int set_domain_info_11d(wlan_private * priv) | |
474 | { | |
475 | int ret; | |
476 | ||
477 | if (!priv->adapter->enable11d) { | |
9012b28a | 478 | lbs_deb_11d("11D: dnld domain Info with 11d disabled\n"); |
876c9d3a MT |
479 | return 0; |
480 | } | |
481 | ||
482 | ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info, | |
483 | cmd_act_set, | |
484 | cmd_option_waitforrsp, 0, NULL); | |
485 | if (ret) | |
9012b28a | 486 | lbs_deb_11d("11D: Fail to dnld domain Info\n"); |
876c9d3a MT |
487 | |
488 | return ret; | |
489 | } | |
490 | ||
491 | /** | |
492 | * @brief This function setups scan channels | |
493 | * @param priv pointer to wlan_private | |
494 | * @param band band | |
495 | * @return 0 | |
496 | */ | |
497 | int libertas_set_universaltable(wlan_private * priv, u8 band) | |
498 | { | |
499 | wlan_adapter *adapter = priv->adapter; | |
500 | u16 size = sizeof(struct chan_freq_power); | |
501 | u16 i = 0; | |
502 | ||
503 | memset(adapter->universal_channel, 0, | |
504 | sizeof(adapter->universal_channel)); | |
505 | ||
506 | adapter->universal_channel[i].nrcfp = | |
507 | sizeof(channel_freq_power_UN_BG) / size; | |
9012b28a | 508 | lbs_deb_11d("11D: BG-band nrcfp=%d\n", |
876c9d3a MT |
509 | adapter->universal_channel[i].nrcfp); |
510 | ||
511 | adapter->universal_channel[i].CFP = channel_freq_power_UN_BG; | |
512 | adapter->universal_channel[i].valid = 1; | |
513 | adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; | |
514 | adapter->universal_channel[i].band = band; | |
515 | i++; | |
516 | ||
517 | return 0; | |
518 | } | |
519 | ||
520 | /** | |
521 | * @brief This function implements command CMD_802_11D_DOMAIN_INFO | |
522 | * @param priv pointer to wlan_private | |
523 | * @param cmd pointer to cmd buffer | |
524 | * @param cmdno cmd ID | |
525 | * @param cmdOption cmd action | |
526 | * @return 0 | |
527 | */ | |
528 | int libertas_cmd_802_11d_domain_info(wlan_private * priv, | |
529 | struct cmd_ds_command *cmd, u16 cmdno, | |
530 | u16 cmdoption) | |
531 | { | |
532 | struct cmd_ds_802_11d_domain_info *pdomaininfo = | |
533 | &cmd->params.domaininfo; | |
534 | struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain; | |
535 | wlan_adapter *adapter = priv->adapter; | |
536 | u8 nr_subband = adapter->domainreg.nr_subband; | |
537 | ||
9012b28a | 538 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a | 539 | |
9012b28a | 540 | lbs_deb_11d("nr_subband=%x\n", nr_subband); |
876c9d3a MT |
541 | |
542 | cmd->command = cpu_to_le16(cmdno); | |
543 | pdomaininfo->action = cpu_to_le16(cmdoption); | |
544 | if (cmdoption == cmd_act_get) { | |
545 | cmd->size = | |
546 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | |
547 | lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd, | |
548 | (int)(cmd->size)); | |
9012b28a | 549 | goto done; |
876c9d3a MT |
550 | } |
551 | ||
552 | domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); | |
553 | memcpy(domain->countrycode, adapter->domainreg.countrycode, | |
554 | sizeof(domain->countrycode)); | |
555 | ||
556 | domain->header.len = | |
557 | cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) + | |
558 | sizeof(domain->countrycode)); | |
559 | ||
560 | if (nr_subband) { | |
561 | memcpy(domain->subband, adapter->domainreg.subband, | |
562 | nr_subband * sizeof(struct ieeetypes_subbandset)); | |
563 | ||
564 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | |
565 | domain->header.len + | |
566 | sizeof(struct mrvlietypesheader) + | |
567 | S_DS_GEN); | |
568 | } else { | |
569 | cmd->size = | |
570 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | |
571 | } | |
572 | ||
573 | lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size)); | |
574 | ||
9012b28a HS |
575 | done: |
576 | lbs_deb_enter(LBS_DEB_11D); | |
876c9d3a MT |
577 | return 0; |
578 | } | |
579 | ||
580 | /** | |
581 | * @brief This function implements private cmd: enable/disable 11D | |
582 | * @param priv pointer to wlan_private | |
583 | * @param wrq pointer to user data | |
584 | * @return 0 or -1 | |
585 | */ | |
586 | int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq) | |
587 | { | |
588 | int data = 0; | |
589 | int *val; | |
590 | ||
9012b28a | 591 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
592 | data = SUBCMD_DATA(wrq); |
593 | ||
9012b28a | 594 | lbs_deb_11d("enable 11D: %s\n", |
876c9d3a MT |
595 | (data == 1) ? "enable" : "Disable"); |
596 | ||
597 | wlan_enable_11d(priv, data); | |
598 | val = (int *)wrq->u.name; | |
599 | *val = priv->adapter->enable11d; | |
600 | ||
9012b28a | 601 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
602 | return 0; |
603 | } | |
604 | ||
605 | /** | |
606 | * @brief This function parses countryinfo from AP and download country info to FW | |
607 | * @param priv pointer to wlan_private | |
608 | * @param resp pointer to command response buffer | |
609 | * @return 0; -1 | |
610 | */ | |
611 | int libertas_ret_802_11d_domain_info(wlan_private * priv, | |
612 | struct cmd_ds_command *resp) | |
613 | { | |
614 | struct cmd_ds_802_11d_domain_info | |
615 | *domaininfo = &resp->params.domaininforesp; | |
616 | struct mrvlietypes_domainparamset *domain = &domaininfo->domain; | |
617 | u16 action = le16_to_cpu(domaininfo->action); | |
618 | s16 ret = 0; | |
619 | u8 nr_subband = 0; | |
620 | ||
9012b28a | 621 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
622 | |
623 | lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp, | |
624 | (int)le16_to_cpu(resp->size)); | |
625 | ||
626 | nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset); | |
627 | /* countrycode 3 bytes */ | |
628 | ||
9012b28a | 629 | lbs_deb_11d("11D Domain Info Resp: nr_subband=%d\n", nr_subband); |
876c9d3a MT |
630 | |
631 | if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { | |
9012b28a | 632 | lbs_deb_11d("Invalid Numrer of Subband returned!!\n"); |
876c9d3a MT |
633 | return -1; |
634 | } | |
635 | ||
636 | switch (action) { | |
637 | case cmd_act_set: /*Proc Set action */ | |
638 | break; | |
639 | ||
640 | case cmd_act_get: | |
641 | break; | |
642 | default: | |
9012b28a | 643 | lbs_deb_11d("Invalid action:%d\n", domaininfo->action); |
876c9d3a MT |
644 | ret = -1; |
645 | break; | |
646 | } | |
647 | ||
9012b28a | 648 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); |
876c9d3a MT |
649 | return ret; |
650 | } | |
651 | ||
652 | /** | |
653 | * @brief This function parses countryinfo from AP and download country info to FW | |
654 | * @param priv pointer to wlan_private | |
655 | * @return 0; -1 | |
656 | */ | |
657 | int libertas_parse_dnld_countryinfo_11d(wlan_private * priv) | |
658 | { | |
659 | int ret; | |
660 | wlan_adapter *adapter = priv->adapter; | |
661 | ||
9012b28a | 662 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
663 | if (priv->adapter->enable11d) { |
664 | memset(&adapter->parsed_region_chan, 0, | |
665 | sizeof(struct parsed_region_chan_11d)); | |
666 | ret = parse_domain_info_11d(&adapter->pattemptedbssdesc-> | |
667 | countryinfo, 0, | |
668 | &adapter->parsed_region_chan); | |
669 | ||
670 | if (ret == -1) { | |
9012b28a HS |
671 | lbs_deb_11d("11D: Err Parse domain_info from AP..\n"); |
672 | goto done; | |
876c9d3a MT |
673 | } |
674 | ||
675 | memset(&adapter->domainreg, 0, | |
676 | sizeof(struct wlan_802_11d_domain_reg)); | |
677 | generate_domain_info_11d(&adapter->parsed_region_chan, | |
678 | &adapter->domainreg); | |
679 | ||
680 | ret = set_domain_info_11d(priv); | |
681 | ||
682 | if (ret) { | |
9012b28a HS |
683 | lbs_deb_11d("11D: Err set domainInfo to FW\n"); |
684 | goto done; | |
876c9d3a MT |
685 | } |
686 | } | |
9012b28a HS |
687 | ret = 0; |
688 | ||
689 | done: | |
690 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | |
691 | return ret; | |
876c9d3a MT |
692 | } |
693 | ||
694 | /** | |
695 | * @brief This function generates 11D info from user specified regioncode and download to FW | |
696 | * @param priv pointer to wlan_private | |
697 | * @return 0; -1 | |
698 | */ | |
699 | int libertas_create_dnld_countryinfo_11d(wlan_private * priv) | |
700 | { | |
701 | int ret; | |
702 | wlan_adapter *adapter = priv->adapter; | |
703 | struct region_channel *region_chan; | |
704 | u8 j; | |
705 | ||
9012b28a HS |
706 | lbs_deb_enter(LBS_DEB_11D); |
707 | lbs_deb_11d("11D:curbssparams.band[%d]\n", adapter->curbssparams.band); | |
876c9d3a MT |
708 | |
709 | if (priv->adapter->enable11d) { | |
710 | /* update parsed_region_chan_11; dnld domaininf to FW */ | |
711 | ||
712 | for (j = 0; j < sizeof(adapter->region_channel) / | |
713 | sizeof(adapter->region_channel[0]); j++) { | |
714 | region_chan = &adapter->region_channel[j]; | |
715 | ||
9012b28a | 716 | lbs_deb_11d("11D:[%d] region_chan->band[%d]\n", j, |
876c9d3a MT |
717 | region_chan->band); |
718 | ||
719 | if (!region_chan || !region_chan->valid | |
720 | || !region_chan->CFP) | |
721 | continue; | |
722 | if (region_chan->band != adapter->curbssparams.band) | |
723 | continue; | |
724 | break; | |
725 | } | |
726 | ||
727 | if (j >= sizeof(adapter->region_channel) / | |
728 | sizeof(adapter->region_channel[0])) { | |
9012b28a | 729 | lbs_deb_11d("11D:region_chan not found. band[%d]\n", |
876c9d3a | 730 | adapter->curbssparams.band); |
9012b28a HS |
731 | ret = -1; |
732 | goto done; | |
876c9d3a MT |
733 | } |
734 | ||
735 | memset(&adapter->parsed_region_chan, 0, | |
736 | sizeof(struct parsed_region_chan_11d)); | |
737 | wlan_generate_parsed_region_chan_11d(region_chan, | |
738 | &adapter-> | |
739 | parsed_region_chan); | |
740 | ||
741 | memset(&adapter->domainreg, 0, | |
742 | sizeof(struct wlan_802_11d_domain_reg)); | |
743 | generate_domain_info_11d(&adapter->parsed_region_chan, | |
744 | &adapter->domainreg); | |
745 | ||
746 | ret = set_domain_info_11d(priv); | |
747 | ||
748 | if (ret) { | |
9012b28a HS |
749 | lbs_deb_11d("11D: Err set domainInfo to FW\n"); |
750 | goto done; | |
876c9d3a MT |
751 | } |
752 | ||
753 | } | |
9012b28a | 754 | ret = 0; |
876c9d3a | 755 | |
9012b28a HS |
756 | done: |
757 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | |
758 | return ret; | |
876c9d3a | 759 | } |