linux/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
<<
>>
Prefs
   1/* IEEE 802.11 SoftMAC layer
   2 * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
   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 "ieee80211.h"
  20#include "dot11d.h"
  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};
  29EXPORT_SYMBOL(ieee80211_wlan_frequencies);
  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                if (!(GET_DOT11D_INFO(ieee)->channel_map)[fwrq->m]) {
  67                        ret = -EINVAL;
  68                        goto out;
  69                }
  70                ieee->current_network.channel = fwrq->m;
  71                ieee->set_chan(ieee->dev, ieee->current_network.channel);
  72
  73                if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
  74                        if (ieee->state == IEEE80211_LINKED) {
  75                                ieee80211_stop_send_beacons(ieee);
  76                                ieee80211_start_send_beacons(ieee);
  77                        }
  78        }
  79
  80        ret = 0;
  81out:
  82        up(&ieee->wx_sem);
  83        return ret;
  84}
  85EXPORT_SYMBOL(ieee80211_wx_set_freq);
  86
  87int ieee80211_wx_get_freq(struct ieee80211_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        /* NM 0.7.0 will not accept channel any more. */
  96        fwrq->m = ieee80211_wlan_frequencies[ieee->current_network.channel-1] * 100000;
  97        fwrq->e = 1;
  98        /* fwrq->m = ieee->current_network.channel; */
  99        /* fwrq->e = 0; */
 100
 101        return 0;
 102}
 103EXPORT_SYMBOL(ieee80211_wx_get_freq);
 104
 105int ieee80211_wx_get_wap(struct ieee80211_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 != IEEE80211_LINKED &&
 120                ieee->state != IEEE80211_LINKED_SCANNING &&
 121                ieee->wap_set == 0)
 122
 123                eth_zero_addr(wrqu->ap_addr.sa_data);
 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(ieee80211_wx_get_wap);
 133
 134int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
 135                         struct iw_request_info *info,
 136                         union iwreq_data *awrq,
 137                         char *extra)
 138{
 139
 140        int ret = 0;
 141        unsigned long flags;
 142
 143        short ifup = ieee->proto_started; /* dev->flags & IFF_UP; */
 144        struct sockaddr *temp = (struct sockaddr *)awrq;
 145
 146        ieee->sync_scan_hurryup = 1;
 147
 148        down(&ieee->wx_sem);
 149        /* use ifconfig hw ether */
 150        if (ieee->iw_mode == IW_MODE_MASTER) {
 151                ret = -1;
 152                goto out;
 153        }
 154
 155        if (temp->sa_family != ARPHRD_ETHER) {
 156                ret = -EINVAL;
 157                goto out;
 158        }
 159
 160        if (ifup)
 161                ieee80211_stop_protocol(ieee);
 162
 163        /* just to avoid to give inconsistent infos in the
 164         * get wx method. not really needed otherwise
 165         */
 166        spin_lock_irqsave(&ieee->lock, flags);
 167
 168        memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
 169        ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
 170
 171        spin_unlock_irqrestore(&ieee->lock, flags);
 172
 173        if (ifup)
 174                ieee80211_start_protocol(ieee);
 175out:
 176        up(&ieee->wx_sem);
 177        return ret;
 178}
 179EXPORT_SYMBOL(ieee80211_wx_set_wap);
 180
 181int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a, union iwreq_data *wrqu, char *b)
 182{
 183        int len, ret = 0;
 184        unsigned long flags;
 185
 186        if (ieee->iw_mode == IW_MODE_MONITOR)
 187                return -1;
 188
 189        /* We want avoid to give to the user inconsistent infos*/
 190        spin_lock_irqsave(&ieee->lock, flags);
 191
 192        if (ieee->current_network.ssid[0] == '\0' ||
 193                ieee->current_network.ssid_len == 0) {
 194                ret = -1;
 195                goto out;
 196        }
 197
 198        if (ieee->state != IEEE80211_LINKED &&
 199                ieee->state != IEEE80211_LINKED_SCANNING &&
 200                ieee->ssid_set == 0) {
 201                ret = -1;
 202                goto out;
 203        }
 204        len = ieee->current_network.ssid_len;
 205        wrqu->essid.length = len;
 206        strncpy(b, ieee->current_network.ssid, len);
 207        wrqu->essid.flags = 1;
 208
 209out:
 210        spin_unlock_irqrestore(&ieee->lock, flags);
 211
 212        return ret;
 213
 214}
 215EXPORT_SYMBOL(ieee80211_wx_get_essid);
 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}
 228EXPORT_SYMBOL(ieee80211_wx_set_rate);
 229
 230int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
 231                             struct iw_request_info *info,
 232                             union iwreq_data *wrqu, char *extra)
 233{
 234        u32 tmp_rate;
 235
 236        tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
 237
 238        wrqu->bitrate.value = tmp_rate * 500000;
 239
 240        return 0;
 241}
 242EXPORT_SYMBOL(ieee80211_wx_get_rate);
 243
 244int ieee80211_wx_set_rts(struct ieee80211_device *ieee,
 245                             struct iw_request_info *info,
 246                             union iwreq_data *wrqu, char *extra)
 247{
 248        if (wrqu->rts.disabled || !wrqu->rts.fixed)
 249                ieee->rts = DEFAULT_RTS_THRESHOLD;
 250        else {
 251                if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
 252                                wrqu->rts.value > MAX_RTS_THRESHOLD)
 253                        return -EINVAL;
 254                ieee->rts = wrqu->rts.value;
 255        }
 256        return 0;
 257}
 258EXPORT_SYMBOL(ieee80211_wx_set_rts);
 259
 260int ieee80211_wx_get_rts(struct ieee80211_device *ieee,
 261                             struct iw_request_info *info,
 262                             union iwreq_data *wrqu, char *extra)
 263{
 264        wrqu->rts.value = ieee->rts;
 265        wrqu->rts.fixed = 0;    /* no auto select */
 266        wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
 267        return 0;
 268}
 269EXPORT_SYMBOL(ieee80211_wx_get_rts);
 270
 271int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
 272                             union iwreq_data *wrqu, char *b)
 273{
 274
 275        ieee->sync_scan_hurryup = 1;
 276
 277        down(&ieee->wx_sem);
 278
 279        if (wrqu->mode == ieee->iw_mode)
 280                goto out;
 281
 282        if (wrqu->mode == IW_MODE_MONITOR)
 283                ieee->dev->type = ARPHRD_IEEE80211;
 284        else
 285                ieee->dev->type = ARPHRD_ETHER;
 286
 287        if (!ieee->proto_started) {
 288                ieee->iw_mode = wrqu->mode;
 289        } else {
 290                ieee80211_stop_protocol(ieee);
 291                ieee->iw_mode = wrqu->mode;
 292                ieee80211_start_protocol(ieee);
 293        }
 294
 295out:
 296        up(&ieee->wx_sem);
 297        return 0;
 298}
 299EXPORT_SYMBOL(ieee80211_wx_set_mode);
 300
 301void ieee80211_wx_sync_scan_wq(struct work_struct *work)
 302{
 303        struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, wx_sync_scan_wq);
 304        short chan;
 305        HT_EXTCHNL_OFFSET chan_offset = 0;
 306        HT_CHANNEL_WIDTH bandwidth = 0;
 307        int b40M = 0;
 308
 309        chan = ieee->current_network.channel;
 310        netif_carrier_off(ieee->dev);
 311
 312        if (ieee->data_hard_stop)
 313                ieee->data_hard_stop(ieee->dev);
 314
 315        ieee80211_stop_send_beacons(ieee);
 316
 317        ieee->state = IEEE80211_LINKED_SCANNING;
 318        ieee->link_change(ieee->dev);
 319        ieee->InitialGainHandler(ieee->dev, IG_Backup);
 320        if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
 321                b40M = 1;
 322                chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
 323                bandwidth = (HT_CHANNEL_WIDTH)ieee->pHTInfo->bCurBW40MHz;
 324                printk("Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
 325                ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
 326                }
 327        ieee80211_start_scan_syncro(ieee);
 328        if (b40M) {
 329                printk("Scan in 20M, back to 40M\n");
 330                if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
 331                        ieee->set_chan(ieee->dev, chan + 2);
 332                else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
 333                        ieee->set_chan(ieee->dev, chan - 2);
 334                else
 335                        ieee->set_chan(ieee->dev, chan);
 336                ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
 337        } else {
 338                ieee->set_chan(ieee->dev, chan);
 339        }
 340
 341        ieee->InitialGainHandler(ieee->dev, IG_Restore);
 342        ieee->state = IEEE80211_LINKED;
 343        ieee->link_change(ieee->dev);
 344        /* To prevent the immediately calling watch_dog after scan. */
 345        if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 || ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) {
 346                ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
 347                ieee->LinkDetectInfo.NumRecvDataInPeriod = 1;
 348        }
 349        if (ieee->data_hard_resume)
 350                ieee->data_hard_resume(ieee->dev);
 351
 352        if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
 353                ieee80211_start_send_beacons(ieee);
 354
 355        netif_carrier_on(ieee->dev);
 356        up(&ieee->wx_sem);
 357
 358}
 359
 360int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
 361                             union iwreq_data *wrqu, char *b)
 362{
 363        int ret = 0;
 364
 365        down(&ieee->wx_sem);
 366
 367        if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) {
 368                ret = -1;
 369                goto out;
 370        }
 371
 372        if (ieee->state == IEEE80211_LINKED) {
 373                queue_work(ieee->wq, &ieee->wx_sync_scan_wq);
 374                /* intentionally forget to up sem */
 375                return 0;
 376        }
 377
 378out:
 379        up(&ieee->wx_sem);
 380        return ret;
 381}
 382EXPORT_SYMBOL(ieee80211_wx_set_scan);
 383
 384int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
 385                              struct iw_request_info *a,
 386                              union iwreq_data *wrqu, char *extra)
 387{
 388
 389        int ret = 0, len;
 390        short proto_started;
 391        unsigned long flags;
 392
 393        ieee->sync_scan_hurryup = 1;
 394        down(&ieee->wx_sem);
 395
 396        proto_started = ieee->proto_started;
 397
 398        if (wrqu->essid.length > IW_ESSID_MAX_SIZE) {
 399                ret = -E2BIG;
 400                goto out;
 401        }
 402
 403        if (ieee->iw_mode == IW_MODE_MONITOR) {
 404                ret = -1;
 405                goto out;
 406        }
 407
 408        if (proto_started)
 409                ieee80211_stop_protocol(ieee);
 410
 411
 412        /* this is just to be sure that the GET wx callback
 413         * has consisten infos. not needed otherwise
 414         */
 415        spin_lock_irqsave(&ieee->lock, flags);
 416
 417        if (wrqu->essid.flags && wrqu->essid.length) {
 418                /* first flush current network.ssid */
 419                len = ((wrqu->essid.length-1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length-1) : IW_ESSID_MAX_SIZE;
 420                strncpy(ieee->current_network.ssid, extra, len+1);
 421                ieee->current_network.ssid_len = len+1;
 422                ieee->ssid_set = 1;
 423        } else {
 424                ieee->ssid_set = 0;
 425                ieee->current_network.ssid[0] = '\0';
 426                ieee->current_network.ssid_len = 0;
 427        }
 428        spin_unlock_irqrestore(&ieee->lock, flags);
 429
 430        if (proto_started)
 431                ieee80211_start_protocol(ieee);
 432out:
 433        up(&ieee->wx_sem);
 434        return ret;
 435}
 436EXPORT_SYMBOL(ieee80211_wx_set_essid);
 437
 438int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
 439                             union iwreq_data *wrqu, char *b)
 440{
 441
 442        wrqu->mode = ieee->iw_mode;
 443        return 0;
 444}
 445EXPORT_SYMBOL(ieee80211_wx_get_mode);
 446
 447int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
 448                               struct iw_request_info *info,
 449                               union iwreq_data *wrqu, char *extra)
 450{
 451
 452        int *parms = (int *)extra;
 453        int enable = (parms[0] > 0);
 454        short prev = ieee->raw_tx;
 455
 456        down(&ieee->wx_sem);
 457
 458        if (enable)
 459                ieee->raw_tx = 1;
 460        else
 461                ieee->raw_tx = 0;
 462
 463        printk(KERN_INFO"raw TX is %s\n",
 464              ieee->raw_tx ? "enabled" : "disabled");
 465
 466        if (ieee->iw_mode == IW_MODE_MONITOR) {
 467                if (prev == 0 && ieee->raw_tx) {
 468                        if (ieee->data_hard_resume)
 469                                ieee->data_hard_resume(ieee->dev);
 470
 471                        netif_carrier_on(ieee->dev);
 472                }
 473
 474                if (prev && ieee->raw_tx == 1)
 475                        netif_carrier_off(ieee->dev);
 476        }
 477
 478        up(&ieee->wx_sem);
 479
 480        return 0;
 481}
 482EXPORT_SYMBOL(ieee80211_wx_set_rawtx);
 483
 484int ieee80211_wx_get_name(struct ieee80211_device *ieee,
 485                             struct iw_request_info *info,
 486                             union iwreq_data *wrqu, char *extra)
 487{
 488        strlcpy(wrqu->name, "802.11", IFNAMSIZ);
 489        if (ieee->modulation & IEEE80211_CCK_MODULATION) {
 490                strlcat(wrqu->name, "b", IFNAMSIZ);
 491                if (ieee->modulation & IEEE80211_OFDM_MODULATION)
 492                        strlcat(wrqu->name, "/g", IFNAMSIZ);
 493        } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
 494                strlcat(wrqu->name, "g", IFNAMSIZ);
 495        }
 496
 497        if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
 498                strlcat(wrqu->name, "/n", IFNAMSIZ);
 499
 500        if ((ieee->state == IEEE80211_LINKED) ||
 501            (ieee->state == IEEE80211_LINKED_SCANNING))
 502                strlcat(wrqu->name, " linked", IFNAMSIZ);
 503        else if (ieee->state != IEEE80211_NOLINK)
 504                strlcat(wrqu->name, " link..", IFNAMSIZ);
 505
 506        return 0;
 507}
 508EXPORT_SYMBOL(ieee80211_wx_get_name);
 509
 510/* this is mostly stolen from hostap */
 511int ieee80211_wx_set_power(struct ieee80211_device *ieee,
 512                                 struct iw_request_info *info,
 513                                 union iwreq_data *wrqu, char *extra)
 514{
 515        int ret = 0;
 516
 517        down(&ieee->wx_sem);
 518
 519        if (wrqu->power.disabled) {
 520                ieee->ps = IEEE80211_PS_DISABLED;
 521                goto exit;
 522        }
 523        if (wrqu->power.flags & IW_POWER_TIMEOUT) {
 524                /* ieee->ps_period = wrqu->power.value / 1000; */
 525                ieee->ps_timeout = wrqu->power.value / 1000;
 526        }
 527
 528        if (wrqu->power.flags & IW_POWER_PERIOD) {
 529
 530                /* ieee->ps_timeout = wrqu->power.value / 1000; */
 531                ieee->ps_period = wrqu->power.value / 1000;
 532                /* wrq->value / 1024; */
 533
 534        }
 535        switch (wrqu->power.flags & IW_POWER_MODE) {
 536        case IW_POWER_UNICAST_R:
 537                ieee->ps = IEEE80211_PS_UNICAST;
 538                break;
 539        case IW_POWER_MULTICAST_R:
 540                ieee->ps = IEEE80211_PS_MBCAST;
 541                break;
 542        case IW_POWER_ALL_R:
 543                ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST;
 544                break;
 545
 546        case IW_POWER_ON:
 547                /* ieee->ps = IEEE80211_PS_DISABLED; */
 548                break;
 549
 550        default:
 551                ret = -EINVAL;
 552                goto exit;
 553
 554        }
 555exit:
 556        up(&ieee->wx_sem);
 557        return ret;
 558
 559}
 560EXPORT_SYMBOL(ieee80211_wx_set_power);
 561
 562/* this is stolen from hostap */
 563int ieee80211_wx_get_power(struct ieee80211_device *ieee,
 564                                 struct iw_request_info *info,
 565                                 union iwreq_data *wrqu, char *extra)
 566{
 567        down(&ieee->wx_sem);
 568
 569        if (ieee->ps == IEEE80211_PS_DISABLED) {
 570                wrqu->power.disabled = 1;
 571                goto exit;
 572        }
 573
 574        wrqu->power.disabled = 0;
 575
 576        if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
 577                wrqu->power.flags = IW_POWER_TIMEOUT;
 578                wrqu->power.value = ieee->ps_timeout * 1000;
 579        } else {
 580                /* ret = -EOPNOTSUPP; */
 581                /* goto exit; */
 582                wrqu->power.flags = IW_POWER_PERIOD;
 583                wrqu->power.value = ieee->ps_period * 1000;
 584                /* ieee->current_network.dtim_period * ieee->current_network.beacon_interval * 1024; */
 585        }
 586
 587        if ((ieee->ps & (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST)) == (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST))
 588                wrqu->power.flags |= IW_POWER_ALL_R;
 589        else if (ieee->ps & IEEE80211_PS_MBCAST)
 590                wrqu->power.flags |= IW_POWER_MULTICAST_R;
 591        else
 592                wrqu->power.flags |= IW_POWER_UNICAST_R;
 593
 594exit:
 595        up(&ieee->wx_sem);
 596        return 0;
 597
 598}
 599EXPORT_SYMBOL(ieee80211_wx_get_power);
 600