261bbd55e94fc1016ff781b29819bb60c344b7d8
[sfrench/cifs-2.6.git] / drivers / net / wireless / libertas / 11d.c
1 /**
2   * This file contains functions for 802.11D.
3   */
4 #include <linux/ctype.h>
5 #include <linux/kernel.h>
6 #include <linux/wireless.h>
7
8 #include "host.h"
9 #include "decl.h"
10 #include "11d.h"
11 #include "dev.h"
12 #include "wext.h"
13
14 #define TX_PWR_DEFAULT  10
15
16 static struct region_code_mapping region_code_mapping[] = {
17         {"US ", 0x10},          /* US FCC      */
18         {"CA ", 0x10},          /* IC Canada   */
19         {"SG ", 0x10},          /* Singapore   */
20         {"EU ", 0x30},          /* ETSI        */
21         {"AU ", 0x30},          /* Australia   */
22         {"KR ", 0x30},          /* Republic Of Korea */
23         {"ES ", 0x31},          /* Spain       */
24         {"FR ", 0x32},          /* France      */
25         {"JP ", 0x40},          /* Japan       */
26 };
27
28 /* Following 2 structure defines the supported channels */
29 static struct chan_freq_power channel_freq_power_UN_BG[] = {
30         {1, 2412, TX_PWR_DEFAULT},
31         {2, 2417, TX_PWR_DEFAULT},
32         {3, 2422, TX_PWR_DEFAULT},
33         {4, 2427, TX_PWR_DEFAULT},
34         {5, 2432, TX_PWR_DEFAULT},
35         {6, 2437, TX_PWR_DEFAULT},
36         {7, 2442, TX_PWR_DEFAULT},
37         {8, 2447, TX_PWR_DEFAULT},
38         {9, 2452, TX_PWR_DEFAULT},
39         {10, 2457, TX_PWR_DEFAULT},
40         {11, 2462, TX_PWR_DEFAULT},
41         {12, 2467, TX_PWR_DEFAULT},
42         {13, 2472, TX_PWR_DEFAULT},
43         {14, 2484, TX_PWR_DEFAULT}
44 };
45
46 static u8 wlan_region_2_code(u8 * region)
47 {
48         u8 i;
49         u8 size = sizeof(region_code_mapping)/
50                   sizeof(struct region_code_mapping);
51
52         for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++)
53                 region[i] = toupper(region[i]);
54
55         for (i = 0; i < size; i++) {
56                 if (!memcmp(region, region_code_mapping[i].region,
57                             COUNTRY_CODE_LEN))
58                         return (region_code_mapping[i].code);
59         }
60
61         /* default is US */
62         return (region_code_mapping[0].code);
63 }
64
65 static u8 *wlan_code_2_region(u8 code)
66 {
67         u8 i;
68         u8 size = sizeof(region_code_mapping)
69                   / sizeof(struct region_code_mapping);
70         for (i = 0; i < size; i++) {
71                 if (region_code_mapping[i].code == code)
72                         return (region_code_mapping[i].region);
73         }
74         /* default is US */
75         return (region_code_mapping[0].region);
76 }
77
78 /**
79  *  @brief This function finds the nrchan-th chan after the firstchan
80  *  @param band       band
81  *  @param firstchan  first channel number
82  *  @param nrchan   number of channels
83  *  @return           the nrchan-th chan number
84 */
85 static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan)
86 /*find the nrchan-th chan after the firstchan*/
87 {
88         u8 i;
89         struct chan_freq_power *cfp;
90         u8 cfp_no;
91
92         cfp = channel_freq_power_UN_BG;
93         cfp_no = sizeof(channel_freq_power_UN_BG) /
94             sizeof(struct chan_freq_power);
95
96         for (i = 0; i < cfp_no; i++) {
97                 if ((cfp + i)->channel == firstchan) {
98                         lbs_deb_11d("firstchan found\n");
99                         break;
100                 }
101         }
102
103         if (i < cfp_no) {
104                 /*if beyond the boundary */
105                 if (i + nrchan < cfp_no) {
106                         *chan = (cfp + i + nrchan)->channel;
107                         return 1;
108                 }
109         }
110
111         return 0;
112 }
113
114 /**
115  *  @brief This function Checks if chan txpwr is learned from AP/IBSS
116  *  @param chan                 chan number
117  *  @param parsed_region_chan   pointer to parsed_region_chan_11d
118  *  @return                     TRUE; FALSE
119 */
120 static u8 wlan_channel_known_11d(u8 chan,
121                           struct parsed_region_chan_11d * parsed_region_chan)
122 {
123         struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr;
124         u8 nr_chan = parsed_region_chan->nr_chan;
125         u8 i = 0;
126
127         lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr,
128                 sizeof(struct chan_power_11d) * nr_chan);
129
130         for (i = 0; i < nr_chan; i++) {
131                 if (chan == chanpwr[i].chan) {
132                         lbs_deb_11d("11D: Found Chan:%d\n", chan);
133                         return 1;
134                 }
135         }
136
137         lbs_deb_11d("11D: Not Find Chan:%d\n", chan);
138         return 0;
139 }
140
141 u32 libertas_chan_2_freq(u8 chan, u8 band)
142 {
143         struct chan_freq_power *cf;
144         u16 cnt;
145         u16 i;
146         u32 freq = 0;
147
148         cf = channel_freq_power_UN_BG;
149         cnt =
150             sizeof(channel_freq_power_UN_BG) /
151             sizeof(struct chan_freq_power);
152
153         for (i = 0; i < cnt; i++) {
154                 if (chan == cf[i].channel)
155                         freq = cf[i].freq;
156         }
157
158         return freq;
159 }
160
161 static int generate_domain_info_11d(struct parsed_region_chan_11d
162                                   *parsed_region_chan,
163                                   struct wlan_802_11d_domain_reg * domaininfo)
164 {
165         u8 nr_subband = 0;
166
167         u8 nr_chan = parsed_region_chan->nr_chan;
168         u8 nr_parsedchan = 0;
169
170         u8 firstchan = 0, nextchan = 0, maxpwr = 0;
171
172         u8 i, flag = 0;
173
174         memcpy(domaininfo->countrycode, parsed_region_chan->countrycode,
175                COUNTRY_CODE_LEN);
176
177         lbs_deb_11d("11D:nrchan=%d\n", nr_chan);
178         lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan,
179                 sizeof(struct parsed_region_chan_11d));
180
181         for (i = 0; i < nr_chan; i++) {
182                 if (!flag) {
183                         flag = 1;
184                         nextchan = firstchan =
185                             parsed_region_chan->chanpwr[i].chan;
186                         maxpwr = parsed_region_chan->chanpwr[i].pwr;
187                         nr_parsedchan = 1;
188                         continue;
189                 }
190
191                 if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 &&
192                     parsed_region_chan->chanpwr[i].pwr == maxpwr) {
193                         nextchan++;
194                         nr_parsedchan++;
195                 } else {
196                         domaininfo->subband[nr_subband].firstchan = firstchan;
197                         domaininfo->subband[nr_subband].nrchan =
198                             nr_parsedchan;
199                         domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
200                         nr_subband++;
201                         nextchan = firstchan =
202                             parsed_region_chan->chanpwr[i].chan;
203                         maxpwr = parsed_region_chan->chanpwr[i].pwr;
204                 }
205         }
206
207         if (flag) {
208                 domaininfo->subband[nr_subband].firstchan = firstchan;
209                 domaininfo->subband[nr_subband].nrchan = nr_parsedchan;
210                 domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
211                 nr_subband++;
212         }
213         domaininfo->nr_subband = nr_subband;
214
215         lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband);
216         lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo,
217                 COUNTRY_CODE_LEN + 1 +
218                 sizeof(struct ieeetypes_subbandset) * nr_subband);
219         return 0;
220 }
221
222 /**
223  *  @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS
224  *  @param region_chan          pointer to struct region_channel
225  *  @param *parsed_region_chan  pointer to parsed_region_chan_11d
226  *  @return                     N/A
227 */
228 static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan,
229                                           struct parsed_region_chan_11d *
230                                           parsed_region_chan)
231 {
232         u8 i;
233         struct chan_freq_power *cfp;
234
235         if (region_chan == NULL) {
236                 lbs_deb_11d("11D: region_chan is NULL\n");
237                 return;
238         }
239
240         cfp = region_chan->CFP;
241         if (cfp == NULL) {
242                 lbs_deb_11d("11D: cfp equal NULL \n");
243                 return;
244         }
245
246         parsed_region_chan->band = region_chan->band;
247         parsed_region_chan->region = region_chan->region;
248         memcpy(parsed_region_chan->countrycode,
249                wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN);
250
251         lbs_deb_11d("11D: region[0x%x] band[%d]\n", parsed_region_chan->region,
252                parsed_region_chan->band);
253
254         for (i = 0; i < region_chan->nrcfp; i++, cfp++) {
255                 parsed_region_chan->chanpwr[i].chan = cfp->channel;
256                 parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower;
257                 lbs_deb_11d("11D: Chan[%d] Pwr[%d]\n",
258                        parsed_region_chan->chanpwr[i].chan,
259                        parsed_region_chan->chanpwr[i].pwr);
260         }
261         parsed_region_chan->nr_chan = region_chan->nrcfp;
262
263         lbs_deb_11d("11D: nrchan[%d]\n", parsed_region_chan->nr_chan);
264
265         return;
266 }
267
268 /**
269  *  @brief generate parsed_region_chan from Domain Info learned from AP/IBSS
270  *  @param region               region ID
271  *  @param band                 band
272  *  @param chan                 chan
273  *  @return                     TRUE;FALSE
274 */
275 static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan)
276 {
277         struct chan_freq_power *cfp;
278         int cfp_no;
279         u8 idx;
280         int ret = 0;
281
282         lbs_deb_enter(LBS_DEB_11D);
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) {
292                                 ret = 0;
293                         } else {
294                                 ret = 1;
295                         }
296                         goto done;
297                 }
298         }
299
300         /*chan is not in the region table */
301
302 done:
303         lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
304         return ret;
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
328         lbs_deb_enter(LBS_DEB_11D);
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 */
344                 goto done;
345         }
346
347         /*Step1: check region_code */
348         parsed_region_chan->region = region =
349             wlan_region_2_code(countryinfo->countrycode);
350
351         lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region);
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 */
367                         lbs_deb_11d("11D: Chan[%d>%d] Overlap\n",
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 */
380                                 lbs_deb_11d("chan is not supported: %d \n", i);
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 */
395                                 lbs_deb_11d(
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
407         lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan);
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
411 done:
412         lbs_deb_enter(LBS_DEB_11D);
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
427         lbs_deb_enter(LBS_DEB_11D);
428
429         if (wlan_channel_known_11d(chan, parsed_region_chan)) {
430                 lbs_deb_11d("11D: Found and do Active Scan\n");
431                 scan_type = cmd_scan_type_active;
432         } else {
433                 lbs_deb_11d("11D: Not Find and do Passive Scan\n");
434         }
435
436         lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type);
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)
463                 lbs_deb_11d("11D: Fail to enable 11D \n");
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) {
478                 lbs_deb_11d("11D: dnld domain Info with 11d disabled\n");
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)
486                 lbs_deb_11d("11D: Fail to dnld domain Info\n");
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;
508         lbs_deb_11d("11D: BG-band nrcfp=%d\n",
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
538         lbs_deb_enter(LBS_DEB_11D);
539
540         lbs_deb_11d("nr_subband=%x\n", nr_subband);
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));
549                 goto done;
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
575 done:
576         lbs_deb_enter(LBS_DEB_11D);
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
591         lbs_deb_enter(LBS_DEB_11D);
592         data = SUBCMD_DATA(wrq);
593
594         lbs_deb_11d("enable 11D: %s\n",
595                (data == 1) ? "enable" : "Disable");
596
597         wlan_enable_11d(priv, data);
598         val = (int *)wrq->u.name;
599         *val = priv->adapter->enable11d;
600
601         lbs_deb_enter(LBS_DEB_11D);
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
621         lbs_deb_enter(LBS_DEB_11D);
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
629         lbs_deb_11d("11D Domain Info Resp: nr_subband=%d\n", nr_subband);
630
631         if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
632                 lbs_deb_11d("Invalid Numrer of Subband returned!!\n");
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:
643                 lbs_deb_11d("Invalid action:%d\n", domaininfo->action);
644                 ret = -1;
645                 break;
646         }
647
648         lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
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                                         struct bss_descriptor * bss)
659 {
660         int ret;
661         wlan_adapter *adapter = priv->adapter;
662
663         lbs_deb_enter(LBS_DEB_11D);
664         if (priv->adapter->enable11d) {
665                 memset(&adapter->parsed_region_chan, 0,
666                        sizeof(struct parsed_region_chan_11d));
667                 ret = parse_domain_info_11d(&bss->countryinfo, 0,
668                                                &adapter->parsed_region_chan);
669
670                 if (ret == -1) {
671                         lbs_deb_11d("11D: Err Parse domain_info from AP..\n");
672                         goto done;
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) {
683                         lbs_deb_11d("11D: Err set domainInfo to FW\n");
684                         goto done;
685                 }
686         }
687         ret = 0;
688
689 done:
690         lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
691         return ret;
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
706         lbs_deb_enter(LBS_DEB_11D);
707         lbs_deb_11d("11D:curbssparams.band[%d]\n", adapter->curbssparams.band);
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
716                         lbs_deb_11d("11D:[%d] region_chan->band[%d]\n", j,
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])) {
729                         lbs_deb_11d("11D:region_chan not found. band[%d]\n",
730                                adapter->curbssparams.band);
731                         ret = -1;
732                         goto done;
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) {
749                         lbs_deb_11d("11D: Err set domainInfo to FW\n");
750                         goto done;
751                 }
752
753         }
754         ret = 0;
755
756 done:
757         lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
758         return ret;
759 }