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 <net/cfg80211.h>
  10#include "core.h"
  11
  12struct ieee80211_channel *
  13rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
  14                  int freq, enum nl80211_channel_type channel_type)
  15{
  16        struct ieee80211_channel *chan;
  17        struct ieee80211_sta_ht_cap *ht_cap;
  18
  19        chan = ieee80211_get_channel(&rdev->wiphy, freq);
  20
  21        /* Primary channel not allowed */
  22        if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
  23                return NULL;
  24
  25        if (channel_type == NL80211_CHAN_HT40MINUS &&
  26            chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
  27                return NULL;
  28        else if (channel_type == NL80211_CHAN_HT40PLUS &&
  29                 chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
  30                return NULL;
  31
  32        ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
  33
  34        if (channel_type != NL80211_CHAN_NO_HT) {
  35                if (!ht_cap->ht_supported)
  36                        return NULL;
  37
  38                if (channel_type != NL80211_CHAN_HT20 &&
  39                    (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
  40                    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
  41                        return NULL;
  42        }
  43
  44        return chan;
  45}
  46
  47static bool can_beacon_sec_chan(struct wiphy *wiphy,
  48                                struct ieee80211_channel *chan,
  49                                enum nl80211_channel_type channel_type)
  50{
  51        struct ieee80211_channel *sec_chan;
  52        int diff;
  53
  54        switch (channel_type) {
  55        case NL80211_CHAN_HT40PLUS:
  56                diff = 20;
  57                break;
  58        case NL80211_CHAN_HT40MINUS:
  59                diff = -20;
  60                break;
  61        default:
  62                return false;
  63        }
  64
  65        sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff);
  66        if (!sec_chan)
  67                return false;
  68
  69        /* we'll need a DFS capability later */
  70        if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
  71                               IEEE80211_CHAN_PASSIVE_SCAN |
  72                               IEEE80211_CHAN_NO_IBSS |
  73                               IEEE80211_CHAN_RADAR))
  74                return false;
  75
  76        return true;
  77}
  78
  79int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
  80                      struct wireless_dev *wdev, int freq,
  81                      enum nl80211_channel_type channel_type)
  82{
  83        struct ieee80211_channel *chan;
  84        int result;
  85
  86        if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR)
  87                wdev = NULL;
  88
  89        if (wdev) {
  90                ASSERT_WDEV_LOCK(wdev);
  91
  92                if (!netif_running(wdev->netdev))
  93                        return -ENETDOWN;
  94        }
  95
  96        if (!rdev->ops->set_channel)
  97                return -EOPNOTSUPP;
  98
  99        chan = rdev_freq_to_chan(rdev, freq, channel_type);
 100        if (!chan)
 101                return -EINVAL;
 102
 103        /* Both channels should be able to initiate communication */
 104        if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC ||
 105                     wdev->iftype == NL80211_IFTYPE_AP ||
 106                     wdev->iftype == NL80211_IFTYPE_AP_VLAN ||
 107                     wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
 108                     wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
 109                switch (channel_type) {
 110                case NL80211_CHAN_HT40PLUS:
 111                case NL80211_CHAN_HT40MINUS:
 112                        if (!can_beacon_sec_chan(&rdev->wiphy, chan,
 113                                                 channel_type)) {
 114                                printk(KERN_DEBUG
 115                                       "cfg80211: Secondary channel not "
 116                                       "allowed to initiate communication\n");
 117                                return -EINVAL;
 118                        }
 119                        break;
 120                default:
 121                        break;
 122                }
 123        }
 124
 125        result = rdev->ops->set_channel(&rdev->wiphy,
 126                                        wdev ? wdev->netdev : NULL,
 127                                        chan, channel_type);
 128        if (result)
 129                return result;
 130
 131        if (wdev)
 132                wdev->channel = chan;
 133
 134        return 0;
 135}
 136