linux/net/mac80211/chan.c
<<
>>
Prefs
   1/*
   2 * mac80211 - channel management
   3 */
   4
   5#include <linux/nl80211.h>
   6#include <net/cfg80211.h>
   7#include "ieee80211_i.h"
   8
   9static enum ieee80211_chan_mode
  10__ieee80211_get_channel_mode(struct ieee80211_local *local,
  11                             struct ieee80211_sub_if_data *ignore)
  12{
  13        struct ieee80211_sub_if_data *sdata;
  14
  15        lockdep_assert_held(&local->iflist_mtx);
  16
  17        list_for_each_entry(sdata, &local->interfaces, list) {
  18                if (sdata == ignore)
  19                        continue;
  20
  21                if (!ieee80211_sdata_running(sdata))
  22                        continue;
  23
  24                switch (sdata->vif.type) {
  25                case NL80211_IFTYPE_MONITOR:
  26                        continue;
  27                case NL80211_IFTYPE_STATION:
  28                        if (!sdata->u.mgd.associated)
  29                                continue;
  30                        break;
  31                case NL80211_IFTYPE_ADHOC:
  32                        if (!sdata->u.ibss.ssid_len)
  33                                continue;
  34                        if (!sdata->u.ibss.fixed_channel)
  35                                return CHAN_MODE_HOPPING;
  36                        break;
  37                case NL80211_IFTYPE_AP_VLAN:
  38                        /* will also have _AP interface */
  39                        continue;
  40                case NL80211_IFTYPE_AP:
  41                        if (!sdata->u.ap.beacon)
  42                                continue;
  43                        break;
  44                case NL80211_IFTYPE_MESH_POINT:
  45                        if (!sdata->wdev.mesh_id_len)
  46                                continue;
  47                        break;
  48                default:
  49                        break;
  50                }
  51
  52                return CHAN_MODE_FIXED;
  53        }
  54
  55        return CHAN_MODE_UNDEFINED;
  56}
  57
  58enum ieee80211_chan_mode
  59ieee80211_get_channel_mode(struct ieee80211_local *local,
  60                           struct ieee80211_sub_if_data *ignore)
  61{
  62        enum ieee80211_chan_mode mode;
  63
  64        mutex_lock(&local->iflist_mtx);
  65        mode = __ieee80211_get_channel_mode(local, ignore);
  66        mutex_unlock(&local->iflist_mtx);
  67
  68        return mode;
  69}
  70
  71static enum nl80211_channel_type
  72ieee80211_get_superchan(struct ieee80211_local *local,
  73                        struct ieee80211_sub_if_data *sdata)
  74{
  75        enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
  76        struct ieee80211_sub_if_data *tmp;
  77
  78        mutex_lock(&local->iflist_mtx);
  79        list_for_each_entry(tmp, &local->interfaces, list) {
  80                if (tmp == sdata)
  81                        continue;
  82
  83                if (!ieee80211_sdata_running(tmp))
  84                        continue;
  85
  86                switch (tmp->vif.bss_conf.channel_type) {
  87                case NL80211_CHAN_NO_HT:
  88                case NL80211_CHAN_HT20:
  89                        if (superchan > tmp->vif.bss_conf.channel_type)
  90                                break;
  91
  92                        superchan = tmp->vif.bss_conf.channel_type;
  93                        break;
  94                case NL80211_CHAN_HT40PLUS:
  95                        WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
  96                        superchan = NL80211_CHAN_HT40PLUS;
  97                        break;
  98                case NL80211_CHAN_HT40MINUS:
  99                        WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
 100                        superchan = NL80211_CHAN_HT40MINUS;
 101                        break;
 102                }
 103        }
 104        mutex_unlock(&local->iflist_mtx);
 105
 106        return superchan;
 107}
 108
 109static bool
 110ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
 111                                       enum nl80211_channel_type chantype2,
 112                                       enum nl80211_channel_type *compat)
 113{
 114        /*
 115         * start out with chantype1 being the result,
 116         * overwriting later if needed
 117         */
 118        if (compat)
 119                *compat = chantype1;
 120
 121        switch (chantype1) {
 122        case NL80211_CHAN_NO_HT:
 123                if (compat)
 124                        *compat = chantype2;
 125                break;
 126        case NL80211_CHAN_HT20:
 127                /*
 128                 * allow any change that doesn't go to no-HT
 129                 * (if it already is no-HT no change is needed)
 130                 */
 131                if (chantype2 == NL80211_CHAN_NO_HT)
 132                        break;
 133                if (compat)
 134                        *compat = chantype2;
 135                break;
 136        case NL80211_CHAN_HT40PLUS:
 137        case NL80211_CHAN_HT40MINUS:
 138                /* allow smaller bandwidth and same */
 139                if (chantype2 == NL80211_CHAN_NO_HT)
 140                        break;
 141                if (chantype2 == NL80211_CHAN_HT20)
 142                        break;
 143                if (chantype2 == chantype1)
 144                        break;
 145                return false;
 146        }
 147
 148        return true;
 149}
 150
 151bool ieee80211_set_channel_type(struct ieee80211_local *local,
 152                                struct ieee80211_sub_if_data *sdata,
 153                                enum nl80211_channel_type chantype)
 154{
 155        enum nl80211_channel_type superchan;
 156        enum nl80211_channel_type compatchan;
 157
 158        superchan = ieee80211_get_superchan(local, sdata);
 159        if (!ieee80211_channel_types_are_compatible(superchan, chantype,
 160                                                    &compatchan))
 161                return false;
 162
 163        local->_oper_channel_type = compatchan;
 164
 165        if (sdata)
 166                sdata->vif.bss_conf.channel_type = chantype;
 167
 168        return true;
 169
 170}
 171