linux/drivers/net/wireless/libertas/11d.c
<<
>>
Prefs
   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
  16static 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 */
  29static 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
  46static u8 lbs_region_2_code(u8 *region)
  47{
  48        u8 i;
  49
  50        for (i = 0; i < COUNTRY_CODE_LEN && region[i]; 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
  63static 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*/
  82static 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*/
 116static 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
 137u32 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
 153static 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 ieee_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*/
 220static 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*/
 267static 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
 294done:
 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*/
 305static int parse_domain_info_11d(struct ieee_ie_country_info_full_set *countryinfo,
 306                                 u8 band,
 307                                 struct parsed_region_chan_11d *parsed_region_chan)
 308{
 309        u8 nr_subband, nrchan;
 310        u8 lastchan, firstchan;
 311        u8 region;
 312        u8 curchan = 0;
 313
 314        u8 idx = 0;             /*chan index in parsed_region_chan */
 315
 316        u8 j, i;
 317
 318        lbs_deb_enter(LBS_DEB_11D);
 319
 320        /*validation Rules:
 321           1. valid region Code
 322           2. First Chan increment
 323           3. channel range no overlap
 324           4. channel is valid?
 325           5. channel is supported by region?
 326           6. Others
 327         */
 328
 329        lbs_deb_hex(LBS_DEB_11D, "countryinfo", (u8 *) countryinfo, 30);
 330
 331        if ((*(countryinfo->countrycode)) == 0
 332            || (countryinfo->header.len <= COUNTRY_CODE_LEN)) {
 333                /* No region Info or Wrong region info: treat as No 11D info */
 334                goto done;
 335        }
 336
 337        /*Step1: check region_code */
 338        parsed_region_chan->region = region =
 339            lbs_region_2_code(countryinfo->countrycode);
 340
 341        lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region);
 342        lbs_deb_hex(LBS_DEB_11D, "countrycode", (char *)countryinfo->countrycode,
 343                COUNTRY_CODE_LEN);
 344
 345        parsed_region_chan->band = band;
 346
 347        memcpy(parsed_region_chan->countrycode, countryinfo->countrycode,
 348               COUNTRY_CODE_LEN);
 349
 350        nr_subband = (countryinfo->header.len - COUNTRY_CODE_LEN) /
 351            sizeof(struct ieee_subbandset);
 352
 353        for (j = 0, lastchan = 0; j < nr_subband; j++) {
 354
 355                if (countryinfo->subband[j].firstchan <= lastchan) {
 356                        /*Step2&3. Check First Chan Num increment and no overlap */
 357                        lbs_deb_11d("chan %d>%d, overlap\n",
 358                               countryinfo->subband[j].firstchan, lastchan);
 359                        continue;
 360                }
 361
 362                firstchan = countryinfo->subband[j].firstchan;
 363                nrchan = countryinfo->subband[j].nrchan;
 364
 365                for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) {
 366                        /*step4: channel is supported? */
 367
 368                        if (!lbs_get_chan_11d(firstchan, i, &curchan)) {
 369                                /* Chan is not found in UN table */
 370                                lbs_deb_11d("chan is not supported: %d \n", i);
 371                                break;
 372                        }
 373
 374                        lastchan = curchan;
 375
 376                        if (lbs_region_chan_supported_11d(region, curchan)) {
 377                                /*step5: Check if curchan is supported by mrvl in region */
 378                                parsed_region_chan->chanpwr[idx].chan = curchan;
 379                                parsed_region_chan->chanpwr[idx].pwr =
 380                                    countryinfo->subband[j].maxtxpwr;
 381                                idx++;
 382                        } else {
 383                                /*not supported and ignore the chan */
 384                                lbs_deb_11d(
 385                                       "i %d, chan %d unsupported in region %x, band %d\n",
 386                                       i, curchan, region, band);
 387                        }
 388                }
 389
 390                /*Step6: Add other checking if any */
 391
 392        }
 393
 394        parsed_region_chan->nr_chan = idx;
 395
 396        lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan);
 397        lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan,
 398                2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx);
 399
 400done:
 401        lbs_deb_enter(LBS_DEB_11D);
 402        return 0;
 403}
 404
 405/**
 406 *  @brief This function calculates the scan type for channels
 407 *  @param chan                 chan number
 408 *  @param parsed_region_chan   pointer to parsed_region_chan_11d
 409 *  @return                     PASSIVE if chan is unknown; ACTIVE if chan is known
 410*/
 411u8 lbs_get_scan_type_11d(u8 chan,
 412                          struct parsed_region_chan_11d * parsed_region_chan)
 413{
 414        u8 scan_type = CMD_SCAN_TYPE_PASSIVE;
 415
 416        lbs_deb_enter(LBS_DEB_11D);
 417
 418        if (lbs_channel_known_11d(chan, parsed_region_chan)) {
 419                lbs_deb_11d("found, do active scan\n");
 420                scan_type = CMD_SCAN_TYPE_ACTIVE;
 421        } else {
 422                lbs_deb_11d("not found, do passive scan\n");
 423        }
 424
 425        lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type);
 426        return scan_type;
 427
 428}
 429
 430void lbs_init_11d(struct lbs_private *priv)
 431{
 432        priv->enable11d = 0;
 433        memset(&(priv->parsed_region_chan), 0,
 434               sizeof(struct parsed_region_chan_11d));
 435        return;
 436}
 437
 438/**
 439 *  @brief This function sets DOMAIN INFO to FW
 440 *  @param priv       pointer to struct lbs_private
 441 *  @return           0; -1
 442*/
 443static int set_domain_info_11d(struct lbs_private *priv)
 444{
 445        int ret;
 446
 447        if (!priv->enable11d) {
 448                lbs_deb_11d("dnld domain Info with 11d disabled\n");
 449                return 0;
 450        }
 451
 452        ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO,
 453                                    CMD_ACT_SET,
 454                                    CMD_OPTION_WAITFORRSP, 0, NULL);
 455        if (ret)
 456                lbs_deb_11d("fail to dnld domain info\n");
 457
 458        return ret;
 459}
 460
 461/**
 462 *  @brief This function setups scan channels
 463 *  @param priv       pointer to struct lbs_private
 464 *  @param band       band
 465 *  @return           0
 466*/
 467int lbs_set_universaltable(struct lbs_private *priv, u8 band)
 468{
 469        u16 size = sizeof(struct chan_freq_power);
 470        u16 i = 0;
 471
 472        memset(priv->universal_channel, 0,
 473               sizeof(priv->universal_channel));
 474
 475        priv->universal_channel[i].nrcfp =
 476            sizeof(channel_freq_power_UN_BG) / size;
 477        lbs_deb_11d("BG-band nrcfp %d\n",
 478               priv->universal_channel[i].nrcfp);
 479
 480        priv->universal_channel[i].CFP = channel_freq_power_UN_BG;
 481        priv->universal_channel[i].valid = 1;
 482        priv->universal_channel[i].region = UNIVERSAL_REGION_CODE;
 483        priv->universal_channel[i].band = band;
 484        i++;
 485
 486        return 0;
 487}
 488
 489/**
 490 *  @brief This function implements command CMD_802_11D_DOMAIN_INFO
 491 *  @param priv       pointer to struct lbs_private
 492 *  @param cmd        pointer to cmd buffer
 493 *  @param cmdno      cmd ID
 494 *  @param cmdOption  cmd action
 495 *  @return           0
 496*/
 497int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
 498                                 struct cmd_ds_command *cmd, u16 cmdno,
 499                                 u16 cmdoption)
 500{
 501        struct cmd_ds_802_11d_domain_info *pdomaininfo =
 502            &cmd->params.domaininfo;
 503        struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain;
 504        u8 nr_subband = priv->domainreg.nr_subband;
 505
 506        lbs_deb_enter(LBS_DEB_11D);
 507
 508        lbs_deb_11d("nr_subband=%x\n", nr_subband);
 509
 510        cmd->command = cpu_to_le16(cmdno);
 511        pdomaininfo->action = cpu_to_le16(cmdoption);
 512        if (cmdoption == CMD_ACT_GET) {
 513                cmd->size =
 514                    cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
 515                lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
 516                        le16_to_cpu(cmd->size));
 517                goto done;
 518        }
 519
 520        domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
 521        memcpy(domain->countrycode, priv->domainreg.countrycode,
 522               sizeof(domain->countrycode));
 523
 524        domain->header.len =
 525            cpu_to_le16(nr_subband * sizeof(struct ieee_subbandset) +
 526                             sizeof(domain->countrycode));
 527
 528        if (nr_subband) {
 529                memcpy(domain->subband, priv->domainreg.subband,
 530                       nr_subband * sizeof(struct ieee_subbandset));
 531
 532                cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
 533                                             le16_to_cpu(domain->header.len) +
 534                                             sizeof(struct mrvl_ie_header) +
 535                                             S_DS_GEN);
 536        } else {
 537                cmd->size =
 538                    cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
 539        }
 540
 541        lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size));
 542
 543done:
 544        lbs_deb_enter(LBS_DEB_11D);
 545        return 0;
 546}
 547
 548/**
 549 *  @brief This function parses countryinfo from AP and download country info to FW
 550 *  @param priv    pointer to struct lbs_private
 551 *  @param resp    pointer to command response buffer
 552 *  @return        0; -1
 553 */
 554int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp)
 555{
 556        struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp;
 557        struct mrvl_ie_domain_param_set *domain = &domaininfo->domain;
 558        u16 action = le16_to_cpu(domaininfo->action);
 559        s16 ret = 0;
 560        u8 nr_subband = 0;
 561
 562        lbs_deb_enter(LBS_DEB_11D);
 563
 564        lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
 565                (int)le16_to_cpu(resp->size));
 566
 567        nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
 568                      sizeof(struct ieee_subbandset);
 569
 570        lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband);
 571
 572        if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
 573                lbs_deb_11d("Invalid Numrer of Subband returned!!\n");
 574                return -1;
 575        }
 576
 577        switch (action) {
 578        case CMD_ACT_SET:       /*Proc Set action */
 579                break;
 580
 581        case CMD_ACT_GET:
 582                break;
 583        default:
 584                lbs_deb_11d("Invalid action:%d\n", domaininfo->action);
 585                ret = -1;
 586                break;
 587        }
 588
 589        lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
 590        return ret;
 591}
 592
 593/**
 594 *  @brief This function parses countryinfo from AP and download country info to FW
 595 *  @param priv    pointer to struct lbs_private
 596 *  @return        0; -1
 597 */
 598int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
 599                                        struct bss_descriptor * bss)
 600{
 601        int ret;
 602
 603        lbs_deb_enter(LBS_DEB_11D);
 604        if (priv->enable11d) {
 605                memset(&priv->parsed_region_chan, 0,
 606                       sizeof(struct parsed_region_chan_11d));
 607                ret = parse_domain_info_11d(&bss->countryinfo, 0,
 608                                               &priv->parsed_region_chan);
 609
 610                if (ret == -1) {
 611                        lbs_deb_11d("error parsing domain_info from AP\n");
 612                        goto done;
 613                }
 614
 615                memset(&priv->domainreg, 0,
 616                       sizeof(struct lbs_802_11d_domain_reg));
 617                generate_domain_info_11d(&priv->parsed_region_chan,
 618                                      &priv->domainreg);
 619
 620                ret = set_domain_info_11d(priv);
 621
 622                if (ret) {
 623                        lbs_deb_11d("error setting domain info\n");
 624                        goto done;
 625                }
 626        }
 627        ret = 0;
 628
 629done:
 630        lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
 631        return ret;
 632}
 633
 634/**
 635 *  @brief This function generates 11D info from user specified regioncode and download to FW
 636 *  @param priv    pointer to struct lbs_private
 637 *  @return        0; -1
 638 */
 639int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv)
 640{
 641        int ret;
 642        struct region_channel *region_chan;
 643        u8 j;
 644
 645        lbs_deb_enter(LBS_DEB_11D);
 646        lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band);
 647
 648        if (priv->enable11d) {
 649                /* update parsed_region_chan_11; dnld domaininf to FW */
 650
 651                for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) {
 652                        region_chan = &priv->region_channel[j];
 653
 654                        lbs_deb_11d("%d region_chan->band %d\n", j,
 655                               region_chan->band);
 656
 657                        if (!region_chan || !region_chan->valid
 658                            || !region_chan->CFP)
 659                                continue;
 660                        if (region_chan->band != priv->curbssparams.band)
 661                                continue;
 662                        break;
 663                }
 664
 665                if (j >= ARRAY_SIZE(priv->region_channel)) {
 666                        lbs_deb_11d("region_chan not found, band %d\n",
 667                               priv->curbssparams.band);
 668                        ret = -1;
 669                        goto done;
 670                }
 671
 672                memset(&priv->parsed_region_chan, 0,
 673                       sizeof(struct parsed_region_chan_11d));
 674                lbs_generate_parsed_region_chan_11d(region_chan,
 675                                                     &priv->
 676                                                     parsed_region_chan);
 677
 678                memset(&priv->domainreg, 0,
 679                       sizeof(struct lbs_802_11d_domain_reg));
 680                generate_domain_info_11d(&priv->parsed_region_chan,
 681                                         &priv->domainreg);
 682
 683                ret = set_domain_info_11d(priv);
 684
 685                if (ret) {
 686                        lbs_deb_11d("error setting domain info\n");
 687                        goto done;
 688                }
 689
 690        }
 691        ret = 0;
 692
 693done:
 694        lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
 695        return ret;
 696}
 697