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