diff options
Diffstat (limited to 'drivers/net/wireless/libertas/11d.c')
-rw-r--r-- | drivers/net/wireless/libertas/11d.c | 697 |
1 files changed, 0 insertions, 697 deletions
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c deleted file mode 100644 index 93f2d5fb3920..000000000000 --- a/drivers/net/wireless/libertas/11d.c +++ /dev/null | |||
@@ -1,697 +0,0 @@ | |||
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 "cmd.h" | ||
10 | #include "decl.h" | ||
11 | #include "11d.h" | ||
12 | #include "dev.h" | ||
13 | #include "wext.h" | ||
14 | |||
15 | #define TX_PWR_DEFAULT 10 | ||
16 | |||
17 | static struct region_code_mapping region_code_mapping[] = { | ||
18 | {"US ", 0x10}, /* US FCC */ | ||
19 | {"CA ", 0x10}, /* IC Canada */ | ||
20 | {"SG ", 0x10}, /* Singapore */ | ||
21 | {"EU ", 0x30}, /* ETSI */ | ||
22 | {"AU ", 0x30}, /* Australia */ | ||
23 | {"KR ", 0x30}, /* Republic Of Korea */ | ||
24 | {"ES ", 0x31}, /* Spain */ | ||
25 | {"FR ", 0x32}, /* France */ | ||
26 | {"JP ", 0x40}, /* Japan */ | ||
27 | }; | ||
28 | |||
29 | /* Following 2 structure defines the supported channels */ | ||
30 | static struct chan_freq_power channel_freq_power_UN_BG[] = { | ||
31 | {1, 2412, TX_PWR_DEFAULT}, | ||
32 | {2, 2417, TX_PWR_DEFAULT}, | ||
33 | {3, 2422, TX_PWR_DEFAULT}, | ||
34 | {4, 2427, TX_PWR_DEFAULT}, | ||
35 | {5, 2432, TX_PWR_DEFAULT}, | ||
36 | {6, 2437, TX_PWR_DEFAULT}, | ||
37 | {7, 2442, TX_PWR_DEFAULT}, | ||
38 | {8, 2447, TX_PWR_DEFAULT}, | ||
39 | {9, 2452, TX_PWR_DEFAULT}, | ||
40 | {10, 2457, TX_PWR_DEFAULT}, | ||
41 | {11, 2462, TX_PWR_DEFAULT}, | ||
42 | {12, 2467, TX_PWR_DEFAULT}, | ||
43 | {13, 2472, TX_PWR_DEFAULT}, | ||
44 | {14, 2484, TX_PWR_DEFAULT} | ||
45 | }; | ||
46 | |||
47 | static u8 lbs_region_2_code(u8 *region) | ||
48 | { | ||
49 | u8 i; | ||
50 | |||
51 | for (i = 0; i < COUNTRY_CODE_LEN && region[i]; i++) | ||
52 | region[i] = toupper(region[i]); | ||
53 | |||
54 | for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { | ||
55 | if (!memcmp(region, region_code_mapping[i].region, | ||
56 | COUNTRY_CODE_LEN)) | ||
57 | return (region_code_mapping[i].code); | ||
58 | } | ||
59 | |||
60 | /* default is US */ | ||
61 | return (region_code_mapping[0].code); | ||
62 | } | ||
63 | |||
64 | static u8 *lbs_code_2_region(u8 code) | ||
65 | { | ||
66 | u8 i; | ||
67 | |||
68 | for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { | ||
69 | if (region_code_mapping[i].code == code) | ||
70 | return (region_code_mapping[i].region); | ||
71 | } | ||
72 | /* default is US */ | ||
73 | return (region_code_mapping[0].region); | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * @brief This function finds the nrchan-th chan after the firstchan | ||
78 | * @param band band | ||
79 | * @param firstchan first channel number | ||
80 | * @param nrchan number of channels | ||
81 | * @return the nrchan-th chan number | ||
82 | */ | ||
83 | static u8 lbs_get_chan_11d(u8 firstchan, u8 nrchan, u8 *chan) | ||
84 | /*find the nrchan-th chan after the firstchan*/ | ||
85 | { | ||
86 | u8 i; | ||
87 | struct chan_freq_power *cfp; | ||
88 | u8 cfp_no; | ||
89 | |||
90 | cfp = channel_freq_power_UN_BG; | ||
91 | cfp_no = ARRAY_SIZE(channel_freq_power_UN_BG); | ||
92 | |||
93 | for (i = 0; i < cfp_no; i++) { | ||
94 | if ((cfp + i)->channel == firstchan) { | ||
95 | lbs_deb_11d("firstchan found\n"); | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (i < cfp_no) { | ||
101 | /*if beyond the boundary */ | ||
102 | if (i + nrchan < cfp_no) { | ||
103 | *chan = (cfp + i + nrchan)->channel; | ||
104 | return 1; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * @brief This function Checks if chan txpwr is learned from AP/IBSS | ||
113 | * @param chan chan number | ||
114 | * @param parsed_region_chan pointer to parsed_region_chan_11d | ||
115 | * @return TRUE; FALSE | ||
116 | */ | ||
117 | static u8 lbs_channel_known_11d(u8 chan, | ||
118 | struct parsed_region_chan_11d * parsed_region_chan) | ||
119 | { | ||
120 | struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; | ||
121 | u8 nr_chan = parsed_region_chan->nr_chan; | ||
122 | u8 i = 0; | ||
123 | |||
124 | lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)chanpwr, | ||
125 | sizeof(struct chan_power_11d) * nr_chan); | ||
126 | |||
127 | for (i = 0; i < nr_chan; i++) { | ||
128 | if (chan == chanpwr[i].chan) { | ||
129 | lbs_deb_11d("found chan %d\n", chan); | ||
130 | return 1; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | lbs_deb_11d("chan %d not found\n", chan); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | u32 lbs_chan_2_freq(u8 chan) | ||
139 | { | ||
140 | struct chan_freq_power *cf; | ||
141 | u16 i; | ||
142 | u32 freq = 0; | ||
143 | |||
144 | cf = channel_freq_power_UN_BG; | ||
145 | |||
146 | for (i = 0; i < ARRAY_SIZE(channel_freq_power_UN_BG); i++) { | ||
147 | if (chan == cf[i].channel) | ||
148 | freq = cf[i].freq; | ||
149 | } | ||
150 | |||
151 | return freq; | ||
152 | } | ||
153 | |||
154 | static int generate_domain_info_11d(struct parsed_region_chan_11d | ||
155 | *parsed_region_chan, | ||
156 | struct lbs_802_11d_domain_reg *domaininfo) | ||
157 | { | ||
158 | u8 nr_subband = 0; | ||
159 | |||
160 | u8 nr_chan = parsed_region_chan->nr_chan; | ||
161 | u8 nr_parsedchan = 0; | ||
162 | |||
163 | u8 firstchan = 0, nextchan = 0, maxpwr = 0; | ||
164 | |||
165 | u8 i, flag = 0; | ||
166 | |||
167 | memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, | ||
168 | COUNTRY_CODE_LEN); | ||
169 | |||
170 | lbs_deb_11d("nrchan %d\n", nr_chan); | ||
171 | lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)parsed_region_chan, | ||
172 | sizeof(struct parsed_region_chan_11d)); | ||
173 | |||
174 | for (i = 0; i < nr_chan; i++) { | ||
175 | if (!flag) { | ||
176 | flag = 1; | ||
177 | nextchan = firstchan = | ||
178 | parsed_region_chan->chanpwr[i].chan; | ||
179 | maxpwr = parsed_region_chan->chanpwr[i].pwr; | ||
180 | nr_parsedchan = 1; | ||
181 | continue; | ||
182 | } | ||
183 | |||
184 | if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && | ||
185 | parsed_region_chan->chanpwr[i].pwr == maxpwr) { | ||
186 | nextchan++; | ||
187 | nr_parsedchan++; | ||
188 | } else { | ||
189 | domaininfo->subband[nr_subband].firstchan = firstchan; | ||
190 | domaininfo->subband[nr_subband].nrchan = | ||
191 | nr_parsedchan; | ||
192 | domaininfo->subband[nr_subband].maxtxpwr = maxpwr; | ||
193 | nr_subband++; | ||
194 | nextchan = firstchan = | ||
195 | parsed_region_chan->chanpwr[i].chan; | ||
196 | maxpwr = parsed_region_chan->chanpwr[i].pwr; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | if (flag) { | ||
201 | domaininfo->subband[nr_subband].firstchan = firstchan; | ||
202 | domaininfo->subband[nr_subband].nrchan = nr_parsedchan; | ||
203 | domaininfo->subband[nr_subband].maxtxpwr = maxpwr; | ||
204 | nr_subband++; | ||
205 | } | ||
206 | domaininfo->nr_subband = nr_subband; | ||
207 | |||
208 | lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband); | ||
209 | lbs_deb_hex(LBS_DEB_11D, "domaininfo", (char *)domaininfo, | ||
210 | COUNTRY_CODE_LEN + 1 + | ||
211 | sizeof(struct ieee_subbandset) * nr_subband); | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS | ||
217 | * @param region_chan pointer to struct region_channel | ||
218 | * @param *parsed_region_chan pointer to parsed_region_chan_11d | ||
219 | * @return N/A | ||
220 | */ | ||
221 | static void lbs_generate_parsed_region_chan_11d(struct region_channel *region_chan, | ||
222 | struct parsed_region_chan_11d * | ||
223 | parsed_region_chan) | ||
224 | { | ||
225 | u8 i; | ||
226 | struct chan_freq_power *cfp; | ||
227 | |||
228 | if (region_chan == NULL) { | ||
229 | lbs_deb_11d("region_chan is NULL\n"); | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | cfp = region_chan->CFP; | ||
234 | if (cfp == NULL) { | ||
235 | lbs_deb_11d("cfp is NULL \n"); | ||
236 | return; | ||
237 | } | ||
238 | |||
239 | parsed_region_chan->band = region_chan->band; | ||
240 | parsed_region_chan->region = region_chan->region; | ||
241 | memcpy(parsed_region_chan->countrycode, | ||
242 | lbs_code_2_region(region_chan->region), COUNTRY_CODE_LEN); | ||
243 | |||
244 | lbs_deb_11d("region 0x%x, band %d\n", parsed_region_chan->region, | ||
245 | parsed_region_chan->band); | ||
246 | |||
247 | for (i = 0; i < region_chan->nrcfp; i++, cfp++) { | ||
248 | parsed_region_chan->chanpwr[i].chan = cfp->channel; | ||
249 | parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; | ||
250 | lbs_deb_11d("chan %d, pwr %d\n", | ||
251 | parsed_region_chan->chanpwr[i].chan, | ||
252 | parsed_region_chan->chanpwr[i].pwr); | ||
253 | } | ||
254 | parsed_region_chan->nr_chan = region_chan->nrcfp; | ||
255 | |||
256 | lbs_deb_11d("nrchan %d\n", parsed_region_chan->nr_chan); | ||
257 | |||
258 | return; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS | ||
263 | * @param region region ID | ||
264 | * @param band band | ||
265 | * @param chan chan | ||
266 | * @return TRUE;FALSE | ||
267 | */ | ||
268 | static u8 lbs_region_chan_supported_11d(u8 region, u8 chan) | ||
269 | { | ||
270 | struct chan_freq_power *cfp; | ||
271 | int cfp_no; | ||
272 | u8 idx; | ||
273 | int ret = 0; | ||
274 | |||
275 | lbs_deb_enter(LBS_DEB_11D); | ||
276 | |||
277 | cfp = lbs_get_region_cfp_table(region, &cfp_no); | ||
278 | if (cfp == NULL) | ||
279 | return 0; | ||
280 | |||
281 | for (idx = 0; idx < cfp_no; idx++) { | ||
282 | if (chan == (cfp + idx)->channel) { | ||
283 | /* If Mrvl Chip Supported? */ | ||
284 | if ((cfp + idx)->unsupported) { | ||
285 | ret = 0; | ||
286 | } else { | ||
287 | ret = 1; | ||
288 | } | ||
289 | goto done; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /*chan is not in the region table */ | ||
294 | |||
295 | done: | ||
296 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * @brief This function checks if chan txpwr is learned from AP/IBSS | ||
302 | * @param chan chan number | ||
303 | * @param parsed_region_chan pointer to parsed_region_chan_11d | ||
304 | * @return 0 | ||
305 | */ | ||
306 | static int parse_domain_info_11d(struct ieee_ie_country_info_full_set *countryinfo, | ||
307 | u8 band, | ||
308 | struct parsed_region_chan_11d *parsed_region_chan) | ||
309 | { | ||
310 | u8 nr_subband, nrchan; | ||
311 | u8 lastchan, firstchan; | ||
312 | u8 region; | ||
313 | u8 curchan = 0; | ||
314 | |||
315 | u8 idx = 0; /*chan index in parsed_region_chan */ | ||
316 | |||
317 | u8 j, i; | ||
318 | |||
319 | lbs_deb_enter(LBS_DEB_11D); | ||
320 | |||
321 | /*validation Rules: | ||
322 | 1. valid region Code | ||
323 | 2. First Chan increment | ||
324 | 3. channel range no overlap | ||
325 | 4. channel is valid? | ||
326 | 5. channel is supported by region? | ||
327 | 6. Others | ||
328 | */ | ||
329 | |||
330 | lbs_deb_hex(LBS_DEB_11D, "countryinfo", (u8 *) countryinfo, 30); | ||
331 | |||
332 | if ((*(countryinfo->countrycode)) == 0 | ||
333 | || (countryinfo->header.len <= COUNTRY_CODE_LEN)) { | ||
334 | /* No region Info or Wrong region info: treat as No 11D info */ | ||
335 | goto done; | ||
336 | } | ||
337 | |||
338 | /*Step1: check region_code */ | ||
339 | parsed_region_chan->region = region = | ||
340 | lbs_region_2_code(countryinfo->countrycode); | ||
341 | |||
342 | lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region); | ||
343 | lbs_deb_hex(LBS_DEB_11D, "countrycode", (char *)countryinfo->countrycode, | ||
344 | COUNTRY_CODE_LEN); | ||
345 | |||
346 | parsed_region_chan->band = band; | ||
347 | |||
348 | memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, | ||
349 | COUNTRY_CODE_LEN); | ||
350 | |||
351 | nr_subband = (countryinfo->header.len - COUNTRY_CODE_LEN) / | ||
352 | sizeof(struct ieee_subbandset); | ||
353 | |||
354 | for (j = 0, lastchan = 0; j < nr_subband; j++) { | ||
355 | |||
356 | if (countryinfo->subband[j].firstchan <= lastchan) { | ||
357 | /*Step2&3. Check First Chan Num increment and no overlap */ | ||
358 | lbs_deb_11d("chan %d>%d, overlap\n", | ||
359 | countryinfo->subband[j].firstchan, lastchan); | ||
360 | continue; | ||
361 | } | ||
362 | |||
363 | firstchan = countryinfo->subband[j].firstchan; | ||
364 | nrchan = countryinfo->subband[j].nrchan; | ||
365 | |||
366 | for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { | ||
367 | /*step4: channel is supported? */ | ||
368 | |||
369 | if (!lbs_get_chan_11d(firstchan, i, &curchan)) { | ||
370 | /* Chan is not found in UN table */ | ||
371 | lbs_deb_11d("chan is not supported: %d \n", i); | ||
372 | break; | ||
373 | } | ||
374 | |||
375 | lastchan = curchan; | ||
376 | |||
377 | if (lbs_region_chan_supported_11d(region, curchan)) { | ||
378 | /*step5: Check if curchan is supported by mrvl in region */ | ||
379 | parsed_region_chan->chanpwr[idx].chan = curchan; | ||
380 | parsed_region_chan->chanpwr[idx].pwr = | ||
381 | countryinfo->subband[j].maxtxpwr; | ||
382 | idx++; | ||
383 | } else { | ||
384 | /*not supported and ignore the chan */ | ||
385 | lbs_deb_11d( | ||
386 | "i %d, chan %d unsupported in region %x, band %d\n", | ||
387 | i, curchan, region, band); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | /*Step6: Add other checking if any */ | ||
392 | |||
393 | } | ||
394 | |||
395 | parsed_region_chan->nr_chan = idx; | ||
396 | |||
397 | lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan); | ||
398 | lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan, | ||
399 | 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); | ||
400 | |||
401 | done: | ||
402 | lbs_deb_enter(LBS_DEB_11D); | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | /** | ||
407 | * @brief This function calculates the scan type for channels | ||
408 | * @param chan chan number | ||
409 | * @param parsed_region_chan pointer to parsed_region_chan_11d | ||
410 | * @return PASSIVE if chan is unknown; ACTIVE if chan is known | ||
411 | */ | ||
412 | u8 lbs_get_scan_type_11d(u8 chan, | ||
413 | struct parsed_region_chan_11d * parsed_region_chan) | ||
414 | { | ||
415 | u8 scan_type = CMD_SCAN_TYPE_PASSIVE; | ||
416 | |||
417 | lbs_deb_enter(LBS_DEB_11D); | ||
418 | |||
419 | if (lbs_channel_known_11d(chan, parsed_region_chan)) { | ||
420 | lbs_deb_11d("found, do active scan\n"); | ||
421 | scan_type = CMD_SCAN_TYPE_ACTIVE; | ||
422 | } else { | ||
423 | lbs_deb_11d("not found, do passive scan\n"); | ||
424 | } | ||
425 | |||
426 | lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type); | ||
427 | return scan_type; | ||
428 | |||
429 | } | ||
430 | |||
431 | void lbs_init_11d(struct lbs_private *priv) | ||
432 | { | ||
433 | priv->enable11d = 0; | ||
434 | memset(&(priv->parsed_region_chan), 0, | ||
435 | sizeof(struct parsed_region_chan_11d)); | ||
436 | return; | ||
437 | } | ||
438 | |||
439 | /** | ||
440 | * @brief This function sets DOMAIN INFO to FW | ||
441 | * @param priv pointer to struct lbs_private | ||
442 | * @return 0; -1 | ||
443 | */ | ||
444 | static int set_domain_info_11d(struct lbs_private *priv) | ||
445 | { | ||
446 | int ret; | ||
447 | |||
448 | if (!priv->enable11d) { | ||
449 | lbs_deb_11d("dnld domain Info with 11d disabled\n"); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO, | ||
454 | CMD_ACT_SET, | ||
455 | CMD_OPTION_WAITFORRSP, 0, NULL); | ||
456 | if (ret) | ||
457 | lbs_deb_11d("fail to dnld domain info\n"); | ||
458 | |||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | /** | ||
463 | * @brief This function setups scan channels | ||
464 | * @param priv pointer to struct lbs_private | ||
465 | * @param band band | ||
466 | * @return 0 | ||
467 | */ | ||
468 | int lbs_set_universaltable(struct lbs_private *priv, u8 band) | ||
469 | { | ||
470 | u16 size = sizeof(struct chan_freq_power); | ||
471 | u16 i = 0; | ||
472 | |||
473 | memset(priv->universal_channel, 0, | ||
474 | sizeof(priv->universal_channel)); | ||
475 | |||
476 | priv->universal_channel[i].nrcfp = | ||
477 | sizeof(channel_freq_power_UN_BG) / size; | ||
478 | lbs_deb_11d("BG-band nrcfp %d\n", | ||
479 | priv->universal_channel[i].nrcfp); | ||
480 | |||
481 | priv->universal_channel[i].CFP = channel_freq_power_UN_BG; | ||
482 | priv->universal_channel[i].valid = 1; | ||
483 | priv->universal_channel[i].region = UNIVERSAL_REGION_CODE; | ||
484 | priv->universal_channel[i].band = band; | ||
485 | i++; | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * @brief This function implements command CMD_802_11D_DOMAIN_INFO | ||
492 | * @param priv pointer to struct lbs_private | ||
493 | * @param cmd pointer to cmd buffer | ||
494 | * @param cmdno cmd ID | ||
495 | * @param cmdOption cmd action | ||
496 | * @return 0 | ||
497 | */ | ||
498 | int lbs_cmd_802_11d_domain_info(struct lbs_private *priv, | ||
499 | struct cmd_ds_command *cmd, u16 cmdno, | ||
500 | u16 cmdoption) | ||
501 | { | ||
502 | struct cmd_ds_802_11d_domain_info *pdomaininfo = | ||
503 | &cmd->params.domaininfo; | ||
504 | struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain; | ||
505 | u8 nr_subband = priv->domainreg.nr_subband; | ||
506 | |||
507 | lbs_deb_enter(LBS_DEB_11D); | ||
508 | |||
509 | lbs_deb_11d("nr_subband=%x\n", nr_subband); | ||
510 | |||
511 | cmd->command = cpu_to_le16(cmdno); | ||
512 | pdomaininfo->action = cpu_to_le16(cmdoption); | ||
513 | if (cmdoption == CMD_ACT_GET) { | ||
514 | cmd->size = | ||
515 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | ||
516 | lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, | ||
517 | le16_to_cpu(cmd->size)); | ||
518 | goto done; | ||
519 | } | ||
520 | |||
521 | domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); | ||
522 | memcpy(domain->countrycode, priv->domainreg.countrycode, | ||
523 | sizeof(domain->countrycode)); | ||
524 | |||
525 | domain->header.len = | ||
526 | cpu_to_le16(nr_subband * sizeof(struct ieee_subbandset) + | ||
527 | sizeof(domain->countrycode)); | ||
528 | |||
529 | if (nr_subband) { | ||
530 | memcpy(domain->subband, priv->domainreg.subband, | ||
531 | nr_subband * sizeof(struct ieee_subbandset)); | ||
532 | |||
533 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | ||
534 | le16_to_cpu(domain->header.len) + | ||
535 | sizeof(struct mrvl_ie_header) + | ||
536 | S_DS_GEN); | ||
537 | } else { | ||
538 | cmd->size = | ||
539 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | ||
540 | } | ||
541 | |||
542 | lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size)); | ||
543 | |||
544 | done: | ||
545 | lbs_deb_enter(LBS_DEB_11D); | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * @brief This function parses countryinfo from AP and download country info to FW | ||
551 | * @param priv pointer to struct lbs_private | ||
552 | * @param resp pointer to command response buffer | ||
553 | * @return 0; -1 | ||
554 | */ | ||
555 | int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp) | ||
556 | { | ||
557 | struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp; | ||
558 | struct mrvl_ie_domain_param_set *domain = &domaininfo->domain; | ||
559 | u16 action = le16_to_cpu(domaininfo->action); | ||
560 | s16 ret = 0; | ||
561 | u8 nr_subband = 0; | ||
562 | |||
563 | lbs_deb_enter(LBS_DEB_11D); | ||
564 | |||
565 | lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp, | ||
566 | (int)le16_to_cpu(resp->size)); | ||
567 | |||
568 | nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) / | ||
569 | sizeof(struct ieee_subbandset); | ||
570 | |||
571 | lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband); | ||
572 | |||
573 | if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { | ||
574 | lbs_deb_11d("Invalid Numrer of Subband returned!!\n"); | ||
575 | return -1; | ||
576 | } | ||
577 | |||
578 | switch (action) { | ||
579 | case CMD_ACT_SET: /*Proc Set action */ | ||
580 | break; | ||
581 | |||
582 | case CMD_ACT_GET: | ||
583 | break; | ||
584 | default: | ||
585 | lbs_deb_11d("Invalid action:%d\n", domaininfo->action); | ||
586 | ret = -1; | ||
587 | break; | ||
588 | } | ||
589 | |||
590 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | ||
591 | return ret; | ||
592 | } | ||
593 | |||
594 | /** | ||
595 | * @brief This function parses countryinfo from AP and download country info to FW | ||
596 | * @param priv pointer to struct lbs_private | ||
597 | * @return 0; -1 | ||
598 | */ | ||
599 | int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv, | ||
600 | struct bss_descriptor * bss) | ||
601 | { | ||
602 | int ret; | ||
603 | |||
604 | lbs_deb_enter(LBS_DEB_11D); | ||
605 | if (priv->enable11d) { | ||
606 | memset(&priv->parsed_region_chan, 0, | ||
607 | sizeof(struct parsed_region_chan_11d)); | ||
608 | ret = parse_domain_info_11d(&bss->countryinfo, 0, | ||
609 | &priv->parsed_region_chan); | ||
610 | |||
611 | if (ret == -1) { | ||
612 | lbs_deb_11d("error parsing domain_info from AP\n"); | ||
613 | goto done; | ||
614 | } | ||
615 | |||
616 | memset(&priv->domainreg, 0, | ||
617 | sizeof(struct lbs_802_11d_domain_reg)); | ||
618 | generate_domain_info_11d(&priv->parsed_region_chan, | ||
619 | &priv->domainreg); | ||
620 | |||
621 | ret = set_domain_info_11d(priv); | ||
622 | |||
623 | if (ret) { | ||
624 | lbs_deb_11d("error setting domain info\n"); | ||
625 | goto done; | ||
626 | } | ||
627 | } | ||
628 | ret = 0; | ||
629 | |||
630 | done: | ||
631 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | ||
632 | return ret; | ||
633 | } | ||
634 | |||
635 | /** | ||
636 | * @brief This function generates 11D info from user specified regioncode and download to FW | ||
637 | * @param priv pointer to struct lbs_private | ||
638 | * @return 0; -1 | ||
639 | */ | ||
640 | int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv) | ||
641 | { | ||
642 | int ret; | ||
643 | struct region_channel *region_chan; | ||
644 | u8 j; | ||
645 | |||
646 | lbs_deb_enter(LBS_DEB_11D); | ||
647 | lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band); | ||
648 | |||
649 | if (priv->enable11d) { | ||
650 | /* update parsed_region_chan_11; dnld domaininf to FW */ | ||
651 | |||
652 | for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) { | ||
653 | region_chan = &priv->region_channel[j]; | ||
654 | |||
655 | lbs_deb_11d("%d region_chan->band %d\n", j, | ||
656 | region_chan->band); | ||
657 | |||
658 | if (!region_chan || !region_chan->valid | ||
659 | || !region_chan->CFP) | ||
660 | continue; | ||
661 | if (region_chan->band != priv->curbssparams.band) | ||
662 | continue; | ||
663 | break; | ||
664 | } | ||
665 | |||
666 | if (j >= ARRAY_SIZE(priv->region_channel)) { | ||
667 | lbs_deb_11d("region_chan not found, band %d\n", | ||
668 | priv->curbssparams.band); | ||
669 | ret = -1; | ||
670 | goto done; | ||
671 | } | ||
672 | |||
673 | memset(&priv->parsed_region_chan, 0, | ||
674 | sizeof(struct parsed_region_chan_11d)); | ||
675 | lbs_generate_parsed_region_chan_11d(region_chan, | ||
676 | &priv-> | ||
677 | parsed_region_chan); | ||
678 | |||
679 | memset(&priv->domainreg, 0, | ||
680 | sizeof(struct lbs_802_11d_domain_reg)); | ||
681 | generate_domain_info_11d(&priv->parsed_region_chan, | ||
682 | &priv->domainreg); | ||
683 | |||
684 | ret = set_domain_info_11d(priv); | ||
685 | |||
686 | if (ret) { | ||
687 | lbs_deb_11d("error setting domain info\n"); | ||
688 | goto done; | ||
689 | } | ||
690 | |||
691 | } | ||
692 | ret = 0; | ||
693 | |||
694 | done: | ||
695 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | ||
696 | return ret; | ||
697 | } | ||