Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux...
[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 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
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 */
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 */
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
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
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 */
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 */
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 */
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 */
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
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 */
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 */
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 */
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  */
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  */
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  */
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 }