linux/net/wireless/chan.c
<<
>>
Prefs
   1/*
   2 * This file contains helper code to handle channel
   3 * settings and keeping track of what is possible at
   4 * any point in time.
   5 *
   6 * Copyright 2009       Johannes Berg <johannes@sipsolutions.net>
   7 */
   8
   9#include <linux/export.h>
  10#include <net/cfg80211.h>
  11#include "core.h"
  12#include "rdev-ops.h"
  13
  14void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
  15                             struct ieee80211_channel *chan,
  16                             enum nl80211_channel_type chan_type)
  17{
  18        if (WARN_ON(!chan))
  19                return;
  20
  21        chandef->chan = chan;
  22        chandef->center_freq2 = 0;
  23
  24        switch (chan_type) {
  25        case NL80211_CHAN_NO_HT:
  26                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
  27                chandef->center_freq1 = chan->center_freq;
  28                break;
  29        case NL80211_CHAN_HT20:
  30                chandef->width = NL80211_CHAN_WIDTH_20;
  31                chandef->center_freq1 = chan->center_freq;
  32                break;
  33        case NL80211_CHAN_HT40PLUS:
  34                chandef->width = NL80211_CHAN_WIDTH_40;
  35                chandef->center_freq1 = chan->center_freq + 10;
  36                break;
  37        case NL80211_CHAN_HT40MINUS:
  38                chandef->width = NL80211_CHAN_WIDTH_40;
  39                chandef->center_freq1 = chan->center_freq - 10;
  40                break;
  41        default:
  42                WARN_ON(1);
  43        }
  44}
  45EXPORT_SYMBOL(cfg80211_chandef_create);
  46
  47bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
  48{
  49        u32 control_freq;
  50
  51        if (!chandef->chan)
  52                return false;
  53
  54        control_freq = chandef->chan->center_freq;
  55
  56        switch (chandef->width) {
  57        case NL80211_CHAN_WIDTH_5:
  58        case NL80211_CHAN_WIDTH_10:
  59        case NL80211_CHAN_WIDTH_20:
  60        case NL80211_CHAN_WIDTH_20_NOHT:
  61                if (chandef->center_freq1 != control_freq)
  62                        return false;
  63                if (chandef->center_freq2)
  64                        return false;
  65                break;
  66        case NL80211_CHAN_WIDTH_40:
  67                if (chandef->center_freq1 != control_freq + 10 &&
  68                    chandef->center_freq1 != control_freq - 10)
  69                        return false;
  70                if (chandef->center_freq2)
  71                        return false;
  72                break;
  73        case NL80211_CHAN_WIDTH_80P80:
  74                if (chandef->center_freq1 != control_freq + 30 &&
  75                    chandef->center_freq1 != control_freq + 10 &&
  76                    chandef->center_freq1 != control_freq - 10 &&
  77                    chandef->center_freq1 != control_freq - 30)
  78                        return false;
  79                if (!chandef->center_freq2)
  80                        return false;
  81                /* adjacent is not allowed -- that's a 160 MHz channel */
  82                if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
  83                    chandef->center_freq2 - chandef->center_freq1 == 80)
  84                        return false;
  85                break;
  86        case NL80211_CHAN_WIDTH_80:
  87                if (chandef->center_freq1 != control_freq + 30 &&
  88                    chandef->center_freq1 != control_freq + 10 &&
  89                    chandef->center_freq1 != control_freq - 10 &&
  90                    chandef->center_freq1 != control_freq - 30)
  91                        return false;
  92                if (chandef->center_freq2)
  93                        return false;
  94                break;
  95        case NL80211_CHAN_WIDTH_160:
  96                if (chandef->center_freq1 != control_freq + 70 &&
  97                    chandef->center_freq1 != control_freq + 50 &&
  98                    chandef->center_freq1 != control_freq + 30 &&
  99                    chandef->center_freq1 != control_freq + 10 &&
 100                    chandef->center_freq1 != control_freq - 10 &&
 101                    chandef->center_freq1 != control_freq - 30 &&
 102                    chandef->center_freq1 != control_freq - 50 &&
 103                    chandef->center_freq1 != control_freq - 70)
 104                        return false;
 105                if (chandef->center_freq2)
 106                        return false;
 107                break;
 108        default:
 109                return false;
 110        }
 111
 112        return true;
 113}
 114EXPORT_SYMBOL(cfg80211_chandef_valid);
 115
 116static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
 117                                  int *pri40, int *pri80)
 118{
 119        int tmp;
 120
 121        switch (c->width) {
 122        case NL80211_CHAN_WIDTH_40:
 123                *pri40 = c->center_freq1;
 124                *pri80 = 0;
 125                break;
 126        case NL80211_CHAN_WIDTH_80:
 127        case NL80211_CHAN_WIDTH_80P80:
 128                *pri80 = c->center_freq1;
 129                /* n_P20 */
 130                tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
 131                /* n_P40 */
 132                tmp /= 2;
 133                /* freq_P40 */
 134                *pri40 = c->center_freq1 - 20 + 40 * tmp;
 135                break;
 136        case NL80211_CHAN_WIDTH_160:
 137                /* n_P20 */
 138                tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
 139                /* n_P40 */
 140                tmp /= 2;
 141                /* freq_P40 */
 142                *pri40 = c->center_freq1 - 60 + 40 * tmp;
 143                /* n_P80 */
 144                tmp /= 2;
 145                *pri80 = c->center_freq1 - 40 + 80 * tmp;
 146                break;
 147        default:
 148                WARN_ON_ONCE(1);
 149        }
 150}
 151
 152static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
 153{
 154        int width;
 155
 156        switch (c->width) {
 157        case NL80211_CHAN_WIDTH_5:
 158                width = 5;
 159                break;
 160        case NL80211_CHAN_WIDTH_10:
 161                width = 10;
 162                break;
 163        case NL80211_CHAN_WIDTH_20:
 164        case NL80211_CHAN_WIDTH_20_NOHT:
 165                width = 20;
 166                break;
 167        case NL80211_CHAN_WIDTH_40:
 168                width = 40;
 169                break;
 170        case NL80211_CHAN_WIDTH_80P80:
 171        case NL80211_CHAN_WIDTH_80:
 172                width = 80;
 173                break;
 174        case NL80211_CHAN_WIDTH_160:
 175                width = 160;
 176                break;
 177        default:
 178                WARN_ON_ONCE(1);
 179                return -1;
 180        }
 181        return width;
 182}
 183
 184const struct cfg80211_chan_def *
 185cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
 186                            const struct cfg80211_chan_def *c2)
 187{
 188        u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
 189
 190        /* If they are identical, return */
 191        if (cfg80211_chandef_identical(c1, c2))
 192                return c1;
 193
 194        /* otherwise, must have same control channel */
 195        if (c1->chan != c2->chan)
 196                return NULL;
 197
 198        /*
 199         * If they have the same width, but aren't identical,
 200         * then they can't be compatible.
 201         */
 202        if (c1->width == c2->width)
 203                return NULL;
 204
 205        /*
 206         * can't be compatible if one of them is 5 or 10 MHz,
 207         * but they don't have the same width.
 208         */
 209        if (c1->width == NL80211_CHAN_WIDTH_5 ||
 210            c1->width == NL80211_CHAN_WIDTH_10 ||
 211            c2->width == NL80211_CHAN_WIDTH_5 ||
 212            c2->width == NL80211_CHAN_WIDTH_10)
 213                return NULL;
 214
 215        if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
 216            c1->width == NL80211_CHAN_WIDTH_20)
 217                return c2;
 218
 219        if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
 220            c2->width == NL80211_CHAN_WIDTH_20)
 221                return c1;
 222
 223        chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
 224        chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
 225
 226        if (c1_pri40 != c2_pri40)
 227                return NULL;
 228
 229        WARN_ON(!c1_pri80 && !c2_pri80);
 230        if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
 231                return NULL;
 232
 233        if (c1->width > c2->width)
 234                return c1;
 235        return c2;
 236}
 237EXPORT_SYMBOL(cfg80211_chandef_compatible);
 238
 239static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
 240                                         u32 bandwidth,
 241                                         enum nl80211_dfs_state dfs_state)
 242{
 243        struct ieee80211_channel *c;
 244        u32 freq;
 245
 246        for (freq = center_freq - bandwidth/2 + 10;
 247             freq <= center_freq + bandwidth/2 - 10;
 248             freq += 20) {
 249                c = ieee80211_get_channel(wiphy, freq);
 250                if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
 251                        continue;
 252
 253                c->dfs_state = dfs_state;
 254                c->dfs_state_entered = jiffies;
 255        }
 256}
 257
 258void cfg80211_set_dfs_state(struct wiphy *wiphy,
 259                            const struct cfg80211_chan_def *chandef,
 260                            enum nl80211_dfs_state dfs_state)
 261{
 262        int width;
 263
 264        if (WARN_ON(!cfg80211_chandef_valid(chandef)))
 265                return;
 266
 267        width = cfg80211_chandef_get_width(chandef);
 268        if (width < 0)
 269                return;
 270
 271        cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
 272                                     width, dfs_state);
 273
 274        if (!chandef->center_freq2)
 275                return;
 276        cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
 277                                     width, dfs_state);
 278}
 279
 280static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
 281                                            u32 center_freq,
 282                                            u32 bandwidth)
 283{
 284        struct ieee80211_channel *c;
 285        u32 freq, start_freq, end_freq;
 286
 287        if (bandwidth <= 20) {
 288                start_freq = center_freq;
 289                end_freq = center_freq;
 290        } else {
 291                start_freq = center_freq - bandwidth/2 + 10;
 292                end_freq = center_freq + bandwidth/2 - 10;
 293        }
 294
 295        for (freq = start_freq; freq <= end_freq; freq += 20) {
 296                c = ieee80211_get_channel(wiphy, freq);
 297                if (!c)
 298                        return -EINVAL;
 299
 300                if (c->flags & IEEE80211_CHAN_RADAR)
 301                        return 1;
 302        }
 303        return 0;
 304}
 305
 306
 307int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
 308                                  const struct cfg80211_chan_def *chandef)
 309{
 310        int width;
 311        int r;
 312
 313        if (WARN_ON(!cfg80211_chandef_valid(chandef)))
 314                return -EINVAL;
 315
 316        width = cfg80211_chandef_get_width(chandef);
 317        if (width < 0)
 318                return -EINVAL;
 319
 320        r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
 321                                            width);
 322        if (r)
 323                return r;
 324
 325        if (!chandef->center_freq2)
 326                return 0;
 327
 328        return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
 329                                               width);
 330}
 331EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
 332
 333static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 334                                        u32 center_freq, u32 bandwidth,
 335                                        u32 prohibited_flags)
 336{
 337        struct ieee80211_channel *c;
 338        u32 freq, start_freq, end_freq;
 339
 340        if (bandwidth <= 20) {
 341                start_freq = center_freq;
 342                end_freq = center_freq;
 343        } else {
 344                start_freq = center_freq - bandwidth/2 + 10;
 345                end_freq = center_freq + bandwidth/2 - 10;
 346        }
 347
 348        for (freq = start_freq; freq <= end_freq; freq += 20) {
 349                c = ieee80211_get_channel(wiphy, freq);
 350                if (!c)
 351                        return false;
 352
 353                /* check for radar flags */
 354                if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
 355                    (c->dfs_state != NL80211_DFS_AVAILABLE))
 356                        return false;
 357
 358                /* check for the other flags */
 359                if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
 360                        return false;
 361        }
 362
 363        return true;
 364}
 365
 366bool cfg80211_chandef_usable(struct wiphy *wiphy,
 367                             const struct cfg80211_chan_def *chandef,
 368                             u32 prohibited_flags)
 369{
 370        struct ieee80211_sta_ht_cap *ht_cap;
 371        struct ieee80211_sta_vht_cap *vht_cap;
 372        u32 width, control_freq;
 373
 374        if (WARN_ON(!cfg80211_chandef_valid(chandef)))
 375                return false;
 376
 377        ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
 378        vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
 379
 380        control_freq = chandef->chan->center_freq;
 381
 382        switch (chandef->width) {
 383        case NL80211_CHAN_WIDTH_5:
 384                width = 5;
 385                break;
 386        case NL80211_CHAN_WIDTH_10:
 387                width = 10;
 388                break;
 389        case NL80211_CHAN_WIDTH_20:
 390                if (!ht_cap->ht_supported)
 391                        return false;
 392        case NL80211_CHAN_WIDTH_20_NOHT:
 393                width = 20;
 394                break;
 395        case NL80211_CHAN_WIDTH_40:
 396                width = 40;
 397                if (!ht_cap->ht_supported)
 398                        return false;
 399                if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
 400                    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
 401                        return false;
 402                if (chandef->center_freq1 < control_freq &&
 403                    chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
 404                        return false;
 405                if (chandef->center_freq1 > control_freq &&
 406                    chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
 407                        return false;
 408                break;
 409        case NL80211_CHAN_WIDTH_80P80:
 410                if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
 411                        return false;
 412        case NL80211_CHAN_WIDTH_80:
 413                if (!vht_cap->vht_supported)
 414                        return false;
 415                prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
 416                width = 80;
 417                break;
 418        case NL80211_CHAN_WIDTH_160:
 419                if (!vht_cap->vht_supported)
 420                        return false;
 421                if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
 422                        return false;
 423                prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
 424                width = 160;
 425                break;
 426        default:
 427                WARN_ON_ONCE(1);
 428                return false;
 429        }
 430
 431        /*
 432         * TODO: What if there are only certain 80/160/80+80 MHz channels
 433         *       allowed by the driver, or only certain combinations?
 434         *       For 40 MHz the driver can set the NO_HT40 flags, but for
 435         *       80/160 MHz and in particular 80+80 MHz this isn't really
 436         *       feasible and we only have NO_80MHZ/NO_160MHZ so far but
 437         *       no way to cover 80+80 MHz or more complex restrictions.
 438         *       Note that such restrictions also need to be advertised to
 439         *       userspace, for example for P2P channel selection.
 440         */
 441
 442        if (width > 20)
 443                prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
 444
 445        /* 5 and 10 MHz are only defined for the OFDM PHY */
 446        if (width < 20)
 447                prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
 448
 449
 450        if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
 451                                         width, prohibited_flags))
 452                return false;
 453
 454        if (!chandef->center_freq2)
 455                return true;
 456        return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
 457                                           width, prohibited_flags);
 458}
 459EXPORT_SYMBOL(cfg80211_chandef_usable);
 460
 461bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
 462                             struct cfg80211_chan_def *chandef)
 463{
 464        bool res;
 465
 466        trace_cfg80211_reg_can_beacon(wiphy, chandef);
 467
 468        res = cfg80211_chandef_usable(wiphy, chandef,
 469                                      IEEE80211_CHAN_DISABLED |
 470                                      IEEE80211_CHAN_PASSIVE_SCAN |
 471                                      IEEE80211_CHAN_NO_IBSS |
 472                                      IEEE80211_CHAN_RADAR);
 473
 474        trace_cfg80211_return_bool(res);
 475        return res;
 476}
 477EXPORT_SYMBOL(cfg80211_reg_can_beacon);
 478
 479int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
 480                                 struct cfg80211_chan_def *chandef)
 481{
 482        if (!rdev->ops->set_monitor_channel)
 483                return -EOPNOTSUPP;
 484        if (!cfg80211_has_monitors_only(rdev))
 485                return -EBUSY;
 486
 487        return rdev_set_monitor_channel(rdev, chandef);
 488}
 489
 490void
 491cfg80211_get_chan_state(struct wireless_dev *wdev,
 492                        struct ieee80211_channel **chan,
 493                        enum cfg80211_chan_mode *chanmode)
 494{
 495        *chan = NULL;
 496        *chanmode = CHAN_MODE_UNDEFINED;
 497
 498        ASSERT_WDEV_LOCK(wdev);
 499
 500        if (wdev->netdev && !netif_running(wdev->netdev))
 501                return;
 502
 503        switch (wdev->iftype) {
 504        case NL80211_IFTYPE_ADHOC:
 505                if (wdev->current_bss) {
 506                        *chan = wdev->current_bss->pub.channel;
 507                        *chanmode = (wdev->ibss_fixed &&
 508                                     !wdev->ibss_dfs_possible)
 509                                  ? CHAN_MODE_SHARED
 510                                  : CHAN_MODE_EXCLUSIVE;
 511                        return;
 512                }
 513        case NL80211_IFTYPE_STATION:
 514        case NL80211_IFTYPE_P2P_CLIENT:
 515                if (wdev->current_bss) {
 516                        *chan = wdev->current_bss->pub.channel;
 517                        *chanmode = CHAN_MODE_SHARED;
 518                        return;
 519                }
 520                break;
 521        case NL80211_IFTYPE_AP:
 522        case NL80211_IFTYPE_P2P_GO:
 523                if (wdev->cac_started) {
 524                        *chan = wdev->channel;
 525                        *chanmode = CHAN_MODE_SHARED;
 526                } else if (wdev->beacon_interval) {
 527                        *chan = wdev->channel;
 528                        *chanmode = CHAN_MODE_SHARED;
 529                }
 530                return;
 531        case NL80211_IFTYPE_MESH_POINT:
 532                if (wdev->mesh_id_len) {
 533                        *chan = wdev->channel;
 534                        *chanmode = CHAN_MODE_SHARED;
 535                }
 536                return;
 537        case NL80211_IFTYPE_MONITOR:
 538        case NL80211_IFTYPE_AP_VLAN:
 539        case NL80211_IFTYPE_WDS:
 540                /* these interface types don't really have a channel */
 541                return;
 542        case NL80211_IFTYPE_P2P_DEVICE:
 543                if (wdev->wiphy->features &
 544                                NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
 545                        *chanmode = CHAN_MODE_EXCLUSIVE;
 546                return;
 547        case NL80211_IFTYPE_UNSPECIFIED:
 548        case NUM_NL80211_IFTYPES:
 549                WARN_ON(1);
 550        }
 551
 552        return;
 553}
 554