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