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" | |
bca61f8a | 9 | #include "cmd.h" |
876c9d3a MT |
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 | ||
10078321 | 47 | static u8 lbs_region_2_code(u8 *region) |
876c9d3a MT |
48 | { |
49 | u8 i; | |
876c9d3a | 50 | |
b929c633 | 51 | for (i = 0; i < COUNTRY_CODE_LEN && region[i]; i++) |
876c9d3a MT |
52 | region[i] = toupper(region[i]); |
53 | ||
c00acf46 | 54 | for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { |
876c9d3a MT |
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 | ||
10078321 | 64 | static u8 *lbs_code_2_region(u8 code) |
876c9d3a MT |
65 | { |
66 | u8 i; | |
c00acf46 AMR |
67 | |
68 | for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { | |
876c9d3a MT |
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 | */ | |
e98a88dd | 83 | static u8 lbs_get_chan_11d(u8 firstchan, u8 nrchan, u8 *chan) |
876c9d3a MT |
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; | |
c00acf46 | 91 | cfp_no = ARRAY_SIZE(channel_freq_power_UN_BG); |
876c9d3a MT |
92 | |
93 | for (i = 0; i < cfp_no; i++) { | |
94 | if ((cfp + i)->channel == firstchan) { | |
9012b28a | 95 | lbs_deb_11d("firstchan found\n"); |
876c9d3a MT |
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 | */ | |
10078321 | 117 | static u8 lbs_channel_known_11d(u8 chan, |
876c9d3a MT |
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 | ||
ece56191 | 124 | lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)chanpwr, |
876c9d3a MT |
125 | sizeof(struct chan_power_11d) * nr_chan); |
126 | ||
127 | for (i = 0; i < nr_chan; i++) { | |
128 | if (chan == chanpwr[i].chan) { | |
ece56191 | 129 | lbs_deb_11d("found chan %d\n", chan); |
876c9d3a MT |
130 | return 1; |
131 | } | |
132 | } | |
133 | ||
ece56191 | 134 | lbs_deb_11d("chan %d not found\n", chan); |
876c9d3a MT |
135 | return 0; |
136 | } | |
137 | ||
e98a88dd | 138 | u32 lbs_chan_2_freq(u8 chan) |
876c9d3a MT |
139 | { |
140 | struct chan_freq_power *cf; | |
876c9d3a MT |
141 | u16 i; |
142 | u32 freq = 0; | |
143 | ||
144 | cf = channel_freq_power_UN_BG; | |
876c9d3a | 145 | |
c00acf46 | 146 | for (i = 0; i < ARRAY_SIZE(channel_freq_power_UN_BG); i++) { |
876c9d3a MT |
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, | |
10078321 | 156 | struct lbs_802_11d_domain_reg *domaininfo) |
876c9d3a MT |
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 | ||
ece56191 HS |
170 | lbs_deb_11d("nrchan %d\n", nr_chan); |
171 | lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)parsed_region_chan, | |
876c9d3a MT |
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 | ||
9012b28a | 208 | lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband); |
ece56191 | 209 | lbs_deb_hex(LBS_DEB_11D, "domaininfo", (char *)domaininfo, |
876c9d3a | 210 | COUNTRY_CODE_LEN + 1 + |
75b6a61a | 211 | sizeof(struct ieee_subbandset) * nr_subband); |
876c9d3a MT |
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 | */ | |
10078321 | 221 | static void lbs_generate_parsed_region_chan_11d(struct region_channel *region_chan, |
876c9d3a MT |
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) { | |
ece56191 | 229 | lbs_deb_11d("region_chan is NULL\n"); |
876c9d3a MT |
230 | return; |
231 | } | |
232 | ||
233 | cfp = region_chan->CFP; | |
234 | if (cfp == NULL) { | |
ece56191 | 235 | lbs_deb_11d("cfp is NULL \n"); |
876c9d3a MT |
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, | |
10078321 | 242 | lbs_code_2_region(region_chan->region), COUNTRY_CODE_LEN); |
876c9d3a | 243 | |
ece56191 | 244 | lbs_deb_11d("region 0x%x, band %d\n", parsed_region_chan->region, |
876c9d3a MT |
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; | |
ece56191 | 250 | lbs_deb_11d("chan %d, pwr %d\n", |
876c9d3a MT |
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 | ||
ece56191 | 256 | lbs_deb_11d("nrchan %d\n", parsed_region_chan->nr_chan); |
876c9d3a MT |
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 | */ | |
e98a88dd | 268 | static u8 lbs_region_chan_supported_11d(u8 region, u8 chan) |
876c9d3a MT |
269 | { |
270 | struct chan_freq_power *cfp; | |
271 | int cfp_no; | |
272 | u8 idx; | |
9012b28a | 273 | int ret = 0; |
876c9d3a | 274 | |
9012b28a | 275 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a | 276 | |
e98a88dd | 277 | cfp = lbs_get_region_cfp_table(region, &cfp_no); |
876c9d3a MT |
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) { | |
9012b28a | 285 | ret = 0; |
876c9d3a | 286 | } else { |
9012b28a | 287 | ret = 1; |
876c9d3a | 288 | } |
9012b28a | 289 | goto done; |
876c9d3a MT |
290 | } |
291 | } | |
292 | ||
293 | /*chan is not in the region table */ | |
9012b28a HS |
294 | |
295 | done: | |
296 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | |
297 | return ret; | |
876c9d3a MT |
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 | */ | |
75b6a61a | 306 | static int parse_domain_info_11d(struct ieee_ie_country_info_full_set *countryinfo, |
876c9d3a | 307 | u8 band, |
75b6a61a | 308 | struct parsed_region_chan_11d *parsed_region_chan) |
876c9d3a MT |
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 | ||
9012b28a | 319 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a MT |
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 | ||
ece56191 | 330 | lbs_deb_hex(LBS_DEB_11D, "countryinfo", (u8 *) countryinfo, 30); |
876c9d3a MT |
331 | |
332 | if ((*(countryinfo->countrycode)) == 0 | |
75b6a61a | 333 | || (countryinfo->header.len <= COUNTRY_CODE_LEN)) { |
876c9d3a | 334 | /* No region Info or Wrong region info: treat as No 11D info */ |
9012b28a | 335 | goto done; |
876c9d3a MT |
336 | } |
337 | ||
338 | /*Step1: check region_code */ | |
339 | parsed_region_chan->region = region = | |
10078321 | 340 | lbs_region_2_code(countryinfo->countrycode); |
876c9d3a | 341 | |
9012b28a | 342 | lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region); |
ece56191 | 343 | lbs_deb_hex(LBS_DEB_11D, "countrycode", (char *)countryinfo->countrycode, |
876c9d3a MT |
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 | ||
75b6a61a DW |
351 | nr_subband = (countryinfo->header.len - COUNTRY_CODE_LEN) / |
352 | sizeof(struct ieee_subbandset); | |
876c9d3a MT |
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 */ | |
ece56191 | 358 | lbs_deb_11d("chan %d>%d, overlap\n", |
876c9d3a MT |
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 | ||
e98a88dd | 369 | if (!lbs_get_chan_11d(firstchan, i, &curchan)) { |
876c9d3a | 370 | /* Chan is not found in UN table */ |
9012b28a | 371 | lbs_deb_11d("chan is not supported: %d \n", i); |
876c9d3a MT |
372 | break; |
373 | } | |
374 | ||
375 | lastchan = curchan; | |
376 | ||
e98a88dd | 377 | if (lbs_region_chan_supported_11d(region, curchan)) { |
876c9d3a MT |
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 */ | |
9012b28a | 385 | lbs_deb_11d( |
ece56191 | 386 | "i %d, chan %d unsupported in region %x, band %d\n", |
876c9d3a MT |
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 | ||
9012b28a | 397 | lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan); |
ece56191 | 398 | lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan, |
876c9d3a MT |
399 | 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); |
400 | ||
9012b28a HS |
401 | done: |
402 | lbs_deb_enter(LBS_DEB_11D); | |
876c9d3a MT |
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 | */ | |
10078321 | 412 | u8 lbs_get_scan_type_11d(u8 chan, |
876c9d3a MT |
413 | struct parsed_region_chan_11d * parsed_region_chan) |
414 | { | |
0aef64d7 | 415 | u8 scan_type = CMD_SCAN_TYPE_PASSIVE; |
876c9d3a | 416 | |
9012b28a | 417 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a | 418 | |
10078321 | 419 | if (lbs_channel_known_11d(chan, parsed_region_chan)) { |
ece56191 | 420 | lbs_deb_11d("found, do active scan\n"); |
0aef64d7 | 421 | scan_type = CMD_SCAN_TYPE_ACTIVE; |
876c9d3a | 422 | } else { |
ece56191 | 423 | lbs_deb_11d("not found, do passive scan\n"); |
876c9d3a MT |
424 | } |
425 | ||
9012b28a | 426 | lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type); |
876c9d3a MT |
427 | return scan_type; |
428 | ||
429 | } | |
430 | ||
69f9032d | 431 | void lbs_init_11d(struct lbs_private *priv) |
876c9d3a | 432 | { |
aa21c004 DW |
433 | priv->enable11d = 0; |
434 | memset(&(priv->parsed_region_chan), 0, | |
876c9d3a MT |
435 | sizeof(struct parsed_region_chan_11d)); |
436 | return; | |
437 | } | |
438 | ||
876c9d3a MT |
439 | /** |
440 | * @brief This function sets DOMAIN INFO to FW | |
69f9032d | 441 | * @param priv pointer to struct lbs_private |
876c9d3a MT |
442 | * @return 0; -1 |
443 | */ | |
69f9032d | 444 | static int set_domain_info_11d(struct lbs_private *priv) |
876c9d3a MT |
445 | { |
446 | int ret; | |
447 | ||
aa21c004 | 448 | if (!priv->enable11d) { |
ece56191 | 449 | lbs_deb_11d("dnld domain Info with 11d disabled\n"); |
876c9d3a MT |
450 | return 0; |
451 | } | |
452 | ||
10078321 | 453 | ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO, |
0aef64d7 DW |
454 | CMD_ACT_SET, |
455 | CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a | 456 | if (ret) |
ece56191 | 457 | lbs_deb_11d("fail to dnld domain info\n"); |
876c9d3a MT |
458 | |
459 | return ret; | |
460 | } | |
461 | ||
462 | /** | |
463 | * @brief This function setups scan channels | |
69f9032d | 464 | * @param priv pointer to struct lbs_private |
876c9d3a MT |
465 | * @param band band |
466 | * @return 0 | |
467 | */ | |
69f9032d | 468 | int lbs_set_universaltable(struct lbs_private *priv, u8 band) |
876c9d3a | 469 | { |
876c9d3a MT |
470 | u16 size = sizeof(struct chan_freq_power); |
471 | u16 i = 0; | |
472 | ||
aa21c004 DW |
473 | memset(priv->universal_channel, 0, |
474 | sizeof(priv->universal_channel)); | |
876c9d3a | 475 | |
aa21c004 | 476 | priv->universal_channel[i].nrcfp = |
876c9d3a | 477 | sizeof(channel_freq_power_UN_BG) / size; |
ece56191 | 478 | lbs_deb_11d("BG-band nrcfp %d\n", |
aa21c004 | 479 | priv->universal_channel[i].nrcfp); |
876c9d3a | 480 | |
aa21c004 DW |
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; | |
876c9d3a MT |
485 | i++; |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
490 | /** | |
491 | * @brief This function implements command CMD_802_11D_DOMAIN_INFO | |
69f9032d | 492 | * @param priv pointer to struct lbs_private |
876c9d3a MT |
493 | * @param cmd pointer to cmd buffer |
494 | * @param cmdno cmd ID | |
495 | * @param cmdOption cmd action | |
496 | * @return 0 | |
497 | */ | |
69f9032d | 498 | int lbs_cmd_802_11d_domain_info(struct lbs_private *priv, |
876c9d3a MT |
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; | |
75b6a61a | 504 | struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain; |
aa21c004 | 505 | u8 nr_subband = priv->domainreg.nr_subband; |
876c9d3a | 506 | |
9012b28a | 507 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a | 508 | |
9012b28a | 509 | lbs_deb_11d("nr_subband=%x\n", nr_subband); |
876c9d3a MT |
510 | |
511 | cmd->command = cpu_to_le16(cmdno); | |
512 | pdomaininfo->action = cpu_to_le16(cmdoption); | |
0aef64d7 | 513 | if (cmdoption == CMD_ACT_GET) { |
876c9d3a MT |
514 | cmd->size = |
515 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | |
ece56191 | 516 | lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, |
c2df2efe | 517 | le16_to_cpu(cmd->size)); |
9012b28a | 518 | goto done; |
876c9d3a MT |
519 | } |
520 | ||
521 | domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); | |
aa21c004 | 522 | memcpy(domain->countrycode, priv->domainreg.countrycode, |
876c9d3a MT |
523 | sizeof(domain->countrycode)); |
524 | ||
525 | domain->header.len = | |
75b6a61a | 526 | cpu_to_le16(nr_subband * sizeof(struct ieee_subbandset) + |
876c9d3a MT |
527 | sizeof(domain->countrycode)); |
528 | ||
529 | if (nr_subband) { | |
aa21c004 | 530 | memcpy(domain->subband, priv->domainreg.subband, |
75b6a61a | 531 | nr_subband * sizeof(struct ieee_subbandset)); |
876c9d3a MT |
532 | |
533 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | |
8362cd41 | 534 | le16_to_cpu(domain->header.len) + |
75b6a61a | 535 | sizeof(struct mrvl_ie_header) + |
876c9d3a MT |
536 | S_DS_GEN); |
537 | } else { | |
538 | cmd->size = | |
539 | cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); | |
540 | } | |
541 | ||
ece56191 | 542 | lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size)); |
876c9d3a | 543 | |
9012b28a HS |
544 | done: |
545 | lbs_deb_enter(LBS_DEB_11D); | |
876c9d3a MT |
546 | return 0; |
547 | } | |
548 | ||
876c9d3a MT |
549 | /** |
550 | * @brief This function parses countryinfo from AP and download country info to FW | |
69f9032d | 551 | * @param priv pointer to struct lbs_private |
876c9d3a MT |
552 | * @param resp pointer to command response buffer |
553 | * @return 0; -1 | |
554 | */ | |
e98a88dd | 555 | int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp) |
876c9d3a | 556 | { |
981f187b | 557 | struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp; |
75b6a61a | 558 | struct mrvl_ie_domain_param_set *domain = &domaininfo->domain; |
876c9d3a MT |
559 | u16 action = le16_to_cpu(domaininfo->action); |
560 | s16 ret = 0; | |
561 | u8 nr_subband = 0; | |
562 | ||
9012b28a | 563 | lbs_deb_enter(LBS_DEB_11D); |
876c9d3a | 564 | |
ece56191 | 565 | lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp, |
876c9d3a MT |
566 | (int)le16_to_cpu(resp->size)); |
567 | ||
981f187b | 568 | nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) / |
75b6a61a | 569 | sizeof(struct ieee_subbandset); |
876c9d3a | 570 | |
ece56191 | 571 | lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband); |
876c9d3a MT |
572 | |
573 | if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { | |
9012b28a | 574 | lbs_deb_11d("Invalid Numrer of Subband returned!!\n"); |
876c9d3a MT |
575 | return -1; |
576 | } | |
577 | ||
578 | switch (action) { | |
0aef64d7 | 579 | case CMD_ACT_SET: /*Proc Set action */ |
876c9d3a MT |
580 | break; |
581 | ||
0aef64d7 | 582 | case CMD_ACT_GET: |
876c9d3a MT |
583 | break; |
584 | default: | |
9012b28a | 585 | lbs_deb_11d("Invalid action:%d\n", domaininfo->action); |
876c9d3a MT |
586 | ret = -1; |
587 | break; | |
588 | } | |
589 | ||
9012b28a | 590 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); |
876c9d3a MT |
591 | return ret; |
592 | } | |
593 | ||
594 | /** | |
595 | * @brief This function parses countryinfo from AP and download country info to FW | |
69f9032d | 596 | * @param priv pointer to struct lbs_private |
876c9d3a MT |
597 | * @return 0; -1 |
598 | */ | |
69f9032d | 599 | int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv, |
e76850d6 | 600 | struct bss_descriptor * bss) |
876c9d3a MT |
601 | { |
602 | int ret; | |
876c9d3a | 603 | |
9012b28a | 604 | lbs_deb_enter(LBS_DEB_11D); |
aa21c004 DW |
605 | if (priv->enable11d) { |
606 | memset(&priv->parsed_region_chan, 0, | |
876c9d3a | 607 | sizeof(struct parsed_region_chan_11d)); |
e76850d6 | 608 | ret = parse_domain_info_11d(&bss->countryinfo, 0, |
aa21c004 | 609 | &priv->parsed_region_chan); |
876c9d3a MT |
610 | |
611 | if (ret == -1) { | |
ece56191 | 612 | lbs_deb_11d("error parsing domain_info from AP\n"); |
9012b28a | 613 | goto done; |
876c9d3a MT |
614 | } |
615 | ||
aa21c004 | 616 | memset(&priv->domainreg, 0, |
10078321 | 617 | sizeof(struct lbs_802_11d_domain_reg)); |
aa21c004 DW |
618 | generate_domain_info_11d(&priv->parsed_region_chan, |
619 | &priv->domainreg); | |
876c9d3a MT |
620 | |
621 | ret = set_domain_info_11d(priv); | |
622 | ||
623 | if (ret) { | |
ece56191 | 624 | lbs_deb_11d("error setting domain info\n"); |
9012b28a | 625 | goto done; |
876c9d3a MT |
626 | } |
627 | } | |
9012b28a HS |
628 | ret = 0; |
629 | ||
630 | done: | |
631 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | |
632 | return ret; | |
876c9d3a MT |
633 | } |
634 | ||
635 | /** | |
636 | * @brief This function generates 11D info from user specified regioncode and download to FW | |
69f9032d | 637 | * @param priv pointer to struct lbs_private |
876c9d3a MT |
638 | * @return 0; -1 |
639 | */ | |
69f9032d | 640 | int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv) |
876c9d3a MT |
641 | { |
642 | int ret; | |
876c9d3a MT |
643 | struct region_channel *region_chan; |
644 | u8 j; | |
645 | ||
9012b28a | 646 | lbs_deb_enter(LBS_DEB_11D); |
aa21c004 | 647 | lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band); |
876c9d3a | 648 | |
aa21c004 | 649 | if (priv->enable11d) { |
876c9d3a MT |
650 | /* update parsed_region_chan_11; dnld domaininf to FW */ |
651 | ||
aa21c004 DW |
652 | for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) { |
653 | region_chan = &priv->region_channel[j]; | |
876c9d3a | 654 | |
ece56191 | 655 | lbs_deb_11d("%d region_chan->band %d\n", j, |
876c9d3a MT |
656 | region_chan->band); |
657 | ||
658 | if (!region_chan || !region_chan->valid | |
659 | || !region_chan->CFP) | |
660 | continue; | |
aa21c004 | 661 | if (region_chan->band != priv->curbssparams.band) |
876c9d3a MT |
662 | continue; |
663 | break; | |
664 | } | |
665 | ||
aa21c004 | 666 | if (j >= ARRAY_SIZE(priv->region_channel)) { |
ece56191 | 667 | lbs_deb_11d("region_chan not found, band %d\n", |
aa21c004 | 668 | priv->curbssparams.band); |
9012b28a HS |
669 | ret = -1; |
670 | goto done; | |
876c9d3a MT |
671 | } |
672 | ||
aa21c004 | 673 | memset(&priv->parsed_region_chan, 0, |
876c9d3a | 674 | sizeof(struct parsed_region_chan_11d)); |
10078321 | 675 | lbs_generate_parsed_region_chan_11d(region_chan, |
aa21c004 | 676 | &priv-> |
876c9d3a MT |
677 | parsed_region_chan); |
678 | ||
aa21c004 | 679 | memset(&priv->domainreg, 0, |
10078321 | 680 | sizeof(struct lbs_802_11d_domain_reg)); |
aa21c004 DW |
681 | generate_domain_info_11d(&priv->parsed_region_chan, |
682 | &priv->domainreg); | |
876c9d3a MT |
683 | |
684 | ret = set_domain_info_11d(priv); | |
685 | ||
686 | if (ret) { | |
ece56191 | 687 | lbs_deb_11d("error setting domain info\n"); |
9012b28a | 688 | goto done; |
876c9d3a MT |
689 | } |
690 | ||
691 | } | |
9012b28a | 692 | ret = 0; |
876c9d3a | 693 | |
9012b28a HS |
694 | done: |
695 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | |
696 | return ret; | |
876c9d3a | 697 | } |