linux/drivers/staging/rtl8192e/ieee80211/ieee80211_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 "ieee80211.h"
  18#ifdef ENABLE_DOT11D
  19#include "dot11d.h"
  20#endif
  21/* FIXME: add A freqs */
  22
  23const long ieee80211_wlan_frequencies[] = {
  24        2412, 2417, 2422, 2427,
  25        2432, 2437, 2442, 2447,
  26        2452, 2457, 2462, 2467,
  27        2472, 2484
  28};
  29
  30
  31int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
  32                             union iwreq_data *wrqu, char *b)
  33{
  34        int ret;
  35        struct iw_freq *fwrq = & wrqu->freq;
  36
  37        down(&ieee->wx_sem);
  38
  39        if(ieee->iw_mode == IW_MODE_INFRA){
  40                ret = -EOPNOTSUPP;
  41                goto out;
  42        }
  43
  44        /* if setting by freq convert to channel */
  45        if (fwrq->e == 1) {
  46                if ((fwrq->m >= (int) 2.412e8 &&
  47                     fwrq->m <= (int) 2.487e8)) {
  48                        int f = fwrq->m / 100000;
  49                        int c = 0;
  50
  51                        while ((c < 14) && (f != ieee80211_wlan_frequencies[c]))
  52                                c++;
  53
  54                        /* hack to fall through */
  55                        fwrq->e = 0;
  56                        fwrq->m = c + 1;
  57                }
  58        }
  59
  60        if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1 ){
  61                ret = -EOPNOTSUPP;
  62                goto out;
  63
  64        }else { /* Set the channel */
  65
  66#ifdef ENABLE_DOT11D
  67                if (!(GET_DOT11D_INFO(ieee)->channel_map)[fwrq->m]) {
  68                        ret = -EINVAL;
  69                        goto out;
  70                }
  71#endif
  72                ieee->current_network.channel = fwrq->m;
  73                ieee->set_chan(ieee->dev, ieee->current_network.channel);
  74
  75                if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
  76                        if(ieee->state == IEEE80211_LINKED){
  77
  78                        ieee80211_stop_send_beacons(ieee);
  79                        ieee80211_start_send_beacons(ieee);
  80                        }
  81        }
  82
  83        ret = 0;
  84out:
  85        up(&ieee->wx_sem);
  86        return ret;
  87}
  88
  89
  90int ieee80211_wx_get_freq(struct ieee80211_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        //NM 0.7.0 will not accept channel any more.
  99        fwrq->m = ieee80211_wlan_frequencies[ieee->current_network.channel-1] * 100000;
 100        fwrq->e = 1;
 101//      fwrq->m = ieee->current_network.channel;
 102//      fwrq->e = 0;
 103
 104        return 0;
 105}
 106
 107int ieee80211_wx_get_wap(struct ieee80211_device *ieee,
 108                            struct iw_request_info *info,
 109                            union iwreq_data *wrqu, char *extra)
 110{
 111        unsigned long flags;
 112        wrqu->ap_addr.sa_family = ARPHRD_ETHER;
 113
 114        if (ieee->iw_mode == IW_MODE_MONITOR)
 115                return -1;
 116
 117        /* We want avoid to give to the user inconsistent infos*/
 118        spin_lock_irqsave(&ieee->lock, flags);
 119
 120        if (ieee->state != IEEE80211_LINKED &&
 121                ieee->state != IEEE80211_LINKED_SCANNING &&
 122                ieee->wap_set == 0)
 123
 124                memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
 125        else
 126                memcpy(wrqu->ap_addr.sa_data,
 127                       ieee->current_network.bssid, ETH_ALEN);
 128
 129        spin_unlock_irqrestore(&ieee->lock, flags);
 130
 131        return 0;
 132}
 133
 134
 135int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
 136                         struct iw_request_info *info,
 137                         union iwreq_data *awrq,
 138                         char *extra)
 139{
 140
 141        int ret = 0;
 142        u8 zero[] = {0,0,0,0,0,0};
 143        unsigned long flags;
 144
 145        short ifup = ieee->proto_started;//dev->flags & IFF_UP;
 146        struct sockaddr *temp = (struct sockaddr *)awrq;
 147
 148        ieee->sync_scan_hurryup = 1;
 149
 150        down(&ieee->wx_sem);
 151        /* use ifconfig hw ether */
 152        if (ieee->iw_mode == IW_MODE_MASTER){
 153                ret = -1;
 154                goto out;
 155        }
 156
 157        if (temp->sa_family != ARPHRD_ETHER){
 158                ret = -EINVAL;
 159                goto out;
 160        }
 161
 162        if (ifup)
 163                ieee80211_stop_protocol(ieee,true);
 164
 165        /* just to avoid to give inconsistent infos in the
 166         * get wx method. not really needed otherwise
 167         */
 168        spin_lock_irqsave(&ieee->lock, flags);
 169
 170        memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
 171        ieee->wap_set = memcmp(temp->sa_data, zero,ETH_ALEN)!=0;
 172
 173        spin_unlock_irqrestore(&ieee->lock, flags);
 174
 175        if (ifup)
 176                ieee80211_start_protocol(ieee);
 177out:
 178        up(&ieee->wx_sem);
 179        return ret;
 180}
 181
 182 int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b)
 183{
 184        int len,ret = 0;
 185        unsigned long flags;
 186
 187        if (ieee->iw_mode == IW_MODE_MONITOR)
 188                return -1;
 189
 190        /* We want avoid to give to the user inconsistent infos*/
 191        spin_lock_irqsave(&ieee->lock, flags);
 192
 193        if (ieee->current_network.ssid[0] == '\0' ||
 194                ieee->current_network.ssid_len == 0){
 195                ret = -1;
 196                goto out;
 197        }
 198
 199        if (ieee->state != IEEE80211_LINKED &&
 200                ieee->state != IEEE80211_LINKED_SCANNING &&
 201                ieee->ssid_set == 0){
 202                ret = -1;
 203                goto out;
 204        }
 205        len = ieee->current_network.ssid_len;
 206        wrqu->essid.length = len;
 207        strncpy(b,ieee->current_network.ssid,len);
 208        wrqu->essid.flags = 1;
 209
 210out:
 211        spin_unlock_irqrestore(&ieee->lock, flags);
 212
 213        return ret;
 214
 215}
 216
 217int ieee80211_wx_set_rate(struct ieee80211_device *ieee,
 218                             struct iw_request_info *info,
 219                             union iwreq_data *wrqu, char *extra)
 220{
 221
 222        u32 target_rate = wrqu->bitrate.value;
 223
 224        ieee->rate = target_rate/100000;
 225        //FIXME: we might want to limit rate also in management protocols.
 226        return 0;
 227}
 228
 229
 230
 231int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
 232                             struct iw_request_info *info,
 233                             union iwreq_data *wrqu, char *extra)
 234{
 235        u32 tmp_rate;
 236#if 0
 237        printk("===>mode:%d, halfNmode:%d\n", ieee->mode, ieee->bHalfWirelessN24GMode);
 238        if (ieee->mode & (IEEE_A | IEEE_B | IEEE_G))
 239                tmp_rate = ieee->rate;
 240        else if (ieee->mode & IEEE_N_5G)
 241                tmp_rate = 580;
 242        else if (ieee->mode & IEEE_N_24G)
 243        {
 244                if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
 245                        tmp_rate = HTHalfMcsToDataRate(ieee, 15);
 246                else
 247                        tmp_rate = HTMcsToDataRate(ieee, 15);
 248        }
 249#else
 250        tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
 251
 252#endif
 253        wrqu->bitrate.value = tmp_rate * 500000;
 254
 255        return 0;
 256}
 257
 258
 259int ieee80211_wx_set_rts(struct ieee80211_device *ieee,
 260                             struct iw_request_info *info,
 261                             union iwreq_data *wrqu, char *extra)
 262{
 263        if (wrqu->rts.disabled || !wrqu->rts.fixed)
 264                ieee->rts = DEFAULT_RTS_THRESHOLD;
 265        else
 266        {
 267                if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
 268                                wrqu->rts.value > MAX_RTS_THRESHOLD)
 269                        return -EINVAL;
 270                ieee->rts = wrqu->rts.value;
 271        }
 272        return 0;
 273}
 274
 275int ieee80211_wx_get_rts(struct ieee80211_device *ieee,
 276                             struct iw_request_info *info,
 277                             union iwreq_data *wrqu, char *extra)
 278{
 279        wrqu->rts.value = ieee->rts;
 280        wrqu->rts.fixed = 0;    /* no auto select */
 281        wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
 282        return 0;
 283}
 284int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
 285                             union iwreq_data *wrqu, char *b)
 286{
 287
 288        ieee->sync_scan_hurryup = 1;
 289
 290        down(&ieee->wx_sem);
 291
 292        if (wrqu->mode == ieee->iw_mode)
 293                goto out;
 294
 295        if (wrqu->mode == IW_MODE_MONITOR){
 296
 297                ieee->dev->type = ARPHRD_IEEE80211;
 298        }else{
 299                ieee->dev->type = ARPHRD_ETHER;
 300        }
 301
 302        if (!ieee->proto_started){
 303                ieee->iw_mode = wrqu->mode;
 304        }else{
 305                ieee80211_stop_protocol(ieee,true);
 306                ieee->iw_mode = wrqu->mode;
 307                ieee80211_start_protocol(ieee);
 308        }
 309
 310out:
 311        up(&ieee->wx_sem);
 312        return 0;
 313}
 314
 315void ieee80211_wx_sync_scan_wq(struct work_struct *work)
 316{
 317        struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, wx_sync_scan_wq);
 318        short chan;
 319        HT_EXTCHNL_OFFSET chan_offset=0;
 320        HT_CHANNEL_WIDTH bandwidth=0;
 321        int b40M = 0;
 322        static int count = 0;
 323        chan = ieee->current_network.channel;
 324
 325#ifdef ENABLE_LPS
 326        if (ieee->LeisurePSLeave) {
 327                ieee->LeisurePSLeave(ieee->dev);
 328        }
 329
 330        /* notify AP to be in PS mode */
 331        ieee80211_sta_ps_send_null_frame(ieee, 1);
 332        ieee80211_sta_ps_send_null_frame(ieee, 1);
 333#endif
 334
 335        if (ieee->data_hard_stop)
 336                ieee->data_hard_stop(ieee->dev);
 337
 338        ieee80211_stop_send_beacons(ieee);
 339
 340        ieee->state = IEEE80211_LINKED_SCANNING;
 341        ieee->link_change(ieee->dev);
 342        ieee->InitialGainHandler(ieee->dev,IG_Backup);
 343        if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
 344                b40M = 1;
 345                chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
 346                bandwidth = (HT_CHANNEL_WIDTH)ieee->pHTInfo->bCurBW40MHz;
 347                printk("Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
 348                ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
 349                }
 350        ieee80211_start_scan_syncro(ieee);
 351        if (b40M) {
 352                printk("Scan in 20M, back to 40M\n");
 353                if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
 354                        ieee->set_chan(ieee->dev, chan + 2);
 355                else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
 356                        ieee->set_chan(ieee->dev, chan - 2);
 357                else
 358                        ieee->set_chan(ieee->dev, chan);
 359                ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
 360        } else {
 361                ieee->set_chan(ieee->dev, chan);
 362        }
 363
 364        ieee->InitialGainHandler(ieee->dev,IG_Restore);
 365        ieee->state = IEEE80211_LINKED;
 366        ieee->link_change(ieee->dev);
 367
 368#ifdef ENABLE_LPS
 369        /* Notify AP that I wake up again */
 370        ieee80211_sta_ps_send_null_frame(ieee, 0);
 371#endif
 372
 373        // To prevent the immediately calling watch_dog after scan.
 374        if(ieee->LinkDetectInfo.NumRecvBcnInPeriod==0||ieee->LinkDetectInfo.NumRecvDataInPeriod==0 )
 375        {
 376                ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
 377                ieee->LinkDetectInfo.NumRecvDataInPeriod= 1;
 378        }
 379        if (ieee->data_hard_resume)
 380                ieee->data_hard_resume(ieee->dev);
 381
 382        if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
 383                ieee80211_start_send_beacons(ieee);
 384
 385        count = 0;
 386        up(&ieee->wx_sem);
 387
 388}
 389
 390int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
 391                             union iwreq_data *wrqu, char *b)
 392{
 393        int ret = 0;
 394
 395        down(&ieee->wx_sem);
 396
 397        if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)){
 398                ret = -1;
 399                goto out;
 400        }
 401
 402        if ( ieee->state == IEEE80211_LINKED){
 403                queue_work(ieee->wq, &ieee->wx_sync_scan_wq);
 404                /* intentionally forget to up sem */
 405                return 0;
 406        }
 407
 408out:
 409        up(&ieee->wx_sem);
 410        return ret;
 411}
 412
 413int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
 414                              struct iw_request_info *a,
 415                              union iwreq_data *wrqu, char *extra)
 416{
 417
 418        int ret=0,len;
 419        short proto_started;
 420        unsigned long flags;
 421
 422        ieee->sync_scan_hurryup = 1;
 423        down(&ieee->wx_sem);
 424
 425        proto_started = ieee->proto_started;
 426
 427        if (wrqu->essid.length > IW_ESSID_MAX_SIZE){
 428                ret= -E2BIG;
 429                goto out;
 430        }
 431
 432        if (ieee->iw_mode == IW_MODE_MONITOR){
 433                ret= -1;
 434                goto out;
 435        }
 436
 437        if(proto_started){
 438                ieee80211_stop_protocol(ieee,true);
 439        }
 440
 441
 442        /* this is just to be sure that the GET wx callback
 443         * has consisten infos. not needed otherwise
 444         */
 445        spin_lock_irqsave(&ieee->lock, flags);
 446
 447        if (wrqu->essid.flags && wrqu->essid.length) {
 448                //first flush current network.ssid
 449                len = ((wrqu->essid.length-1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length-1) : IW_ESSID_MAX_SIZE;
 450                strncpy(ieee->current_network.ssid, extra, len+1);
 451                ieee->current_network.ssid_len = len+1;
 452                ieee->ssid_set = 1;
 453        }
 454        else{
 455                ieee->ssid_set = 0;
 456                ieee->current_network.ssid[0] = '\0';
 457                ieee->current_network.ssid_len = 0;
 458        }
 459        spin_unlock_irqrestore(&ieee->lock, flags);
 460
 461        if (proto_started)
 462                ieee80211_start_protocol(ieee);
 463out:
 464        up(&ieee->wx_sem);
 465        return ret;
 466}
 467
 468 int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
 469                             union iwreq_data *wrqu, char *b)
 470{
 471
 472        wrqu->mode = ieee->iw_mode;
 473        return 0;
 474}
 475
 476 int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
 477                               struct iw_request_info *info,
 478                               union iwreq_data *wrqu, char *extra)
 479{
 480
 481        int *parms = (int *)extra;
 482        int enable = (parms[0] > 0);
 483        short prev = ieee->raw_tx;
 484
 485        down(&ieee->wx_sem);
 486
 487        if(enable)
 488                ieee->raw_tx = 1;
 489        else
 490                ieee->raw_tx = 0;
 491
 492        printk(KERN_INFO"raw TX is %s\n",
 493              ieee->raw_tx ? "enabled" : "disabled");
 494
 495        if(ieee->iw_mode == IW_MODE_MONITOR)
 496        {
 497                if(prev == 0 && ieee->raw_tx){
 498                        if (ieee->data_hard_resume)
 499                                ieee->data_hard_resume(ieee->dev);
 500
 501                        netif_carrier_on(ieee->dev);
 502                }
 503
 504                if(prev && ieee->raw_tx == 1)
 505                        netif_carrier_off(ieee->dev);
 506        }
 507
 508        up(&ieee->wx_sem);
 509
 510        return 0;
 511}
 512
 513int ieee80211_wx_get_name(struct ieee80211_device *ieee,
 514                             struct iw_request_info *info,
 515                             union iwreq_data *wrqu, char *extra)
 516{
 517        strcpy(wrqu->name, "802.11");
 518        if(ieee->modulation & IEEE80211_CCK_MODULATION)
 519                strcat(wrqu->name, "b");
 520        if(ieee->modulation & IEEE80211_OFDM_MODULATION)
 521                strcat(wrqu->name, "g");
 522        if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
 523                strcat(wrqu->name, "n");
 524        return 0;
 525}
 526
 527
 528/* this is mostly stolen from hostap */
 529int ieee80211_wx_set_power(struct ieee80211_device *ieee,
 530                                 struct iw_request_info *info,
 531                                 union iwreq_data *wrqu, char *extra)
 532{
 533        int ret = 0;
 534#if 1
 535        if(
 536                (!ieee->sta_wake_up) ||
 537        //      (!ieee->ps_request_tx_ack) ||
 538                (!ieee->enter_sleep_state) ||
 539                (!ieee->ps_is_queue_empty)){
 540
 541        //      printk("ERROR. PS mode is tryied to be use but driver missed a callback\n\n");
 542
 543                return -1;
 544        }
 545#endif
 546        down(&ieee->wx_sem);
 547
 548        if (wrqu->power.disabled){
 549                ieee->ps = IEEE80211_PS_DISABLED;
 550                goto exit;
 551        }
 552        if (wrqu->power.flags & IW_POWER_TIMEOUT) {
 553                //ieee->ps_period = wrqu->power.value / 1000;
 554                ieee->ps_timeout = wrqu->power.value / 1000;
 555        }
 556
 557        if (wrqu->power.flags & IW_POWER_PERIOD) {
 558
 559                //ieee->ps_timeout = wrqu->power.value / 1000;
 560                ieee->ps_period = wrqu->power.value / 1000;
 561                //wrq->value / 1024;
 562
 563        }
 564        switch (wrqu->power.flags & IW_POWER_MODE) {
 565        case IW_POWER_UNICAST_R:
 566                ieee->ps = IEEE80211_PS_UNICAST;
 567                break;
 568        case IW_POWER_MULTICAST_R:
 569                ieee->ps = IEEE80211_PS_MBCAST;
 570                break;
 571        case IW_POWER_ALL_R:
 572                ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST;
 573                break;
 574
 575        case IW_POWER_ON:
 576        //      ieee->ps = IEEE80211_PS_DISABLED;
 577                break;
 578
 579        default:
 580                ret = -EINVAL;
 581                goto exit;
 582
 583        }
 584exit:
 585        up(&ieee->wx_sem);
 586        return ret;
 587
 588}
 589
 590/* this is stolen from hostap */
 591int ieee80211_wx_get_power(struct ieee80211_device *ieee,
 592                                 struct iw_request_info *info,
 593                                 union iwreq_data *wrqu, char *extra)
 594{
 595        int ret =0;
 596
 597        down(&ieee->wx_sem);
 598
 599        if(ieee->ps == IEEE80211_PS_DISABLED){
 600                wrqu->power.disabled = 1;
 601                goto exit;
 602        }
 603
 604        wrqu->power.disabled = 0;
 605
 606        if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
 607                wrqu->power.flags = IW_POWER_TIMEOUT;
 608                wrqu->power.value = ieee->ps_timeout * 1000;
 609        } else {
 610//              ret = -EOPNOTSUPP;
 611//              goto exit;
 612                wrqu->power.flags = IW_POWER_PERIOD;
 613                wrqu->power.value = ieee->ps_period * 1000;
 614//ieee->current_network.dtim_period * ieee->current_network.beacon_interval * 1024;
 615        }
 616
 617       if ((ieee->ps & (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST)) == (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST))
 618                wrqu->power.flags |= IW_POWER_ALL_R;
 619        else if (ieee->ps & IEEE80211_PS_MBCAST)
 620                wrqu->power.flags |= IW_POWER_MULTICAST_R;
 621        else
 622                wrqu->power.flags |= IW_POWER_UNICAST_R;
 623
 624exit:
 625        up(&ieee->wx_sem);
 626        return ret;
 627
 628}
 629
 630