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