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