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