linux/drivers/net/wireless/libertas/wext.c
<<
>>
Prefs
   1/**
   2  * This file contains ioctl functions
   3  */
   4#include <linux/ctype.h>
   5#include <linux/delay.h>
   6#include <linux/if.h>
   7#include <linux/if_arp.h>
   8#include <linux/wireless.h>
   9#include <linux/bitops.h>
  10
  11#include <net/lib80211.h>
  12#include <net/iw_handler.h>
  13
  14#include "host.h"
  15#include "radiotap.h"
  16#include "decl.h"
  17#include "defs.h"
  18#include "dev.h"
  19#include "wext.h"
  20#include "scan.h"
  21#include "assoc.h"
  22#include "cmd.h"
  23
  24
  25static inline void lbs_postpone_association_work(struct lbs_private *priv)
  26{
  27        if (priv->surpriseremoved)
  28                return;
  29        cancel_delayed_work(&priv->assoc_work);
  30        queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ / 2);
  31}
  32
  33static inline void lbs_do_association_work(struct lbs_private *priv)
  34{
  35        if (priv->surpriseremoved)
  36                return;
  37        cancel_delayed_work(&priv->assoc_work);
  38        queue_delayed_work(priv->work_thread, &priv->assoc_work, 0);
  39}
  40
  41static inline void lbs_cancel_association_work(struct lbs_private *priv)
  42{
  43        cancel_delayed_work(&priv->assoc_work);
  44        kfree(priv->pending_assoc_req);
  45        priv->pending_assoc_req = NULL;
  46}
  47
  48
  49/**
  50 *  @brief Find the channel frequency power info with specific channel
  51 *
  52 *  @param priv         A pointer to struct lbs_private structure
  53 *  @param band         it can be BAND_A, BAND_G or BAND_B
  54 *  @param channel      the channel for looking
  55 *  @return             A pointer to struct chan_freq_power structure or NULL if not find.
  56 */
  57struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
  58        struct lbs_private *priv,
  59        u8 band,
  60        u16 channel)
  61{
  62        struct chan_freq_power *cfp = NULL;
  63        struct region_channel *rc;
  64        int i, j;
  65
  66        for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
  67                rc = &priv->region_channel[j];
  68
  69                if (priv->enable11d)
  70                        rc = &priv->universal_channel[j];
  71                if (!rc->valid || !rc->CFP)
  72                        continue;
  73                if (rc->band != band)
  74                        continue;
  75                for (i = 0; i < rc->nrcfp; i++) {
  76                        if (rc->CFP[i].channel == channel) {
  77                                cfp = &rc->CFP[i];
  78                                break;
  79                        }
  80                }
  81        }
  82
  83        if (!cfp && channel)
  84                lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
  85                       "cfp by band %d / channel %d\n", band, channel);
  86
  87        return cfp;
  88}
  89
  90/**
  91 *  @brief Find the channel frequency power info with specific frequency
  92 *
  93 *  @param priv         A pointer to struct lbs_private structure
  94 *  @param band         it can be BAND_A, BAND_G or BAND_B
  95 *  @param freq         the frequency for looking
  96 *  @return             A pointer to struct chan_freq_power structure or NULL if not find.
  97 */
  98static struct chan_freq_power *find_cfp_by_band_and_freq(
  99        struct lbs_private *priv,
 100        u8 band,
 101        u32 freq)
 102{
 103        struct chan_freq_power *cfp = NULL;
 104        struct region_channel *rc;
 105        int i, j;
 106
 107        for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
 108                rc = &priv->region_channel[j];
 109
 110                if (priv->enable11d)
 111                        rc = &priv->universal_channel[j];
 112                if (!rc->valid || !rc->CFP)
 113                        continue;
 114                if (rc->band != band)
 115                        continue;
 116                for (i = 0; i < rc->nrcfp; i++) {
 117                        if (rc->CFP[i].freq == freq) {
 118                                cfp = &rc->CFP[i];
 119                                break;
 120                        }
 121                }
 122        }
 123
 124        if (!cfp && freq)
 125                lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
 126                       "band %d / freq %d\n", band, freq);
 127
 128        return cfp;
 129}
 130
 131/**
 132 *  @brief Copy active data rates based on adapter mode and status
 133 *
 134 *  @param priv              A pointer to struct lbs_private structure
 135 *  @param rate                 The buf to return the active rates
 136 */
 137static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
 138{
 139        lbs_deb_enter(LBS_DEB_WEXT);
 140
 141        if ((priv->connect_status != LBS_CONNECTED) &&
 142                (priv->mesh_connect_status != LBS_CONNECTED))
 143                memcpy(rates, lbs_bg_rates, MAX_RATES);
 144        else
 145                memcpy(rates, priv->curbssparams.rates, MAX_RATES);
 146
 147        lbs_deb_leave(LBS_DEB_WEXT);
 148}
 149
 150static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
 151                         char *cwrq, char *extra)
 152{
 153
 154        lbs_deb_enter(LBS_DEB_WEXT);
 155
 156        /* We could add support for 802.11n here as needed. Jean II */
 157        snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g");
 158
 159        lbs_deb_leave(LBS_DEB_WEXT);
 160        return 0;
 161}
 162
 163static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
 164                         struct iw_freq *fwrq, char *extra)
 165{
 166        struct lbs_private *priv = dev->ml_priv;
 167        struct chan_freq_power *cfp;
 168
 169        lbs_deb_enter(LBS_DEB_WEXT);
 170
 171        cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
 172                                           priv->curbssparams.channel);
 173
 174        if (!cfp) {
 175                if (priv->curbssparams.channel)
 176                        lbs_deb_wext("invalid channel %d\n",
 177                               priv->curbssparams.channel);
 178                return -EINVAL;
 179        }
 180
 181        fwrq->m = (long)cfp->freq * 100000;
 182        fwrq->e = 1;
 183
 184        lbs_deb_wext("freq %u\n", fwrq->m);
 185        lbs_deb_leave(LBS_DEB_WEXT);
 186        return 0;
 187}
 188
 189static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
 190                        struct sockaddr *awrq, char *extra)
 191{
 192        struct lbs_private *priv = dev->ml_priv;
 193
 194        lbs_deb_enter(LBS_DEB_WEXT);
 195
 196        if (priv->connect_status == LBS_CONNECTED) {
 197                memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
 198        } else {
 199                memset(awrq->sa_data, 0, ETH_ALEN);
 200        }
 201        awrq->sa_family = ARPHRD_ETHER;
 202
 203        lbs_deb_leave(LBS_DEB_WEXT);
 204        return 0;
 205}
 206
 207static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
 208                         struct iw_point *dwrq, char *extra)
 209{
 210        struct lbs_private *priv = dev->ml_priv;
 211
 212        lbs_deb_enter(LBS_DEB_WEXT);
 213
 214        /*
 215         * Check the size of the string
 216         */
 217
 218        if (dwrq->length > 16) {
 219                return -E2BIG;
 220        }
 221
 222        mutex_lock(&priv->lock);
 223        memset(priv->nodename, 0, sizeof(priv->nodename));
 224        memcpy(priv->nodename, extra, dwrq->length);
 225        mutex_unlock(&priv->lock);
 226
 227        lbs_deb_leave(LBS_DEB_WEXT);
 228        return 0;
 229}
 230
 231static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
 232                         struct iw_point *dwrq, char *extra)
 233{
 234        struct lbs_private *priv = dev->ml_priv;
 235
 236        lbs_deb_enter(LBS_DEB_WEXT);
 237
 238        dwrq->length = strlen(priv->nodename);
 239        memcpy(extra, priv->nodename, dwrq->length);
 240        extra[dwrq->length] = '\0';
 241
 242        dwrq->flags = 1;        /* active */
 243
 244        lbs_deb_leave(LBS_DEB_WEXT);
 245        return 0;
 246}
 247
 248static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
 249                         struct iw_point *dwrq, char *extra)
 250{
 251        struct lbs_private *priv = dev->ml_priv;
 252
 253        lbs_deb_enter(LBS_DEB_WEXT);
 254
 255        /* Use nickname to indicate that mesh is on */
 256
 257        if (priv->mesh_connect_status == LBS_CONNECTED) {
 258                strncpy(extra, "Mesh", 12);
 259                extra[12] = '\0';
 260                dwrq->length = strlen(extra);
 261        }
 262
 263        else {
 264                extra[0] = '\0';
 265                dwrq->length = 0;
 266        }
 267
 268        lbs_deb_leave(LBS_DEB_WEXT);
 269        return 0;
 270}
 271
 272static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
 273                        struct iw_param *vwrq, char *extra)
 274{
 275        int ret = 0;
 276        struct lbs_private *priv = dev->ml_priv;
 277        u32 val = vwrq->value;
 278
 279        lbs_deb_enter(LBS_DEB_WEXT);
 280
 281        if (vwrq->disabled)
 282                val = MRVDRV_RTS_MAX_VALUE;
 283
 284        if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
 285                return -EINVAL;
 286
 287        ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, (u16) val);
 288
 289        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 290        return ret;
 291}
 292
 293static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
 294                        struct iw_param *vwrq, char *extra)
 295{
 296        struct lbs_private *priv = dev->ml_priv;
 297        int ret = 0;
 298        u16 val = 0;
 299
 300        lbs_deb_enter(LBS_DEB_WEXT);
 301
 302        ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
 303        if (ret)
 304                goto out;
 305
 306        vwrq->value = val;
 307        vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
 308        vwrq->fixed = 1;
 309
 310out:
 311        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 312        return ret;
 313}
 314
 315static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
 316                         struct iw_param *vwrq, char *extra)
 317{
 318        struct lbs_private *priv = dev->ml_priv;
 319        int ret = 0;
 320        u32 val = vwrq->value;
 321
 322        lbs_deb_enter(LBS_DEB_WEXT);
 323
 324        if (vwrq->disabled)
 325                val = MRVDRV_FRAG_MAX_VALUE;
 326
 327        if (val < MRVDRV_FRAG_MIN_VALUE || val > MRVDRV_FRAG_MAX_VALUE)
 328                return -EINVAL;
 329
 330        ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
 331
 332        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 333        return ret;
 334}
 335
 336static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
 337                         struct iw_param *vwrq, char *extra)
 338{
 339        struct lbs_private *priv = dev->ml_priv;
 340        int ret = 0;
 341        u16 val = 0;
 342
 343        lbs_deb_enter(LBS_DEB_WEXT);
 344
 345        ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
 346        if (ret)
 347                goto out;
 348
 349        vwrq->value = val;
 350        vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
 351                          || (val > MRVDRV_FRAG_MAX_VALUE));
 352        vwrq->fixed = 1;
 353
 354out:
 355        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 356        return ret;
 357}
 358
 359static int lbs_get_mode(struct net_device *dev,
 360                         struct iw_request_info *info, u32 * uwrq, char *extra)
 361{
 362        struct lbs_private *priv = dev->ml_priv;
 363
 364        lbs_deb_enter(LBS_DEB_WEXT);
 365
 366        *uwrq = priv->mode;
 367
 368        lbs_deb_leave(LBS_DEB_WEXT);
 369        return 0;
 370}
 371
 372static int mesh_wlan_get_mode(struct net_device *dev,
 373                              struct iw_request_info *info, u32 * uwrq,
 374                              char *extra)
 375{
 376        lbs_deb_enter(LBS_DEB_WEXT);
 377
 378        *uwrq = IW_MODE_REPEAT;
 379
 380        lbs_deb_leave(LBS_DEB_WEXT);
 381        return 0;
 382}
 383
 384static int lbs_get_txpow(struct net_device *dev,
 385                          struct iw_request_info *info,
 386                          struct iw_param *vwrq, char *extra)
 387{
 388        struct lbs_private *priv = dev->ml_priv;
 389        s16 curlevel = 0;
 390        int ret = 0;
 391
 392        lbs_deb_enter(LBS_DEB_WEXT);
 393
 394        if (!priv->radio_on) {
 395                lbs_deb_wext("tx power off\n");
 396                vwrq->value = 0;
 397                vwrq->disabled = 1;
 398                goto out;
 399        }
 400
 401        ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
 402        if (ret)
 403                goto out;
 404
 405        lbs_deb_wext("tx power level %d dbm\n", curlevel);
 406        priv->txpower_cur = curlevel;
 407
 408        vwrq->value = curlevel;
 409        vwrq->fixed = 1;
 410        vwrq->disabled = 0;
 411        vwrq->flags = IW_TXPOW_DBM;
 412
 413out:
 414        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 415        return ret;
 416}
 417
 418static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
 419                          struct iw_param *vwrq, char *extra)
 420{
 421        struct lbs_private *priv = dev->ml_priv;
 422        int ret = 0;
 423        u16 slimit = 0, llimit = 0;
 424
 425        lbs_deb_enter(LBS_DEB_WEXT);
 426
 427        if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
 428                return -EOPNOTSUPP;
 429
 430        /* The MAC has a 4-bit Total_Tx_Count register
 431           Total_Tx_Count = 1 + Tx_Retry_Count */
 432#define TX_RETRY_MIN 0
 433#define TX_RETRY_MAX 14
 434        if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
 435                return -EINVAL;
 436
 437        /* Add 1 to convert retry count to try count */
 438        if (vwrq->flags & IW_RETRY_SHORT)
 439                slimit = (u16) (vwrq->value + 1);
 440        else if (vwrq->flags & IW_RETRY_LONG)
 441                llimit = (u16) (vwrq->value + 1);
 442        else
 443                slimit = llimit = (u16) (vwrq->value + 1); /* set both */
 444
 445        if (llimit) {
 446                ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
 447                                       llimit);
 448                if (ret)
 449                        goto out;
 450        }
 451
 452        if (slimit) {
 453                /* txretrycount follows the short retry limit */
 454                priv->txretrycount = slimit;
 455                ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
 456                                       slimit);
 457                if (ret)
 458                        goto out;
 459        }
 460
 461out:
 462        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 463        return ret;
 464}
 465
 466static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
 467                          struct iw_param *vwrq, char *extra)
 468{
 469        struct lbs_private *priv = dev->ml_priv;
 470        int ret = 0;
 471        u16 val = 0;
 472
 473        lbs_deb_enter(LBS_DEB_WEXT);
 474
 475        vwrq->disabled = 0;
 476
 477        if (vwrq->flags & IW_RETRY_LONG) {
 478                ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT, &val);
 479                if (ret)
 480                        goto out;
 481
 482                /* Subtract 1 to convert try count to retry count */
 483                vwrq->value = val - 1;
 484                vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
 485        } else {
 486                ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT, &val);
 487                if (ret)
 488                        goto out;
 489
 490                /* txretry count follows the short retry limit */
 491                priv->txretrycount = val;
 492                /* Subtract 1 to convert try count to retry count */
 493                vwrq->value = val - 1;
 494                vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
 495        }
 496
 497out:
 498        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 499        return ret;
 500}
 501
 502static inline void sort_channels(struct iw_freq *freq, int num)
 503{
 504        int i, j;
 505        struct iw_freq temp;
 506
 507        for (i = 0; i < num; i++)
 508                for (j = i + 1; j < num; j++)
 509                        if (freq[i].i > freq[j].i) {
 510                                temp.i = freq[i].i;
 511                                temp.m = freq[i].m;
 512
 513                                freq[i].i = freq[j].i;
 514                                freq[i].m = freq[j].m;
 515
 516                                freq[j].i = temp.i;
 517                                freq[j].m = temp.m;
 518                        }
 519}
 520
 521/* data rate listing
 522        MULTI_BANDS:
 523                abg             a       b       b/g
 524   Infra        G(12)           A(8)    B(4)    G(12)
 525   Adhoc        A+B(12)         A(8)    B(4)    B(4)
 526
 527        non-MULTI_BANDS:
 528                                        b       b/g
 529   Infra                                B(4)    G(12)
 530   Adhoc                                B(4)    B(4)
 531 */
 532/**
 533 *  @brief Get Range Info
 534 *
 535 *  @param dev                  A pointer to net_device structure
 536 *  @param info                 A pointer to iw_request_info structure
 537 *  @param vwrq                 A pointer to iw_param structure
 538 *  @param extra                A pointer to extra data buf
 539 *  @return                     0 --success, otherwise fail
 540 */
 541static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
 542                          struct iw_point *dwrq, char *extra)
 543{
 544        int i, j;
 545        struct lbs_private *priv = dev->ml_priv;
 546        struct iw_range *range = (struct iw_range *)extra;
 547        struct chan_freq_power *cfp;
 548        u8 rates[MAX_RATES + 1];
 549
 550        u8 flag = 0;
 551
 552        lbs_deb_enter(LBS_DEB_WEXT);
 553
 554        dwrq->length = sizeof(struct iw_range);
 555        memset(range, 0, sizeof(struct iw_range));
 556
 557        range->min_nwid = 0;
 558        range->max_nwid = 0;
 559
 560        memset(rates, 0, sizeof(rates));
 561        copy_active_data_rates(priv, rates);
 562        range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
 563        for (i = 0; i < range->num_bitrates; i++)
 564                range->bitrate[i] = rates[i] * 500000;
 565        range->num_bitrates = i;
 566        lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
 567               range->num_bitrates);
 568
 569        range->num_frequency = 0;
 570
 571        range->scan_capa = IW_SCAN_CAPA_ESSID;
 572
 573        if (priv->enable11d &&
 574            (priv->connect_status == LBS_CONNECTED ||
 575            priv->mesh_connect_status == LBS_CONNECTED)) {
 576                u8 chan_no;
 577                u8 band;
 578
 579                struct parsed_region_chan_11d *parsed_region_chan =
 580                    &priv->parsed_region_chan;
 581
 582                if (parsed_region_chan == NULL) {
 583                        lbs_deb_wext("11d: parsed_region_chan is NULL\n");
 584                        goto out;
 585                }
 586                band = parsed_region_chan->band;
 587                lbs_deb_wext("band %d, nr_char %d\n", band,
 588                       parsed_region_chan->nr_chan);
 589
 590                for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
 591                     && (i < parsed_region_chan->nr_chan); i++) {
 592                        chan_no = parsed_region_chan->chanpwr[i].chan;
 593                        lbs_deb_wext("chan_no %d\n", chan_no);
 594                        range->freq[range->num_frequency].i = (long)chan_no;
 595                        range->freq[range->num_frequency].m =
 596                            (long)lbs_chan_2_freq(chan_no) * 100000;
 597                        range->freq[range->num_frequency].e = 1;
 598                        range->num_frequency++;
 599                }
 600                flag = 1;
 601        }
 602        if (!flag) {
 603                for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
 604                     && (j < ARRAY_SIZE(priv->region_channel)); j++) {
 605                        cfp = priv->region_channel[j].CFP;
 606                        for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
 607                             && priv->region_channel[j].valid
 608                             && cfp
 609                             && (i < priv->region_channel[j].nrcfp); i++) {
 610                                range->freq[range->num_frequency].i =
 611                                    (long)cfp->channel;
 612                                range->freq[range->num_frequency].m =
 613                                    (long)cfp->freq * 100000;
 614                                range->freq[range->num_frequency].e = 1;
 615                                cfp++;
 616                                range->num_frequency++;
 617                        }
 618                }
 619        }
 620
 621        lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
 622               IW_MAX_FREQUENCIES, range->num_frequency);
 623
 624        range->num_channels = range->num_frequency;
 625
 626        sort_channels(&range->freq[0], range->num_frequency);
 627
 628        /*
 629         * Set an indication of the max TCP throughput in bit/s that we can
 630         * expect using this interface
 631         */
 632        if (i > 2)
 633                range->throughput = 5000 * 1000;
 634        else
 635                range->throughput = 1500 * 1000;
 636
 637        range->min_rts = MRVDRV_RTS_MIN_VALUE;
 638        range->max_rts = MRVDRV_RTS_MAX_VALUE;
 639        range->min_frag = MRVDRV_FRAG_MIN_VALUE;
 640        range->max_frag = MRVDRV_FRAG_MAX_VALUE;
 641
 642        range->encoding_size[0] = 5;
 643        range->encoding_size[1] = 13;
 644        range->num_encoding_sizes = 2;
 645        range->max_encoding_tokens = 4;
 646
 647        /*
 648         * Right now we support only "iwconfig ethX power on|off"
 649         */
 650        range->pm_capa = IW_POWER_ON;
 651
 652        /*
 653         * Minimum version we recommend
 654         */
 655        range->we_version_source = 15;
 656
 657        /*
 658         * Version we are compiled with
 659         */
 660        range->we_version_compiled = WIRELESS_EXT;
 661
 662        range->retry_capa = IW_RETRY_LIMIT;
 663        range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
 664
 665        range->min_retry = TX_RETRY_MIN;
 666        range->max_retry = TX_RETRY_MAX;
 667
 668        /*
 669         * Set the qual, level and noise range values
 670         */
 671        range->max_qual.qual = 100;
 672        range->max_qual.level = 0;
 673        range->max_qual.noise = 0;
 674        range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
 675
 676        range->avg_qual.qual = 70;
 677        /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
 678        range->avg_qual.level = 0;
 679        range->avg_qual.noise = 0;
 680        range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
 681
 682        range->sensitivity = 0;
 683
 684        /* Setup the supported power level ranges */
 685        memset(range->txpower, 0, sizeof(range->txpower));
 686        range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE;
 687        range->txpower[0] = priv->txpower_min;
 688        range->txpower[1] = priv->txpower_max;
 689        range->num_txpower = 2;
 690
 691        range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
 692                                IW_EVENT_CAPA_MASK(SIOCGIWAP) |
 693                                IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
 694        range->event_capa[1] = IW_EVENT_CAPA_K_1;
 695
 696        if (priv->fwcapinfo & FW_CAPINFO_WPA) {
 697                range->enc_capa =   IW_ENC_CAPA_WPA
 698                                  | IW_ENC_CAPA_WPA2
 699                                  | IW_ENC_CAPA_CIPHER_TKIP
 700                                  | IW_ENC_CAPA_CIPHER_CCMP;
 701        }
 702
 703out:
 704        lbs_deb_leave(LBS_DEB_WEXT);
 705        return 0;
 706}
 707
 708static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
 709                          struct iw_param *vwrq, char *extra)
 710{
 711        struct lbs_private *priv = dev->ml_priv;
 712
 713        lbs_deb_enter(LBS_DEB_WEXT);
 714
 715        if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
 716                if (vwrq->disabled)
 717                        return 0;
 718                else
 719                        return -EINVAL;
 720        }
 721
 722        /* PS is currently supported only in Infrastructure mode
 723         * Remove this check if it is to be supported in IBSS mode also
 724         */
 725
 726        if (vwrq->disabled) {
 727                priv->psmode = LBS802_11POWERMODECAM;
 728                if (priv->psstate != PS_STATE_FULL_POWER) {
 729                        lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
 730                }
 731
 732                return 0;
 733        }
 734
 735        if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
 736                lbs_deb_wext(
 737                       "setting power timeout is not supported\n");
 738                return -EINVAL;
 739        } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
 740                lbs_deb_wext("setting power period not supported\n");
 741                return -EINVAL;
 742        }
 743
 744        if (priv->psmode != LBS802_11POWERMODECAM) {
 745                return 0;
 746        }
 747
 748        priv->psmode = LBS802_11POWERMODEMAX_PSP;
 749
 750        if (priv->connect_status == LBS_CONNECTED) {
 751                lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
 752        }
 753
 754        lbs_deb_leave(LBS_DEB_WEXT);
 755        return 0;
 756}
 757
 758static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
 759                          struct iw_param *vwrq, char *extra)
 760{
 761        struct lbs_private *priv = dev->ml_priv;
 762
 763        lbs_deb_enter(LBS_DEB_WEXT);
 764
 765        vwrq->value = 0;
 766        vwrq->flags = 0;
 767        vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
 768                || priv->connect_status == LBS_DISCONNECTED;
 769
 770        lbs_deb_leave(LBS_DEB_WEXT);
 771        return 0;
 772}
 773
 774static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
 775{
 776        enum {
 777                POOR = 30,
 778                FAIR = 60,
 779                GOOD = 80,
 780                VERY_GOOD = 90,
 781                EXCELLENT = 95,
 782                PERFECT = 100
 783        };
 784        struct lbs_private *priv = dev->ml_priv;
 785        u32 rssi_qual;
 786        u32 tx_qual;
 787        u32 quality = 0;
 788        int stats_valid = 0;
 789        u8 rssi;
 790        u32 tx_retries;
 791        struct cmd_ds_802_11_get_log log;
 792
 793        lbs_deb_enter(LBS_DEB_WEXT);
 794
 795        priv->wstats.status = priv->mode;
 796
 797        /* If we're not associated, all quality values are meaningless */
 798        if ((priv->connect_status != LBS_CONNECTED) &&
 799            (priv->mesh_connect_status != LBS_CONNECTED))
 800                goto out;
 801
 802        /* Quality by RSSI */
 803        priv->wstats.qual.level =
 804            CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
 805             priv->NF[TYPE_BEACON][TYPE_NOAVG]);
 806
 807        if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
 808                priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
 809        } else {
 810                priv->wstats.qual.noise =
 811                    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
 812        }
 813
 814        lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
 815        lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
 816
 817        rssi = priv->wstats.qual.level - priv->wstats.qual.noise;
 818        if (rssi < 15)
 819                rssi_qual = rssi * POOR / 10;
 820        else if (rssi < 20)
 821                rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR;
 822        else if (rssi < 30)
 823                rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR;
 824        else if (rssi < 40)
 825                rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) /
 826                    10 + GOOD;
 827        else
 828                rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) /
 829                    10 + VERY_GOOD;
 830        quality = rssi_qual;
 831
 832        /* Quality by TX errors */
 833        priv->wstats.discard.retries = dev->stats.tx_errors;
 834
 835        memset(&log, 0, sizeof(log));
 836        log.hdr.size = cpu_to_le16(sizeof(log));
 837        lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
 838
 839        tx_retries = le32_to_cpu(log.retry);
 840
 841        if (tx_retries > 75)
 842                tx_qual = (90 - tx_retries) * POOR / 15;
 843        else if (tx_retries > 70)
 844                tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
 845        else if (tx_retries > 65)
 846                tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
 847        else if (tx_retries > 50)
 848                tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
 849                    15 + GOOD;
 850        else
 851                tx_qual = (50 - tx_retries) *
 852                    (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
 853        quality = min(quality, tx_qual);
 854
 855        priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
 856        priv->wstats.discard.retries = tx_retries;
 857        priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
 858
 859        /* Calculate quality */
 860        priv->wstats.qual.qual = min_t(u8, quality, 100);
 861        priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
 862        stats_valid = 1;
 863
 864        /* update stats asynchronously for future calls */
 865        lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
 866                                        0, 0, NULL);
 867out:
 868        if (!stats_valid) {
 869                priv->wstats.miss.beacon = 0;
 870                priv->wstats.discard.retries = 0;
 871                priv->wstats.qual.qual = 0;
 872                priv->wstats.qual.level = 0;
 873                priv->wstats.qual.noise = 0;
 874                priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
 875                priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
 876                    IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
 877        }
 878
 879        lbs_deb_leave(LBS_DEB_WEXT);
 880        return &priv->wstats;
 881
 882
 883}
 884
 885static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
 886                  struct iw_freq *fwrq, char *extra)
 887{
 888        int ret = -EINVAL;
 889        struct lbs_private *priv = dev->ml_priv;
 890        struct chan_freq_power *cfp;
 891        struct assoc_request * assoc_req;
 892
 893        lbs_deb_enter(LBS_DEB_WEXT);
 894
 895        mutex_lock(&priv->lock);
 896        assoc_req = lbs_get_association_request(priv);
 897        if (!assoc_req) {
 898                ret = -ENOMEM;
 899                goto out;
 900        }
 901
 902        /* If setting by frequency, convert to a channel */
 903        if (fwrq->e == 1) {
 904                long f = fwrq->m / 100000;
 905
 906                cfp = find_cfp_by_band_and_freq(priv, 0, f);
 907                if (!cfp) {
 908                        lbs_deb_wext("invalid freq %ld\n", f);
 909                        goto out;
 910                }
 911
 912                fwrq->e = 0;
 913                fwrq->m = (int) cfp->channel;
 914        }
 915
 916        /* Setting by channel number */
 917        if (fwrq->m > 1000 || fwrq->e > 0) {
 918                goto out;
 919        }
 920
 921        cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
 922        if (!cfp) {
 923                goto out;
 924        }
 925
 926        assoc_req->channel = fwrq->m;
 927        ret = 0;
 928
 929out:
 930        if (ret == 0) {
 931                set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
 932                lbs_postpone_association_work(priv);
 933        } else {
 934                lbs_cancel_association_work(priv);
 935        }
 936        mutex_unlock(&priv->lock);
 937
 938        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 939        return ret;
 940}
 941
 942static int lbs_mesh_set_freq(struct net_device *dev,
 943                             struct iw_request_info *info,
 944                             struct iw_freq *fwrq, char *extra)
 945{
 946        struct lbs_private *priv = dev->ml_priv;
 947        struct chan_freq_power *cfp;
 948        int ret = -EINVAL;
 949
 950        lbs_deb_enter(LBS_DEB_WEXT);
 951
 952        /* If setting by frequency, convert to a channel */
 953        if (fwrq->e == 1) {
 954                long f = fwrq->m / 100000;
 955
 956                cfp = find_cfp_by_band_and_freq(priv, 0, f);
 957                if (!cfp) {
 958                        lbs_deb_wext("invalid freq %ld\n", f);
 959                        goto out;
 960                }
 961
 962                fwrq->e = 0;
 963                fwrq->m = (int) cfp->channel;
 964        }
 965
 966        /* Setting by channel number */
 967        if (fwrq->m > 1000 || fwrq->e > 0) {
 968                goto out;
 969        }
 970
 971        cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
 972        if (!cfp) {
 973                goto out;
 974        }
 975
 976        if (fwrq->m != priv->curbssparams.channel) {
 977                lbs_deb_wext("mesh channel change forces eth disconnect\n");
 978                if (priv->mode == IW_MODE_INFRA)
 979                        lbs_cmd_80211_deauthenticate(priv,
 980                                                     priv->curbssparams.bssid,
 981                                                     WLAN_REASON_DEAUTH_LEAVING);
 982                else if (priv->mode == IW_MODE_ADHOC)
 983                        lbs_adhoc_stop(priv);
 984        }
 985        lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
 986        lbs_update_channel(priv);
 987        ret = 0;
 988
 989out:
 990        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
 991        return ret;
 992}
 993
 994static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
 995                  struct iw_param *vwrq, char *extra)
 996{
 997        struct lbs_private *priv = dev->ml_priv;
 998        u8 new_rate = 0;
 999        int ret = -EINVAL;
1000        u8 rates[MAX_RATES + 1];
1001
1002        lbs_deb_enter(LBS_DEB_WEXT);
1003        lbs_deb_wext("vwrq->value %d\n", vwrq->value);
1004        lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
1005
1006        if (vwrq->fixed && vwrq->value == -1)
1007                goto out;
1008
1009        /* Auto rate? */
1010        priv->enablehwauto = !vwrq->fixed;
1011
1012        if (vwrq->value == -1)
1013                priv->cur_rate = 0;
1014        else {
1015                if (vwrq->value % 100000)
1016                        goto out;
1017
1018                new_rate = vwrq->value / 500000;
1019                priv->cur_rate = new_rate;
1020                /* the rest is only needed for lbs_set_data_rate() */
1021                memset(rates, 0, sizeof(rates));
1022                copy_active_data_rates(priv, rates);
1023                if (!memchr(rates, new_rate, sizeof(rates))) {
1024                        lbs_pr_alert("fixed data rate 0x%X out of range\n",
1025                                new_rate);
1026                        goto out;
1027                }
1028                if (priv->fwrelease < 0x09000000) {
1029                        ret = lbs_set_power_adapt_cfg(priv, 0,
1030                                        POW_ADAPT_DEFAULT_P0,
1031                                        POW_ADAPT_DEFAULT_P1,
1032                                        POW_ADAPT_DEFAULT_P2);
1033                        if (ret)
1034                                goto out;
1035                }
1036                ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
1037                                TPC_DEFAULT_P2, 1);
1038                if (ret)
1039                        goto out;
1040        }
1041
1042        /* Try the newer command first (Firmware Spec 5.1 and above) */
1043        ret = lbs_cmd_802_11_rate_adapt_rateset(priv, CMD_ACT_SET);
1044
1045        /* Fallback to older version */
1046        if (ret)
1047                ret = lbs_set_data_rate(priv, new_rate);
1048
1049out:
1050        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1051        return ret;
1052}
1053
1054static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
1055                  struct iw_param *vwrq, char *extra)
1056{
1057        struct lbs_private *priv = dev->ml_priv;
1058
1059        lbs_deb_enter(LBS_DEB_WEXT);
1060
1061        if (priv->connect_status == LBS_CONNECTED) {
1062                vwrq->value = priv->cur_rate * 500000;
1063
1064                if (priv->enablehwauto)
1065                        vwrq->fixed = 0;
1066                else
1067                        vwrq->fixed = 1;
1068
1069        } else {
1070                vwrq->fixed = 0;
1071                vwrq->value = 0;
1072        }
1073
1074        lbs_deb_leave(LBS_DEB_WEXT);
1075        return 0;
1076}
1077
1078static int lbs_set_mode(struct net_device *dev,
1079                  struct iw_request_info *info, u32 * uwrq, char *extra)
1080{
1081        int ret = 0;
1082        struct lbs_private *priv = dev->ml_priv;
1083        struct assoc_request * assoc_req;
1084
1085        lbs_deb_enter(LBS_DEB_WEXT);
1086
1087        if (   (*uwrq != IW_MODE_ADHOC)
1088            && (*uwrq != IW_MODE_INFRA)
1089            && (*uwrq != IW_MODE_AUTO)) {
1090                lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
1091                ret = -EINVAL;
1092                goto out;
1093        }
1094
1095        mutex_lock(&priv->lock);
1096        assoc_req = lbs_get_association_request(priv);
1097        if (!assoc_req) {
1098                ret = -ENOMEM;
1099                lbs_cancel_association_work(priv);
1100        } else {
1101                assoc_req->mode = *uwrq;
1102                set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1103                lbs_postpone_association_work(priv);
1104                lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
1105        }
1106        mutex_unlock(&priv->lock);
1107
1108out:
1109        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1110        return ret;
1111}
1112
1113
1114/**
1115 *  @brief Get Encryption key
1116 *
1117 *  @param dev                  A pointer to net_device structure
1118 *  @param info                 A pointer to iw_request_info structure
1119 *  @param vwrq                 A pointer to iw_param structure
1120 *  @param extra                A pointer to extra data buf
1121 *  @return                     0 --success, otherwise fail
1122 */
1123static int lbs_get_encode(struct net_device *dev,
1124                           struct iw_request_info *info,
1125                           struct iw_point *dwrq, u8 * extra)
1126{
1127        struct lbs_private *priv = dev->ml_priv;
1128        int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1129
1130        lbs_deb_enter(LBS_DEB_WEXT);
1131
1132        lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
1133               dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
1134
1135        dwrq->flags = 0;
1136
1137        /* Authentication method */
1138        switch (priv->secinfo.auth_mode) {
1139        case IW_AUTH_ALG_OPEN_SYSTEM:
1140                dwrq->flags = IW_ENCODE_OPEN;
1141                break;
1142
1143        case IW_AUTH_ALG_SHARED_KEY:
1144        case IW_AUTH_ALG_LEAP:
1145                dwrq->flags = IW_ENCODE_RESTRICTED;
1146                break;
1147        default:
1148                dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
1149                break;
1150        }
1151
1152        memset(extra, 0, 16);
1153
1154        mutex_lock(&priv->lock);
1155
1156        /* Default to returning current transmit key */
1157        if (index < 0)
1158                index = priv->wep_tx_keyidx;
1159
1160        if ((priv->wep_keys[index].len) && priv->secinfo.wep_enabled) {
1161                memcpy(extra, priv->wep_keys[index].key,
1162                       priv->wep_keys[index].len);
1163                dwrq->length = priv->wep_keys[index].len;
1164
1165                dwrq->flags |= (index + 1);
1166                /* Return WEP enabled */
1167                dwrq->flags &= ~IW_ENCODE_DISABLED;
1168        } else if ((priv->secinfo.WPAenabled)
1169                   || (priv->secinfo.WPA2enabled)) {
1170                /* return WPA enabled */
1171                dwrq->flags &= ~IW_ENCODE_DISABLED;
1172                dwrq->flags |= IW_ENCODE_NOKEY;
1173        } else {
1174                dwrq->flags |= IW_ENCODE_DISABLED;
1175        }
1176
1177        mutex_unlock(&priv->lock);
1178
1179        lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
1180               extra[0], extra[1], extra[2],
1181               extra[3], extra[4], extra[5], dwrq->length);
1182
1183        lbs_deb_wext("return flags 0x%x\n", dwrq->flags);
1184
1185        lbs_deb_leave(LBS_DEB_WEXT);
1186        return 0;
1187}
1188
1189/**
1190 *  @brief Set Encryption key (internal)
1191 *
1192 *  @param priv                 A pointer to private card structure
1193 *  @param key_material         A pointer to key material
1194 *  @param key_length           length of key material
1195 *  @param index                key index to set
1196 *  @param set_tx_key           Force set TX key (1 = yes, 0 = no)
1197 *  @return                     0 --success, otherwise fail
1198 */
1199static int lbs_set_wep_key(struct assoc_request *assoc_req,
1200                            const char *key_material,
1201                            u16 key_length,
1202                            u16 index,
1203                            int set_tx_key)
1204{
1205        int ret = 0;
1206        struct enc_key *pkey;
1207
1208        lbs_deb_enter(LBS_DEB_WEXT);
1209
1210        /* Paranoid validation of key index */
1211        if (index > 3) {
1212                ret = -EINVAL;
1213                goto out;
1214        }
1215
1216        /* validate max key length */
1217        if (key_length > KEY_LEN_WEP_104) {
1218                ret = -EINVAL;
1219                goto out;
1220        }
1221
1222        pkey = &assoc_req->wep_keys[index];
1223
1224        if (key_length > 0) {
1225                memset(pkey, 0, sizeof(struct enc_key));
1226                pkey->type = KEY_TYPE_ID_WEP;
1227
1228                /* Standardize the key length */
1229                pkey->len = (key_length > KEY_LEN_WEP_40) ?
1230                                KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
1231                memcpy(pkey->key, key_material, key_length);
1232        }
1233
1234        if (set_tx_key) {
1235                /* Ensure the chosen key is valid */
1236                if (!pkey->len) {
1237                        lbs_deb_wext("key not set, so cannot enable it\n");
1238                        ret = -EINVAL;
1239                        goto out;
1240                }
1241                assoc_req->wep_tx_keyidx = index;
1242        }
1243
1244        assoc_req->secinfo.wep_enabled = 1;
1245
1246out:
1247        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1248        return ret;
1249}
1250
1251static int validate_key_index(u16 def_index, u16 raw_index,
1252                              u16 *out_index, u16 *is_default)
1253{
1254        if (!out_index || !is_default)
1255                return -EINVAL;
1256
1257        /* Verify index if present, otherwise use default TX key index */
1258        if (raw_index > 0) {
1259                if (raw_index > 4)
1260                        return -EINVAL;
1261                *out_index = raw_index - 1;
1262        } else {
1263                *out_index = def_index;
1264                *is_default = 1;
1265        }
1266        return 0;
1267}
1268
1269static void disable_wep(struct assoc_request *assoc_req)
1270{
1271        int i;
1272
1273        lbs_deb_enter(LBS_DEB_WEXT);
1274
1275        /* Set Open System auth mode */
1276        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1277
1278        /* Clear WEP keys and mark WEP as disabled */
1279        assoc_req->secinfo.wep_enabled = 0;
1280        for (i = 0; i < 4; i++)
1281                assoc_req->wep_keys[i].len = 0;
1282
1283        set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1284        set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1285
1286        lbs_deb_leave(LBS_DEB_WEXT);
1287}
1288
1289static void disable_wpa(struct assoc_request *assoc_req)
1290{
1291        lbs_deb_enter(LBS_DEB_WEXT);
1292
1293        memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
1294        assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
1295        set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
1296
1297        memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
1298        assoc_req->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST;
1299        set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
1300
1301        assoc_req->secinfo.WPAenabled = 0;
1302        assoc_req->secinfo.WPA2enabled = 0;
1303        set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1304
1305        lbs_deb_leave(LBS_DEB_WEXT);
1306}
1307
1308/**
1309 *  @brief Set Encryption key
1310 *
1311 *  @param dev                  A pointer to net_device structure
1312 *  @param info                 A pointer to iw_request_info structure
1313 *  @param vwrq                 A pointer to iw_param structure
1314 *  @param extra                A pointer to extra data buf
1315 *  @return                     0 --success, otherwise fail
1316 */
1317static int lbs_set_encode(struct net_device *dev,
1318                    struct iw_request_info *info,
1319                    struct iw_point *dwrq, char *extra)
1320{
1321        int ret = 0;
1322        struct lbs_private *priv = dev->ml_priv;
1323        struct assoc_request * assoc_req;
1324        u16 is_default = 0, index = 0, set_tx_key = 0;
1325
1326        lbs_deb_enter(LBS_DEB_WEXT);
1327
1328        mutex_lock(&priv->lock);
1329        assoc_req = lbs_get_association_request(priv);
1330        if (!assoc_req) {
1331                ret = -ENOMEM;
1332                goto out;
1333        }
1334
1335        if (dwrq->flags & IW_ENCODE_DISABLED) {
1336                disable_wep (assoc_req);
1337                disable_wpa (assoc_req);
1338                goto out;
1339        }
1340
1341        ret = validate_key_index(assoc_req->wep_tx_keyidx,
1342                                 (dwrq->flags & IW_ENCODE_INDEX),
1343                                 &index, &is_default);
1344        if (ret) {
1345                ret = -EINVAL;
1346                goto out;
1347        }
1348
1349        /* If WEP isn't enabled, or if there is no key data but a valid
1350         * index, set the TX key.
1351         */
1352        if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
1353                set_tx_key = 1;
1354
1355        ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
1356        if (ret)
1357                goto out;
1358
1359        if (dwrq->length)
1360                set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1361        if (set_tx_key)
1362                set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
1363
1364        if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1365                assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1366        } else if (dwrq->flags & IW_ENCODE_OPEN) {
1367                assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1368        }
1369
1370out:
1371        if (ret == 0) {
1372                set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1373                lbs_postpone_association_work(priv);
1374        } else {
1375                lbs_cancel_association_work(priv);
1376        }
1377        mutex_unlock(&priv->lock);
1378
1379        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1380        return ret;
1381}
1382
1383/**
1384 *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
1385 *
1386 *  @param dev                  A pointer to net_device structure
1387 *  @param info                 A pointer to iw_request_info structure
1388 *  @param vwrq                 A pointer to iw_param structure
1389 *  @param extra                A pointer to extra data buf
1390 *  @return                     0 on success, otherwise failure
1391 */
1392static int lbs_get_encodeext(struct net_device *dev,
1393                              struct iw_request_info *info,
1394                              struct iw_point *dwrq,
1395                              char *extra)
1396{
1397        int ret = -EINVAL;
1398        struct lbs_private *priv = dev->ml_priv;
1399        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
1400        int index, max_key_len;
1401
1402        lbs_deb_enter(LBS_DEB_WEXT);
1403
1404        max_key_len = dwrq->length - sizeof(*ext);
1405        if (max_key_len < 0)
1406                goto out;
1407
1408        index = dwrq->flags & IW_ENCODE_INDEX;
1409        if (index) {
1410                if (index < 1 || index > 4)
1411                        goto out;
1412                index--;
1413        } else {
1414                index = priv->wep_tx_keyidx;
1415        }
1416
1417        if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
1418            ext->alg != IW_ENCODE_ALG_WEP) {
1419                if (index != 0 || priv->mode != IW_MODE_INFRA)
1420                        goto out;
1421        }
1422
1423        dwrq->flags = index + 1;
1424        memset(ext, 0, sizeof(*ext));
1425
1426        if (   !priv->secinfo.wep_enabled
1427            && !priv->secinfo.WPAenabled
1428            && !priv->secinfo.WPA2enabled) {
1429                ext->alg = IW_ENCODE_ALG_NONE;
1430                ext->key_len = 0;
1431                dwrq->flags |= IW_ENCODE_DISABLED;
1432        } else {
1433                u8 *key = NULL;
1434
1435                if (   priv->secinfo.wep_enabled
1436                    && !priv->secinfo.WPAenabled
1437                    && !priv->secinfo.WPA2enabled) {
1438                        /* WEP */
1439                        ext->alg = IW_ENCODE_ALG_WEP;
1440                        ext->key_len = priv->wep_keys[index].len;
1441                        key = &priv->wep_keys[index].key[0];
1442                } else if (   !priv->secinfo.wep_enabled
1443                           && (priv->secinfo.WPAenabled ||
1444                               priv->secinfo.WPA2enabled)) {
1445                        /* WPA */
1446                        struct enc_key * pkey = NULL;
1447
1448                        if (   priv->wpa_mcast_key.len
1449                            && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
1450                                pkey = &priv->wpa_mcast_key;
1451                        else if (   priv->wpa_unicast_key.len
1452                                 && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
1453                                pkey = &priv->wpa_unicast_key;
1454
1455                        if (pkey) {
1456                                if (pkey->type == KEY_TYPE_ID_AES) {
1457                                        ext->alg = IW_ENCODE_ALG_CCMP;
1458                                } else {
1459                                        ext->alg = IW_ENCODE_ALG_TKIP;
1460                                }
1461                                ext->key_len = pkey->len;
1462                                key = &pkey->key[0];
1463                        } else {
1464                                ext->alg = IW_ENCODE_ALG_TKIP;
1465                                ext->key_len = 0;
1466                        }
1467                } else {
1468                        goto out;
1469                }
1470
1471                if (ext->key_len > max_key_len) {
1472                        ret = -E2BIG;
1473                        goto out;
1474                }
1475
1476                if (ext->key_len)
1477                        memcpy(ext->key, key, ext->key_len);
1478                else
1479                        dwrq->flags |= IW_ENCODE_NOKEY;
1480                dwrq->flags |= IW_ENCODE_ENABLED;
1481        }
1482        ret = 0;
1483
1484out:
1485        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1486        return ret;
1487}
1488
1489/**
1490 *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
1491 *
1492 *  @param dev                  A pointer to net_device structure
1493 *  @param info                 A pointer to iw_request_info structure
1494 *  @param vwrq                 A pointer to iw_param structure
1495 *  @param extra                A pointer to extra data buf
1496 *  @return                     0 --success, otherwise fail
1497 */
1498static int lbs_set_encodeext(struct net_device *dev,
1499                              struct iw_request_info *info,
1500                              struct iw_point *dwrq,
1501                              char *extra)
1502{
1503        int ret = 0;
1504        struct lbs_private *priv = dev->ml_priv;
1505        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
1506        int alg = ext->alg;
1507        struct assoc_request * assoc_req;
1508
1509        lbs_deb_enter(LBS_DEB_WEXT);
1510
1511        mutex_lock(&priv->lock);
1512        assoc_req = lbs_get_association_request(priv);
1513        if (!assoc_req) {
1514                ret = -ENOMEM;
1515                goto out;
1516        }
1517
1518        if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
1519                disable_wep (assoc_req);
1520                disable_wpa (assoc_req);
1521        } else if (alg == IW_ENCODE_ALG_WEP) {
1522                u16 is_default = 0, index, set_tx_key = 0;
1523
1524                ret = validate_key_index(assoc_req->wep_tx_keyidx,
1525                                         (dwrq->flags & IW_ENCODE_INDEX),
1526                                         &index, &is_default);
1527                if (ret)
1528                        goto out;
1529
1530                /* If WEP isn't enabled, or if there is no key data but a valid
1531                 * index, or if the set-TX-key flag was passed, set the TX key.
1532                 */
1533                if (   !assoc_req->secinfo.wep_enabled
1534                    || (dwrq->length == 0 && !is_default)
1535                    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
1536                        set_tx_key = 1;
1537
1538                /* Copy key to driver */
1539                ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
1540                                        set_tx_key);
1541                if (ret)
1542                        goto out;
1543
1544                if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1545                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1546                } else if (dwrq->flags & IW_ENCODE_OPEN) {
1547                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1548                }
1549
1550                /* Mark the various WEP bits as modified */
1551                set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1552                if (dwrq->length)
1553                        set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1554                if (set_tx_key)
1555                        set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
1556        } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
1557                struct enc_key * pkey;
1558
1559                /* validate key length */
1560                if (((alg == IW_ENCODE_ALG_TKIP)
1561                        && (ext->key_len != KEY_LEN_WPA_TKIP))
1562                    || ((alg == IW_ENCODE_ALG_CCMP)
1563                        && (ext->key_len != KEY_LEN_WPA_AES))) {
1564                                lbs_deb_wext("invalid size %d for key of alg "
1565                                       "type %d\n",
1566                                       ext->key_len,
1567                                       alg);
1568                                ret = -EINVAL;
1569                                goto out;
1570                }
1571
1572                if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1573                        pkey = &assoc_req->wpa_mcast_key;
1574                        set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
1575                } else {
1576                        pkey = &assoc_req->wpa_unicast_key;
1577                        set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
1578                }
1579
1580                memset(pkey, 0, sizeof (struct enc_key));
1581                memcpy(pkey->key, ext->key, ext->key_len);
1582                pkey->len = ext->key_len;
1583                if (pkey->len)
1584                        pkey->flags |= KEY_INFO_WPA_ENABLED;
1585
1586                /* Do this after zeroing key structure */
1587                if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1588                        pkey->flags |= KEY_INFO_WPA_MCAST;
1589                } else {
1590                        pkey->flags |= KEY_INFO_WPA_UNICAST;
1591                }
1592
1593                if (alg == IW_ENCODE_ALG_TKIP) {
1594                        pkey->type = KEY_TYPE_ID_TKIP;
1595                } else if (alg == IW_ENCODE_ALG_CCMP) {
1596                        pkey->type = KEY_TYPE_ID_AES;
1597                }
1598
1599                /* If WPA isn't enabled yet, do that now */
1600                if (   assoc_req->secinfo.WPAenabled == 0
1601                    && assoc_req->secinfo.WPA2enabled == 0) {
1602                        assoc_req->secinfo.WPAenabled = 1;
1603                        assoc_req->secinfo.WPA2enabled = 1;
1604                        set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1605                }
1606
1607                /* Only disable wep if necessary: can't waste time here. */
1608                if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
1609                        disable_wep(assoc_req);
1610        }
1611
1612out:
1613        if (ret == 0) {
1614                /* 802.1x and WPA rekeying must happen as quickly as possible,
1615                 * especially during the 4-way handshake; thus if in
1616                 * infrastructure mode, and either (a) 802.1x is enabled or
1617                 * (b) WPA is being used, set the key right away.
1618                 */
1619                if (assoc_req->mode == IW_MODE_INFRA &&
1620                    ((assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_802_1X) ||
1621                     (assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_PSK) ||
1622                      assoc_req->secinfo.WPAenabled ||
1623                      assoc_req->secinfo.WPA2enabled)) {
1624                        lbs_do_association_work(priv);
1625                } else
1626                        lbs_postpone_association_work(priv);
1627        } else {
1628                lbs_cancel_association_work(priv);
1629        }
1630        mutex_unlock(&priv->lock);
1631
1632        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1633        return ret;
1634}
1635
1636
1637static int lbs_set_genie(struct net_device *dev,
1638                          struct iw_request_info *info,
1639                          struct iw_point *dwrq,
1640                          char *extra)
1641{
1642        struct lbs_private *priv = dev->ml_priv;
1643        int ret = 0;
1644        struct assoc_request * assoc_req;
1645
1646        lbs_deb_enter(LBS_DEB_WEXT);
1647
1648        mutex_lock(&priv->lock);
1649        assoc_req = lbs_get_association_request(priv);
1650        if (!assoc_req) {
1651                ret = -ENOMEM;
1652                goto out;
1653        }
1654
1655        if (dwrq->length > MAX_WPA_IE_LEN ||
1656            (dwrq->length && extra == NULL)) {
1657                ret = -EINVAL;
1658                goto out;
1659        }
1660
1661        if (dwrq->length) {
1662                memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
1663                assoc_req->wpa_ie_len = dwrq->length;
1664        } else {
1665                memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
1666                assoc_req->wpa_ie_len = 0;
1667        }
1668
1669out:
1670        if (ret == 0) {
1671                set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
1672                lbs_postpone_association_work(priv);
1673        } else {
1674                lbs_cancel_association_work(priv);
1675        }
1676        mutex_unlock(&priv->lock);
1677
1678        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1679        return ret;
1680}
1681
1682static int lbs_get_genie(struct net_device *dev,
1683                          struct iw_request_info *info,
1684                          struct iw_point *dwrq,
1685                          char *extra)
1686{
1687        int ret = 0;
1688        struct lbs_private *priv = dev->ml_priv;
1689
1690        lbs_deb_enter(LBS_DEB_WEXT);
1691
1692        if (priv->wpa_ie_len == 0) {
1693                dwrq->length = 0;
1694                goto out;
1695        }
1696
1697        if (dwrq->length < priv->wpa_ie_len) {
1698                ret = -E2BIG;
1699                goto out;
1700        }
1701
1702        dwrq->length = priv->wpa_ie_len;
1703        memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
1704
1705out:
1706        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1707        return ret;
1708}
1709
1710
1711static int lbs_set_auth(struct net_device *dev,
1712                         struct iw_request_info *info,
1713                         struct iw_param *dwrq,
1714                         char *extra)
1715{
1716        struct lbs_private *priv = dev->ml_priv;
1717        struct assoc_request * assoc_req;
1718        int ret = 0;
1719        int updated = 0;
1720
1721        lbs_deb_enter(LBS_DEB_WEXT);
1722
1723        mutex_lock(&priv->lock);
1724        assoc_req = lbs_get_association_request(priv);
1725        if (!assoc_req) {
1726                ret = -ENOMEM;
1727                goto out;
1728        }
1729
1730        switch (dwrq->flags & IW_AUTH_INDEX) {
1731        case IW_AUTH_PRIVACY_INVOKED:
1732        case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1733        case IW_AUTH_TKIP_COUNTERMEASURES:
1734        case IW_AUTH_CIPHER_PAIRWISE:
1735        case IW_AUTH_CIPHER_GROUP:
1736        case IW_AUTH_DROP_UNENCRYPTED:
1737                /*
1738                 * libertas does not use these parameters
1739                 */
1740                break;
1741
1742        case IW_AUTH_KEY_MGMT:
1743                assoc_req->secinfo.key_mgmt = dwrq->value;
1744                updated = 1;
1745                break;
1746
1747        case IW_AUTH_WPA_VERSION:
1748                if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
1749                        assoc_req->secinfo.WPAenabled = 0;
1750                        assoc_req->secinfo.WPA2enabled = 0;
1751                        disable_wpa (assoc_req);
1752                }
1753                if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
1754                        assoc_req->secinfo.WPAenabled = 1;
1755                        assoc_req->secinfo.wep_enabled = 0;
1756                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1757                }
1758                if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
1759                        assoc_req->secinfo.WPA2enabled = 1;
1760                        assoc_req->secinfo.wep_enabled = 0;
1761                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1762                }
1763                updated = 1;
1764                break;
1765
1766        case IW_AUTH_80211_AUTH_ALG:
1767                if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
1768                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1769                } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1770                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1771                } else if (dwrq->value & IW_AUTH_ALG_LEAP) {
1772                        assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
1773                } else {
1774                        ret = -EINVAL;
1775                }
1776                updated = 1;
1777                break;
1778
1779        case IW_AUTH_WPA_ENABLED:
1780                if (dwrq->value) {
1781                        if (!assoc_req->secinfo.WPAenabled &&
1782                            !assoc_req->secinfo.WPA2enabled) {
1783                                assoc_req->secinfo.WPAenabled = 1;
1784                                assoc_req->secinfo.WPA2enabled = 1;
1785                                assoc_req->secinfo.wep_enabled = 0;
1786                                assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1787                        }
1788                } else {
1789                        assoc_req->secinfo.WPAenabled = 0;
1790                        assoc_req->secinfo.WPA2enabled = 0;
1791                        disable_wpa (assoc_req);
1792                }
1793                updated = 1;
1794                break;
1795
1796        default:
1797                ret = -EOPNOTSUPP;
1798                break;
1799        }
1800
1801out:
1802        if (ret == 0) {
1803                if (updated)
1804                        set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1805                lbs_postpone_association_work(priv);
1806        } else if (ret != -EOPNOTSUPP) {
1807                lbs_cancel_association_work(priv);
1808        }
1809        mutex_unlock(&priv->lock);
1810
1811        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1812        return ret;
1813}
1814
1815static int lbs_get_auth(struct net_device *dev,
1816                         struct iw_request_info *info,
1817                         struct iw_param *dwrq,
1818                         char *extra)
1819{
1820        int ret = 0;
1821        struct lbs_private *priv = dev->ml_priv;
1822
1823        lbs_deb_enter(LBS_DEB_WEXT);
1824
1825        switch (dwrq->flags & IW_AUTH_INDEX) {
1826        case IW_AUTH_KEY_MGMT:
1827                dwrq->value = priv->secinfo.key_mgmt;
1828                break;
1829
1830        case IW_AUTH_WPA_VERSION:
1831                dwrq->value = 0;
1832                if (priv->secinfo.WPAenabled)
1833                        dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
1834                if (priv->secinfo.WPA2enabled)
1835                        dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
1836                if (!dwrq->value)
1837                        dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
1838                break;
1839
1840        case IW_AUTH_80211_AUTH_ALG:
1841                dwrq->value = priv->secinfo.auth_mode;
1842                break;
1843
1844        case IW_AUTH_WPA_ENABLED:
1845                if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
1846                        dwrq->value = 1;
1847                break;
1848
1849        default:
1850                ret = -EOPNOTSUPP;
1851        }
1852
1853        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1854        return ret;
1855}
1856
1857
1858static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
1859                   struct iw_param *vwrq, char *extra)
1860{
1861        int ret = 0;
1862        struct lbs_private *priv = dev->ml_priv;
1863        s16 dbm = (s16) vwrq->value;
1864
1865        lbs_deb_enter(LBS_DEB_WEXT);
1866
1867        if (vwrq->disabled) {
1868                lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
1869                goto out;
1870        }
1871
1872        if (vwrq->fixed == 0) {
1873                /* User requests automatic tx power control, however there are
1874                 * many auto tx settings.  For now use firmware defaults until
1875                 * we come up with a good way to expose these to the user. */
1876                if (priv->fwrelease < 0x09000000) {
1877                        ret = lbs_set_power_adapt_cfg(priv, 1,
1878                                        POW_ADAPT_DEFAULT_P0,
1879                                        POW_ADAPT_DEFAULT_P1,
1880                                        POW_ADAPT_DEFAULT_P2);
1881                        if (ret)
1882                                goto out;
1883                }
1884                ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
1885                                TPC_DEFAULT_P2, 1);
1886                if (ret)
1887                        goto out;
1888                dbm = priv->txpower_max;
1889        } else {
1890                /* Userspace check in iwrange if it should use dBm or mW,
1891                 * therefore this should never happen... Jean II */
1892                if ((vwrq->flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
1893                        ret = -EOPNOTSUPP;
1894                        goto out;
1895                }
1896
1897                /* Validate requested power level against firmware allowed
1898                 * levels */
1899                if (priv->txpower_min && (dbm < priv->txpower_min)) {
1900                        ret = -EINVAL;
1901                        goto out;
1902                }
1903
1904                if (priv->txpower_max && (dbm > priv->txpower_max)) {
1905                        ret = -EINVAL;
1906                        goto out;
1907                }
1908                if (priv->fwrelease < 0x09000000) {
1909                        ret = lbs_set_power_adapt_cfg(priv, 0,
1910                                        POW_ADAPT_DEFAULT_P0,
1911                                        POW_ADAPT_DEFAULT_P1,
1912                                        POW_ADAPT_DEFAULT_P2);
1913                        if (ret)
1914                                goto out;
1915                }
1916                ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
1917                                TPC_DEFAULT_P2, 1);
1918                if (ret)
1919                        goto out;
1920        }
1921
1922        /* If the radio was off, turn it on */
1923        if (!priv->radio_on) {
1924                ret = lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 1);
1925                if (ret)
1926                        goto out;
1927        }
1928
1929        lbs_deb_wext("txpower set %d dBm\n", dbm);
1930
1931        ret = lbs_set_tx_power(priv, dbm);
1932
1933out:
1934        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1935        return ret;
1936}
1937
1938static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
1939                   struct iw_point *dwrq, char *extra)
1940{
1941        struct lbs_private *priv = dev->ml_priv;
1942
1943        lbs_deb_enter(LBS_DEB_WEXT);
1944
1945        /*
1946         * Note : if dwrq->flags != 0, we should get the relevant SSID from
1947         * the SSID list...
1948         */
1949
1950        /*
1951         * Get the current SSID
1952         */
1953        if (priv->connect_status == LBS_CONNECTED) {
1954                memcpy(extra, priv->curbssparams.ssid,
1955                       priv->curbssparams.ssid_len);
1956                extra[priv->curbssparams.ssid_len] = '\0';
1957        } else {
1958                memset(extra, 0, 32);
1959                extra[priv->curbssparams.ssid_len] = '\0';
1960        }
1961        /*
1962         * If none, we may want to get the one that was set
1963         */
1964
1965        dwrq->length = priv->curbssparams.ssid_len;
1966
1967        dwrq->flags = 1;        /* active */
1968
1969        lbs_deb_leave(LBS_DEB_WEXT);
1970        return 0;
1971}
1972
1973static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
1974                   struct iw_point *dwrq, char *extra)
1975{
1976        struct lbs_private *priv = dev->ml_priv;
1977        int ret = 0;
1978        u8 ssid[IW_ESSID_MAX_SIZE];
1979        u8 ssid_len = 0;
1980        struct assoc_request * assoc_req;
1981        int in_ssid_len = dwrq->length;
1982        DECLARE_SSID_BUF(ssid_buf);
1983
1984        lbs_deb_enter(LBS_DEB_WEXT);
1985
1986        if (!priv->radio_on) {
1987                ret = -EINVAL;
1988                goto out;
1989        }
1990
1991        /* Check the size of the string */
1992        if (in_ssid_len > IW_ESSID_MAX_SIZE) {
1993                ret = -E2BIG;
1994                goto out;
1995        }
1996
1997        memset(&ssid, 0, sizeof(ssid));
1998
1999        if (!dwrq->flags || !in_ssid_len) {
2000                /* "any" SSID requested; leave SSID blank */
2001        } else {
2002                /* Specific SSID requested */
2003                memcpy(&ssid, extra, in_ssid_len);
2004                ssid_len = in_ssid_len;
2005        }
2006
2007        if (!ssid_len) {
2008                lbs_deb_wext("requested any SSID\n");
2009        } else {
2010                lbs_deb_wext("requested SSID '%s'\n",
2011                             print_ssid(ssid_buf, ssid, ssid_len));
2012        }
2013
2014out:
2015        mutex_lock(&priv->lock);
2016        if (ret == 0) {
2017                /* Get or create the current association request */
2018                assoc_req = lbs_get_association_request(priv);
2019                if (!assoc_req) {
2020                        ret = -ENOMEM;
2021                } else {
2022                        /* Copy the SSID to the association request */
2023                        memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
2024                        assoc_req->ssid_len = ssid_len;
2025                        set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
2026                        lbs_postpone_association_work(priv);
2027                }
2028        }
2029
2030        /* Cancel the association request if there was an error */
2031        if (ret != 0) {
2032                lbs_cancel_association_work(priv);
2033        }
2034
2035        mutex_unlock(&priv->lock);
2036
2037        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2038        return ret;
2039}
2040
2041static int lbs_mesh_get_essid(struct net_device *dev,
2042                              struct iw_request_info *info,
2043                              struct iw_point *dwrq, char *extra)
2044{
2045        struct lbs_private *priv = dev->ml_priv;
2046
2047        lbs_deb_enter(LBS_DEB_WEXT);
2048
2049        memcpy(extra, priv->mesh_ssid, priv->mesh_ssid_len);
2050
2051        dwrq->length = priv->mesh_ssid_len;
2052
2053        dwrq->flags = 1;        /* active */
2054
2055        lbs_deb_leave(LBS_DEB_WEXT);
2056        return 0;
2057}
2058
2059static int lbs_mesh_set_essid(struct net_device *dev,
2060                              struct iw_request_info *info,
2061                              struct iw_point *dwrq, char *extra)
2062{
2063        struct lbs_private *priv = dev->ml_priv;
2064        int ret = 0;
2065
2066        lbs_deb_enter(LBS_DEB_WEXT);
2067
2068        if (!priv->radio_on) {
2069                ret = -EINVAL;
2070                goto out;
2071        }
2072
2073        /* Check the size of the string */
2074        if (dwrq->length > IW_ESSID_MAX_SIZE) {
2075                ret = -E2BIG;
2076                goto out;
2077        }
2078
2079        if (!dwrq->flags || !dwrq->length) {
2080                ret = -EINVAL;
2081                goto out;
2082        } else {
2083                /* Specific SSID requested */
2084                memcpy(priv->mesh_ssid, extra, dwrq->length);
2085                priv->mesh_ssid_len = dwrq->length;
2086        }
2087
2088        lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
2089                        priv->curbssparams.channel);
2090 out:
2091        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2092        return ret;
2093}
2094
2095/**
2096 *  @brief Connect to the AP or Ad-hoc Network with specific bssid
2097 *
2098 *  @param dev          A pointer to net_device structure
2099 *  @param info         A pointer to iw_request_info structure
2100 *  @param awrq         A pointer to iw_param structure
2101 *  @param extra        A pointer to extra data buf
2102 *  @return             0 --success, otherwise fail
2103 */
2104static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
2105                 struct sockaddr *awrq, char *extra)
2106{
2107        struct lbs_private *priv = dev->ml_priv;
2108        struct assoc_request * assoc_req;
2109        int ret = 0;
2110
2111        lbs_deb_enter(LBS_DEB_WEXT);
2112
2113        if (!priv->radio_on)
2114                return -EINVAL;
2115
2116        if (awrq->sa_family != ARPHRD_ETHER)
2117                return -EINVAL;
2118
2119        lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data);
2120
2121        mutex_lock(&priv->lock);
2122
2123        /* Get or create the current association request */
2124        assoc_req = lbs_get_association_request(priv);
2125        if (!assoc_req) {
2126                lbs_cancel_association_work(priv);
2127                ret = -ENOMEM;
2128        } else {
2129                /* Copy the BSSID to the association request */
2130                memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
2131                set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
2132                lbs_postpone_association_work(priv);
2133        }
2134
2135        mutex_unlock(&priv->lock);
2136
2137        return ret;
2138}
2139
2140/*
2141 * iwconfig settable callbacks
2142 */
2143static const iw_handler lbs_handler[] = {
2144        (iw_handler) NULL,      /* SIOCSIWCOMMIT */
2145        (iw_handler) lbs_get_name,      /* SIOCGIWNAME */
2146        (iw_handler) NULL,      /* SIOCSIWNWID */
2147        (iw_handler) NULL,      /* SIOCGIWNWID */
2148        (iw_handler) lbs_set_freq,      /* SIOCSIWFREQ */
2149        (iw_handler) lbs_get_freq,      /* SIOCGIWFREQ */
2150        (iw_handler) lbs_set_mode,      /* SIOCSIWMODE */
2151        (iw_handler) lbs_get_mode,      /* SIOCGIWMODE */
2152        (iw_handler) NULL,      /* SIOCSIWSENS */
2153        (iw_handler) NULL,      /* SIOCGIWSENS */
2154        (iw_handler) NULL,      /* SIOCSIWRANGE */
2155        (iw_handler) lbs_get_range,     /* SIOCGIWRANGE */
2156        (iw_handler) NULL,      /* SIOCSIWPRIV */
2157        (iw_handler) NULL,      /* SIOCGIWPRIV */
2158        (iw_handler) NULL,      /* SIOCSIWSTATS */
2159        (iw_handler) NULL,      /* SIOCGIWSTATS */
2160        iw_handler_set_spy,     /* SIOCSIWSPY */
2161        iw_handler_get_spy,     /* SIOCGIWSPY */
2162        iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
2163        iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
2164        (iw_handler) lbs_set_wap,       /* SIOCSIWAP */
2165        (iw_handler) lbs_get_wap,       /* SIOCGIWAP */
2166        (iw_handler) NULL,      /* SIOCSIWMLME */
2167        (iw_handler) NULL,      /* SIOCGIWAPLIST - deprecated */
2168        (iw_handler) lbs_set_scan,      /* SIOCSIWSCAN */
2169        (iw_handler) lbs_get_scan,      /* SIOCGIWSCAN */
2170        (iw_handler) lbs_set_essid,     /* SIOCSIWESSID */
2171        (iw_handler) lbs_get_essid,     /* SIOCGIWESSID */
2172        (iw_handler) lbs_set_nick,      /* SIOCSIWNICKN */
2173        (iw_handler) lbs_get_nick,      /* SIOCGIWNICKN */
2174        (iw_handler) NULL,      /* -- hole -- */
2175        (iw_handler) NULL,      /* -- hole -- */
2176        (iw_handler) lbs_set_rate,      /* SIOCSIWRATE */
2177        (iw_handler) lbs_get_rate,      /* SIOCGIWRATE */
2178        (iw_handler) lbs_set_rts,       /* SIOCSIWRTS */
2179        (iw_handler) lbs_get_rts,       /* SIOCGIWRTS */
2180        (iw_handler) lbs_set_frag,      /* SIOCSIWFRAG */
2181        (iw_handler) lbs_get_frag,      /* SIOCGIWFRAG */
2182        (iw_handler) lbs_set_txpow,     /* SIOCSIWTXPOW */
2183        (iw_handler) lbs_get_txpow,     /* SIOCGIWTXPOW */
2184        (iw_handler) lbs_set_retry,     /* SIOCSIWRETRY */
2185        (iw_handler) lbs_get_retry,     /* SIOCGIWRETRY */
2186        (iw_handler) lbs_set_encode,    /* SIOCSIWENCODE */
2187        (iw_handler) lbs_get_encode,    /* SIOCGIWENCODE */
2188        (iw_handler) lbs_set_power,     /* SIOCSIWPOWER */
2189        (iw_handler) lbs_get_power,     /* SIOCGIWPOWER */
2190        (iw_handler) NULL,      /* -- hole -- */
2191        (iw_handler) NULL,      /* -- hole -- */
2192        (iw_handler) lbs_set_genie,     /* SIOCSIWGENIE */
2193        (iw_handler) lbs_get_genie,     /* SIOCGIWGENIE */
2194        (iw_handler) lbs_set_auth,      /* SIOCSIWAUTH */
2195        (iw_handler) lbs_get_auth,      /* SIOCGIWAUTH */
2196        (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
2197        (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
2198        (iw_handler) NULL,              /* SIOCSIWPMKSA */
2199};
2200
2201static const iw_handler mesh_wlan_handler[] = {
2202        (iw_handler) NULL,      /* SIOCSIWCOMMIT */
2203        (iw_handler) lbs_get_name,      /* SIOCGIWNAME */
2204        (iw_handler) NULL,      /* SIOCSIWNWID */
2205        (iw_handler) NULL,      /* SIOCGIWNWID */
2206        (iw_handler) lbs_mesh_set_freq, /* SIOCSIWFREQ */
2207        (iw_handler) lbs_get_freq,      /* SIOCGIWFREQ */
2208        (iw_handler) NULL,              /* SIOCSIWMODE */
2209        (iw_handler) mesh_wlan_get_mode,        /* SIOCGIWMODE */
2210        (iw_handler) NULL,      /* SIOCSIWSENS */
2211        (iw_handler) NULL,      /* SIOCGIWSENS */
2212        (iw_handler) NULL,      /* SIOCSIWRANGE */
2213        (iw_handler) lbs_get_range,     /* SIOCGIWRANGE */
2214        (iw_handler) NULL,      /* SIOCSIWPRIV */
2215        (iw_handler) NULL,      /* SIOCGIWPRIV */
2216        (iw_handler) NULL,      /* SIOCSIWSTATS */
2217        (iw_handler) NULL,      /* SIOCGIWSTATS */
2218        iw_handler_set_spy,     /* SIOCSIWSPY */
2219        iw_handler_get_spy,     /* SIOCGIWSPY */
2220        iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
2221        iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
2222        (iw_handler) NULL,      /* SIOCSIWAP */
2223        (iw_handler) NULL,      /* SIOCGIWAP */
2224        (iw_handler) NULL,      /* SIOCSIWMLME */
2225        (iw_handler) NULL,      /* SIOCGIWAPLIST - deprecated */
2226        (iw_handler) lbs_set_scan,      /* SIOCSIWSCAN */
2227        (iw_handler) lbs_get_scan,      /* SIOCGIWSCAN */
2228        (iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
2229        (iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
2230        (iw_handler) NULL,              /* SIOCSIWNICKN */
2231        (iw_handler) mesh_get_nick,     /* SIOCGIWNICKN */
2232        (iw_handler) NULL,      /* -- hole -- */
2233        (iw_handler) NULL,      /* -- hole -- */
2234        (iw_handler) lbs_set_rate,      /* SIOCSIWRATE */
2235        (iw_handler) lbs_get_rate,      /* SIOCGIWRATE */
2236        (iw_handler) lbs_set_rts,       /* SIOCSIWRTS */
2237        (iw_handler) lbs_get_rts,       /* SIOCGIWRTS */
2238        (iw_handler) lbs_set_frag,      /* SIOCSIWFRAG */
2239        (iw_handler) lbs_get_frag,      /* SIOCGIWFRAG */
2240        (iw_handler) lbs_set_txpow,     /* SIOCSIWTXPOW */
2241        (iw_handler) lbs_get_txpow,     /* SIOCGIWTXPOW */
2242        (iw_handler) lbs_set_retry,     /* SIOCSIWRETRY */
2243        (iw_handler) lbs_get_retry,     /* SIOCGIWRETRY */
2244        (iw_handler) lbs_set_encode,    /* SIOCSIWENCODE */
2245        (iw_handler) lbs_get_encode,    /* SIOCGIWENCODE */
2246        (iw_handler) lbs_set_power,     /* SIOCSIWPOWER */
2247        (iw_handler) lbs_get_power,     /* SIOCGIWPOWER */
2248        (iw_handler) NULL,      /* -- hole -- */
2249        (iw_handler) NULL,      /* -- hole -- */
2250        (iw_handler) lbs_set_genie,     /* SIOCSIWGENIE */
2251        (iw_handler) lbs_get_genie,     /* SIOCGIWGENIE */
2252        (iw_handler) lbs_set_auth,      /* SIOCSIWAUTH */
2253        (iw_handler) lbs_get_auth,      /* SIOCGIWAUTH */
2254        (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
2255        (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
2256        (iw_handler) NULL,              /* SIOCSIWPMKSA */
2257};
2258struct iw_handler_def lbs_handler_def = {
2259        .num_standard   = ARRAY_SIZE(lbs_handler),
2260        .standard       = (iw_handler *) lbs_handler,
2261        .get_wireless_stats = lbs_get_wireless_stats,
2262};
2263
2264struct iw_handler_def mesh_handler_def = {
2265        .num_standard   = ARRAY_SIZE(mesh_wlan_handler),
2266        .standard       = (iw_handler *) mesh_wlan_handler,
2267        .get_wireless_stats = lbs_get_wireless_stats,
2268};
2269