linux/drivers/staging/rtl8192e/rtllib_softmac_wx.c
<<
>>
Prefs
   1/* IEEE 802.11 SoftMAC layer
   2 * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
   3 *
   4 * Mostly extracted from the rtl8180-sa2400 driver for the
   5 * in-kernel generic ieee802.11 stack.
   6 *
   7 * Some pieces of code might be stolen from ipw2100 driver
   8 * copyright of who own it's copyright ;-)
   9 *
  10 * PS wx handler mostly stolen from hostap, copyright who
  11 * own it's copyright ;-)
  12 *
  13 * released under the GPL
  14 */
  15
  16
  17#include <linux/etherdevice.h>
  18
  19#include "rtllib.h"
  20#include "dot11d.h"
  21/* FIXME: add A freqs */
  22
  23const long rtllib_wlan_frequencies[] = {
  24        2412, 2417, 2422, 2427,
  25        2432, 2437, 2442, 2447,
  26        2452, 2457, 2462, 2467,
  27        2472, 2484
  28};
  29EXPORT_SYMBOL(rtllib_wlan_frequencies);
  30
  31
  32int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a,
  33                             union iwreq_data *wrqu, char *b)
  34{
  35        int ret;
  36        struct iw_freq *fwrq = &wrqu->freq;
  37
  38        down(&ieee->wx_sem);
  39
  40        if (ieee->iw_mode == IW_MODE_INFRA) {
  41                ret = 0;
  42                goto out;
  43        }
  44
  45        /* if setting by freq convert to channel */
  46        if (fwrq->e == 1) {
  47                if ((fwrq->m >= (int) 2.412e8 &&
  48                     fwrq->m <= (int) 2.487e8)) {
  49                        int f = fwrq->m / 100000;
  50                        int c = 0;
  51
  52                        while ((c < 14) && (f != rtllib_wlan_frequencies[c]))
  53                                c++;
  54
  55                        /* hack to fall through */
  56                        fwrq->e = 0;
  57                        fwrq->m = c + 1;
  58                }
  59        }
  60
  61        if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) {
  62                ret = -EOPNOTSUPP;
  63                goto out;
  64
  65        } else { /* Set the channel */
  66
  67                if (ieee->active_channel_map[fwrq->m] != 1) {
  68                        ret = -EINVAL;
  69                        goto out;
  70                }
  71                ieee->current_network.channel = fwrq->m;
  72                ieee->set_chan(ieee->dev, ieee->current_network.channel);
  73
  74                if (ieee->iw_mode == IW_MODE_ADHOC ||
  75                    ieee->iw_mode == IW_MODE_MASTER)
  76                        if (ieee->state == RTLLIB_LINKED) {
  77                                rtllib_stop_send_beacons(ieee);
  78                                rtllib_start_send_beacons(ieee);
  79                        }
  80        }
  81
  82        ret = 0;
  83out:
  84        up(&ieee->wx_sem);
  85        return ret;
  86}
  87EXPORT_SYMBOL(rtllib_wx_set_freq);
  88
  89
  90int rtllib_wx_get_freq(struct rtllib_device *ieee,
  91                             struct iw_request_info *a,
  92                             union iwreq_data *wrqu, char *b)
  93{
  94        struct iw_freq *fwrq = &wrqu->freq;
  95
  96        if (ieee->current_network.channel == 0)
  97                return -1;
  98        fwrq->m = rtllib_wlan_frequencies[ieee->current_network.channel-1] *
  99                  100000;
 100        fwrq->e = 1;
 101        return 0;
 102}
 103EXPORT_SYMBOL(rtllib_wx_get_freq);
 104
 105int rtllib_wx_get_wap(struct rtllib_device *ieee,
 106                            struct iw_request_info *info,
 107                            union iwreq_data *wrqu, char *extra)
 108{
 109        unsigned long flags;
 110
 111        wrqu->ap_addr.sa_family = ARPHRD_ETHER;
 112
 113        if (ieee->iw_mode == IW_MODE_MONITOR)
 114                return -1;
 115
 116        /* We want avoid to give to the user inconsistent infos*/
 117        spin_lock_irqsave(&ieee->lock, flags);
 118
 119        if (ieee->state != RTLLIB_LINKED &&
 120                ieee->state != RTLLIB_LINKED_SCANNING &&
 121                ieee->wap_set == 0)
 122
 123                memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
 124        else
 125                memcpy(wrqu->ap_addr.sa_data,
 126                       ieee->current_network.bssid, ETH_ALEN);
 127
 128        spin_unlock_irqrestore(&ieee->lock, flags);
 129
 130        return 0;
 131}
 132EXPORT_SYMBOL(rtllib_wx_get_wap);
 133
 134
 135int rtllib_wx_set_wap(struct rtllib_device *ieee,
 136                         struct iw_request_info *info,
 137                         union iwreq_data *awrq,
 138                         char *extra)
 139{
 140
 141        int ret = 0;
 142        unsigned long flags;
 143
 144        short ifup = ieee->proto_started;
 145        struct sockaddr *temp = (struct sockaddr *)awrq;
 146
 147        rtllib_stop_scan_syncro(ieee);
 148
 149        down(&ieee->wx_sem);
 150        /* use ifconfig hw ether */
 151        if (ieee->iw_mode == IW_MODE_MASTER) {
 152                ret = -1;
 153                goto out;
 154        }
 155
 156        if (temp->sa_family != ARPHRD_ETHER) {
 157                ret = -EINVAL;
 158                goto out;
 159        }
 160
 161        if (is_zero_ether_addr(temp->sa_data)) {
 162                spin_lock_irqsave(&ieee->lock, flags);
 163                memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
 164                ieee->wap_set = 0;
 165                spin_unlock_irqrestore(&ieee->lock, flags);
 166                ret = -1;
 167                goto out;
 168        }
 169
 170
 171        if (ifup)
 172                rtllib_stop_protocol(ieee, true);
 173
 174        /* just to avoid to give inconsistent infos in the
 175         * get wx method. not really needed otherwise
 176         */
 177        spin_lock_irqsave(&ieee->lock, flags);
 178
 179        ieee->cannot_notify = false;
 180        memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
 181        ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
 182
 183        spin_unlock_irqrestore(&ieee->lock, flags);
 184
 185        if (ifup)
 186                rtllib_start_protocol(ieee);
 187out:
 188        up(&ieee->wx_sem);
 189        return ret;
 190}
 191EXPORT_SYMBOL(rtllib_wx_set_wap);
 192
 193int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a,
 194                         union iwreq_data *wrqu, char *b)
 195{
 196        int len, ret = 0;
 197        unsigned long flags;
 198
 199        if (ieee->iw_mode == IW_MODE_MONITOR)
 200                return -1;
 201
 202        /* We want avoid to give to the user inconsistent infos*/
 203        spin_lock_irqsave(&ieee->lock, flags);
 204
 205        if (ieee->current_network.ssid[0] == '\0' ||
 206                ieee->current_network.ssid_len == 0) {
 207                ret = -1;
 208                goto out;
 209        }
 210
 211        if (ieee->state != RTLLIB_LINKED &&
 212                ieee->state != RTLLIB_LINKED_SCANNING &&
 213                ieee->ssid_set == 0) {
 214                ret = -1;
 215                goto out;
 216        }
 217        len = ieee->current_network.ssid_len;
 218        wrqu->essid.length = len;
 219        strncpy(b, ieee->current_network.ssid, len);
 220        wrqu->essid.flags = 1;
 221
 222out:
 223        spin_unlock_irqrestore(&ieee->lock, flags);
 224
 225        return ret;
 226
 227}
 228EXPORT_SYMBOL(rtllib_wx_get_essid);
 229
 230int rtllib_wx_set_rate(struct rtllib_device *ieee,
 231                             struct iw_request_info *info,
 232                             union iwreq_data *wrqu, char *extra)
 233{
 234
 235        u32 target_rate = wrqu->bitrate.value;
 236
 237        ieee->rate = target_rate/100000;
 238        return 0;
 239}
 240EXPORT_SYMBOL(rtllib_wx_set_rate);
 241
 242int rtllib_wx_get_rate(struct rtllib_device *ieee,
 243                             struct iw_request_info *info,
 244                             union iwreq_data *wrqu, char *extra)
 245{
 246        u32 tmp_rate = 0;
 247        tmp_rate = TxCountToDataRate(ieee,
 248                                     ieee->softmac_stats.CurrentShowTxate);
 249        wrqu->bitrate.value = tmp_rate * 500000;
 250
 251        return 0;
 252}
 253EXPORT_SYMBOL(rtllib_wx_get_rate);
 254
 255
 256int rtllib_wx_set_rts(struct rtllib_device *ieee,
 257                             struct iw_request_info *info,
 258                             union iwreq_data *wrqu, char *extra)
 259{
 260        if (wrqu->rts.disabled || !wrqu->rts.fixed)
 261                ieee->rts = DEFAULT_RTS_THRESHOLD;
 262        else {
 263                if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
 264                                wrqu->rts.value > MAX_RTS_THRESHOLD)
 265                        return -EINVAL;
 266                ieee->rts = wrqu->rts.value;
 267        }
 268        return 0;
 269}
 270EXPORT_SYMBOL(rtllib_wx_set_rts);
 271
 272int rtllib_wx_get_rts(struct rtllib_device *ieee,
 273                             struct iw_request_info *info,
 274                             union iwreq_data *wrqu, char *extra)
 275{
 276        wrqu->rts.value = ieee->rts;
 277        wrqu->rts.fixed = 0;    /* no auto select */
 278        wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
 279        return 0;
 280}
 281EXPORT_SYMBOL(rtllib_wx_get_rts);
 282
 283int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a,
 284                             union iwreq_data *wrqu, char *b)
 285{
 286        int set_mode_status = 0;
 287
 288        rtllib_stop_scan_syncro(ieee);
 289        down(&ieee->wx_sem);
 290        switch (wrqu->mode) {
 291        case IW_MODE_MONITOR:
 292        case IW_MODE_ADHOC:
 293        case IW_MODE_INFRA:
 294                break;
 295        case IW_MODE_AUTO:
 296                wrqu->mode = IW_MODE_INFRA;
 297                break;
 298        default:
 299                set_mode_status = -EINVAL;
 300                goto out;
 301        }
 302
 303        if (wrqu->mode == ieee->iw_mode)
 304                goto out;
 305
 306        if (wrqu->mode == IW_MODE_MONITOR) {
 307                ieee->dev->type = ARPHRD_IEEE80211;
 308                rtllib_EnableNetMonitorMode(ieee->dev, false);
 309        } else {
 310                ieee->dev->type = ARPHRD_ETHER;
 311                if (ieee->iw_mode == IW_MODE_MONITOR)
 312                        rtllib_DisableNetMonitorMode(ieee->dev, false);
 313        }
 314
 315        if (!ieee->proto_started) {
 316                ieee->iw_mode = wrqu->mode;
 317        } else {
 318                rtllib_stop_protocol(ieee, true);
 319                ieee->iw_mode = wrqu->mode;
 320                rtllib_start_protocol(ieee);
 321        }
 322
 323out:
 324        up(&ieee->wx_sem);
 325        return set_mode_status;
 326}
 327EXPORT_SYMBOL(rtllib_wx_set_mode);
 328
 329void rtllib_wx_sync_scan_wq(void *data)
 330{
 331        struct rtllib_device *ieee = container_of_work_rsl(data,
 332                                     struct rtllib_device, wx_sync_scan_wq);
 333        short chan;
 334        enum ht_extchnl_offset chan_offset = 0;
 335        enum ht_channel_width bandwidth = 0;
 336        int b40M = 0;
 337        static int count;
 338
 339        if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) {
 340                rtllib_start_scan_syncro(ieee, 0);
 341                goto out;
 342        }
 343
 344        chan = ieee->current_network.channel;
 345
 346        if (ieee->LeisurePSLeave)
 347                ieee->LeisurePSLeave(ieee->dev);
 348        /* notify AP to be in PS mode */
 349        rtllib_sta_ps_send_null_frame(ieee, 1);
 350        rtllib_sta_ps_send_null_frame(ieee, 1);
 351
 352        rtllib_stop_all_queues(ieee);
 353
 354        if (ieee->data_hard_stop)
 355                ieee->data_hard_stop(ieee->dev);
 356        rtllib_stop_send_beacons(ieee);
 357        ieee->state = RTLLIB_LINKED_SCANNING;
 358        ieee->link_change(ieee->dev);
 359        /* wait for ps packet to be kicked out successfully */
 360        msleep(50);
 361
 362        if (ieee->ScanOperationBackupHandler)
 363                ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP);
 364
 365        if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT &&
 366            ieee->pHTInfo->bCurBW40MHz) {
 367                b40M = 1;
 368                chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
 369                bandwidth = (enum ht_channel_width)ieee->pHTInfo->bCurBW40MHz;
 370                RT_TRACE(COMP_DBG, "Scan in 40M, force to 20M first:%d, %d\n",
 371                         chan_offset, bandwidth);
 372                ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20,
 373                                       HT_EXTCHNL_OFFSET_NO_EXT);
 374        }
 375
 376        rtllib_start_scan_syncro(ieee, 0);
 377
 378        if (b40M) {
 379                RT_TRACE(COMP_DBG, "Scan in 20M, back to 40M\n");
 380                if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
 381                        ieee->set_chan(ieee->dev, chan + 2);
 382                else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
 383                        ieee->set_chan(ieee->dev, chan - 2);
 384                else
 385                        ieee->set_chan(ieee->dev, chan);
 386                ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
 387        } else {
 388                ieee->set_chan(ieee->dev, chan);
 389        }
 390
 391        if (ieee->ScanOperationBackupHandler)
 392                ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE);
 393
 394        ieee->state = RTLLIB_LINKED;
 395        ieee->link_change(ieee->dev);
 396
 397        /* Notify AP that I wake up again */
 398        rtllib_sta_ps_send_null_frame(ieee, 0);
 399
 400        if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 ||
 401            ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) {
 402                ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
 403                ieee->LinkDetectInfo.NumRecvDataInPeriod = 1;
 404        }
 405
 406        if (ieee->data_hard_resume)
 407                ieee->data_hard_resume(ieee->dev);
 408
 409        if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
 410                rtllib_start_send_beacons(ieee);
 411
 412        rtllib_wake_all_queues(ieee);
 413
 414        count = 0;
 415out:
 416        up(&ieee->wx_sem);
 417
 418}
 419
 420int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a,
 421                             union iwreq_data *wrqu, char *b)
 422{
 423        int ret = 0;
 424
 425        down(&ieee->wx_sem);
 426
 427        if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) {
 428                ret = -1;
 429                goto out;
 430        }
 431
 432        if (ieee->state == RTLLIB_LINKED) {
 433                queue_work_rsl(ieee->wq, &ieee->wx_sync_scan_wq);
 434                /* intentionally forget to up sem */
 435                return 0;
 436        }
 437
 438out:
 439        up(&ieee->wx_sem);
 440        return ret;
 441}
 442EXPORT_SYMBOL(rtllib_wx_set_scan);
 443
 444int rtllib_wx_set_essid(struct rtllib_device *ieee,
 445                        struct iw_request_info *a,
 446                        union iwreq_data *wrqu, char *extra)
 447{
 448
 449        int ret = 0, len, i;
 450        short proto_started;
 451        unsigned long flags;
 452
 453        rtllib_stop_scan_syncro(ieee);
 454        down(&ieee->wx_sem);
 455
 456        proto_started = ieee->proto_started;
 457
 458        len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length :
 459               IW_ESSID_MAX_SIZE;
 460
 461        if (len > IW_ESSID_MAX_SIZE) {
 462                ret = -E2BIG;
 463                goto out;
 464        }
 465
 466        if (ieee->iw_mode == IW_MODE_MONITOR) {
 467                ret = -1;
 468                goto out;
 469        }
 470
 471        for (i = 0; i < len; i++) {
 472                if (extra[i] < 0) {
 473                        ret = -1;
 474                        goto out;
 475                }
 476        }
 477
 478        if (proto_started)
 479                rtllib_stop_protocol(ieee, true);
 480
 481
 482        /* this is just to be sure that the GET wx callback
 483         * has consistent infos. not needed otherwise
 484         */
 485        spin_lock_irqsave(&ieee->lock, flags);
 486
 487        if (wrqu->essid.flags && wrqu->essid.length) {
 488                strncpy(ieee->current_network.ssid, extra, len);
 489                ieee->current_network.ssid_len = len;
 490                ieee->cannot_notify = false;
 491                ieee->ssid_set = 1;
 492        } else {
 493                ieee->ssid_set = 0;
 494                ieee->current_network.ssid[0] = '\0';
 495                ieee->current_network.ssid_len = 0;
 496        }
 497        spin_unlock_irqrestore(&ieee->lock, flags);
 498
 499        if (proto_started)
 500                rtllib_start_protocol(ieee);
 501out:
 502        up(&ieee->wx_sem);
 503        return ret;
 504}
 505EXPORT_SYMBOL(rtllib_wx_set_essid);
 506
 507int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a,
 508                       union iwreq_data *wrqu, char *b)
 509{
 510        wrqu->mode = ieee->iw_mode;
 511        return 0;
 512}
 513EXPORT_SYMBOL(rtllib_wx_get_mode);
 514
 515int rtllib_wx_set_rawtx(struct rtllib_device *ieee,
 516                        struct iw_request_info *info,
 517                        union iwreq_data *wrqu, char *extra)
 518{
 519
 520        int *parms = (int *)extra;
 521        int enable = (parms[0] > 0);
 522        short prev = ieee->raw_tx;
 523
 524        down(&ieee->wx_sem);
 525
 526        if (enable)
 527                ieee->raw_tx = 1;
 528        else
 529                ieee->raw_tx = 0;
 530
 531        printk(KERN_INFO"raw TX is %s\n",
 532              ieee->raw_tx ? "enabled" : "disabled");
 533
 534        if (ieee->iw_mode == IW_MODE_MONITOR) {
 535                if (prev == 0 && ieee->raw_tx) {
 536                        if (ieee->data_hard_resume)
 537                                ieee->data_hard_resume(ieee->dev);
 538
 539                        netif_carrier_on(ieee->dev);
 540                }
 541
 542                if (prev && ieee->raw_tx == 1)
 543                        netif_carrier_off(ieee->dev);
 544        }
 545
 546        up(&ieee->wx_sem);
 547
 548        return 0;
 549}
 550EXPORT_SYMBOL(rtllib_wx_set_rawtx);
 551
 552int rtllib_wx_get_name(struct rtllib_device *ieee,
 553                             struct iw_request_info *info,
 554                             union iwreq_data *wrqu, char *extra)
 555{
 556        strcpy(wrqu->name, "802.11");
 557
 558        if (ieee->modulation & RTLLIB_CCK_MODULATION)
 559                strcat(wrqu->name, "b");
 560        if (ieee->modulation & RTLLIB_OFDM_MODULATION)
 561                strcat(wrqu->name, "g");
 562        if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
 563                strcat(wrqu->name, "n");
 564        return 0;
 565}
 566EXPORT_SYMBOL(rtllib_wx_get_name);
 567
 568
 569/* this is mostly stolen from hostap */
 570int rtllib_wx_set_power(struct rtllib_device *ieee,
 571                                 struct iw_request_info *info,
 572                                 union iwreq_data *wrqu, char *extra)
 573{
 574        int ret = 0;
 575
 576        if ((!ieee->sta_wake_up) ||
 577            (!ieee->enter_sleep_state) ||
 578            (!ieee->ps_is_queue_empty)) {
 579                RTLLIB_DEBUG(RTLLIB_DL_ERR, "%s(): PS mode is tried to be use "
 580                             "but driver missed a callback\n\n", __func__);
 581                return -1;
 582        }
 583
 584        down(&ieee->wx_sem);
 585
 586        if (wrqu->power.disabled) {
 587                RT_TRACE(COMP_DBG, "===>%s(): power disable\n", __func__);
 588                ieee->ps = RTLLIB_PS_DISABLED;
 589                goto exit;
 590        }
 591        if (wrqu->power.flags & IW_POWER_TIMEOUT) {
 592                ieee->ps_timeout = wrqu->power.value / 1000;
 593                RT_TRACE(COMP_DBG, "===>%s():ps_timeout is %d\n", __func__,
 594                         ieee->ps_timeout);
 595        }
 596
 597        if (wrqu->power.flags & IW_POWER_PERIOD)
 598                ieee->ps_period = wrqu->power.value / 1000;
 599
 600        switch (wrqu->power.flags & IW_POWER_MODE) {
 601        case IW_POWER_UNICAST_R:
 602                ieee->ps = RTLLIB_PS_UNICAST;
 603                break;
 604        case IW_POWER_MULTICAST_R:
 605                ieee->ps = RTLLIB_PS_MBCAST;
 606                break;
 607        case IW_POWER_ALL_R:
 608                ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST;
 609                break;
 610
 611        case IW_POWER_ON:
 612                break;
 613
 614        default:
 615                ret = -EINVAL;
 616                goto exit;
 617
 618        }
 619exit:
 620        up(&ieee->wx_sem);
 621        return ret;
 622
 623}
 624EXPORT_SYMBOL(rtllib_wx_set_power);
 625
 626/* this is stolen from hostap */
 627int rtllib_wx_get_power(struct rtllib_device *ieee,
 628                                 struct iw_request_info *info,
 629                                 union iwreq_data *wrqu, char *extra)
 630{
 631        int ret = 0;
 632
 633        down(&ieee->wx_sem);
 634
 635        if (ieee->ps == RTLLIB_PS_DISABLED) {
 636                wrqu->power.disabled = 1;
 637                goto exit;
 638        }
 639
 640        wrqu->power.disabled = 0;
 641
 642        if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
 643                wrqu->power.flags = IW_POWER_TIMEOUT;
 644                wrqu->power.value = ieee->ps_timeout * 1000;
 645        } else {
 646                wrqu->power.flags = IW_POWER_PERIOD;
 647                wrqu->power.value = ieee->ps_period * 1000;
 648        }
 649
 650        if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) ==
 651            (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST))
 652                wrqu->power.flags |= IW_POWER_ALL_R;
 653        else if (ieee->ps & RTLLIB_PS_MBCAST)
 654                wrqu->power.flags |= IW_POWER_MULTICAST_R;
 655        else
 656                wrqu->power.flags |= IW_POWER_UNICAST_R;
 657
 658exit:
 659        up(&ieee->wx_sem);
 660        return ret;
 661
 662}
 663EXPORT_SYMBOL(rtllib_wx_get_power);
 664