1f444de05SJohannes Berg /* 2f444de05SJohannes Berg * mac80211 - channel management 3f444de05SJohannes Berg */ 4f444de05SJohannes Berg 50aaffa9bSJohannes Berg #include <linux/nl80211.h> 63117bbdbSPaul Stewart #include <net/cfg80211.h> 7f444de05SJohannes Berg #include "ieee80211_i.h" 8f444de05SJohannes Berg 9368a07d2SJohannes Berg static enum ieee80211_chan_mode 10f444de05SJohannes Berg __ieee80211_get_channel_mode(struct ieee80211_local *local, 11f444de05SJohannes Berg struct ieee80211_sub_if_data *ignore) 12f444de05SJohannes Berg { 13f444de05SJohannes Berg struct ieee80211_sub_if_data *sdata; 14f444de05SJohannes Berg 1546a5ebafSJohannes Berg lockdep_assert_held(&local->iflist_mtx); 16f444de05SJohannes Berg 17f444de05SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 18f444de05SJohannes Berg if (sdata == ignore) 19f444de05SJohannes Berg continue; 20f444de05SJohannes Berg 21f444de05SJohannes Berg if (!ieee80211_sdata_running(sdata)) 22f444de05SJohannes Berg continue; 23f444de05SJohannes Berg 24e9980e6dSJohannes Berg switch (sdata->vif.type) { 25e9980e6dSJohannes Berg case NL80211_IFTYPE_MONITOR: 26f444de05SJohannes Berg continue; 27e9980e6dSJohannes Berg case NL80211_IFTYPE_STATION: 28e9980e6dSJohannes Berg if (!sdata->u.mgd.associated) 29f444de05SJohannes Berg continue; 30e9980e6dSJohannes Berg break; 31e9980e6dSJohannes Berg case NL80211_IFTYPE_ADHOC: 32f444de05SJohannes Berg if (!sdata->u.ibss.ssid_len) 33f444de05SJohannes Berg continue; 34f444de05SJohannes Berg if (!sdata->u.ibss.fixed_channel) 35f444de05SJohannes Berg return CHAN_MODE_HOPPING; 36e9980e6dSJohannes Berg break; 37e9980e6dSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 38e9980e6dSJohannes Berg /* will also have _AP interface */ 39f444de05SJohannes Berg continue; 40e9980e6dSJohannes Berg case NL80211_IFTYPE_AP: 41e9980e6dSJohannes Berg if (!sdata->u.ap.beacon) 42e9980e6dSJohannes Berg continue; 43e9980e6dSJohannes Berg break; 44be0f4237SThomas Pedersen case NL80211_IFTYPE_MESH_POINT: 45be0f4237SThomas Pedersen if (!sdata->wdev.mesh_id_len) 46be0f4237SThomas Pedersen continue; 47be0f4237SThomas Pedersen break; 48e9980e6dSJohannes Berg default: 49e9980e6dSJohannes Berg break; 50e9980e6dSJohannes Berg } 51f444de05SJohannes Berg 52f444de05SJohannes Berg return CHAN_MODE_FIXED; 53f444de05SJohannes Berg } 54f444de05SJohannes Berg 55f444de05SJohannes Berg return CHAN_MODE_UNDEFINED; 56f444de05SJohannes Berg } 57f444de05SJohannes Berg 58f444de05SJohannes Berg enum ieee80211_chan_mode 59f444de05SJohannes Berg ieee80211_get_channel_mode(struct ieee80211_local *local, 60f444de05SJohannes Berg struct ieee80211_sub_if_data *ignore) 61f444de05SJohannes Berg { 62f444de05SJohannes Berg enum ieee80211_chan_mode mode; 63f444de05SJohannes Berg 64f444de05SJohannes Berg mutex_lock(&local->iflist_mtx); 65f444de05SJohannes Berg mode = __ieee80211_get_channel_mode(local, ignore); 66f444de05SJohannes Berg mutex_unlock(&local->iflist_mtx); 67f444de05SJohannes Berg 68f444de05SJohannes Berg return mode; 69f444de05SJohannes Berg } 700aaffa9bSJohannes Berg 7123a85b45SMichal Kazior static enum nl80211_channel_type 7223a85b45SMichal Kazior ieee80211_get_superchan(struct ieee80211_local *local, 7323a85b45SMichal Kazior struct ieee80211_sub_if_data *sdata) 740aaffa9bSJohannes Berg { 750aaffa9bSJohannes Berg enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; 7623a85b45SMichal Kazior struct ieee80211_sub_if_data *tmp; 770aaffa9bSJohannes Berg 780aaffa9bSJohannes Berg mutex_lock(&local->iflist_mtx); 790aaffa9bSJohannes Berg list_for_each_entry(tmp, &local->interfaces, list) { 800aaffa9bSJohannes Berg if (tmp == sdata) 810aaffa9bSJohannes Berg continue; 820aaffa9bSJohannes Berg 830aaffa9bSJohannes Berg if (!ieee80211_sdata_running(tmp)) 840aaffa9bSJohannes Berg continue; 850aaffa9bSJohannes Berg 860aaffa9bSJohannes Berg switch (tmp->vif.bss_conf.channel_type) { 870aaffa9bSJohannes Berg case NL80211_CHAN_NO_HT: 880aaffa9bSJohannes Berg case NL80211_CHAN_HT20: 899db372fdSFelix Fietkau if (superchan > tmp->vif.bss_conf.channel_type) 909db372fdSFelix Fietkau break; 919db372fdSFelix Fietkau 920aaffa9bSJohannes Berg superchan = tmp->vif.bss_conf.channel_type; 930aaffa9bSJohannes Berg break; 940aaffa9bSJohannes Berg case NL80211_CHAN_HT40PLUS: 950aaffa9bSJohannes Berg WARN_ON(superchan == NL80211_CHAN_HT40MINUS); 960aaffa9bSJohannes Berg superchan = NL80211_CHAN_HT40PLUS; 970aaffa9bSJohannes Berg break; 980aaffa9bSJohannes Berg case NL80211_CHAN_HT40MINUS: 990aaffa9bSJohannes Berg WARN_ON(superchan == NL80211_CHAN_HT40PLUS); 1000aaffa9bSJohannes Berg superchan = NL80211_CHAN_HT40MINUS; 1010aaffa9bSJohannes Berg break; 1020aaffa9bSJohannes Berg } 1030aaffa9bSJohannes Berg } 10423a85b45SMichal Kazior mutex_unlock(&local->iflist_mtx); 1050aaffa9bSJohannes Berg 10623a85b45SMichal Kazior return superchan; 10723a85b45SMichal Kazior } 10823a85b45SMichal Kazior 10923a85b45SMichal Kazior static bool 11023a85b45SMichal Kazior ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, 11123a85b45SMichal Kazior enum nl80211_channel_type chantype2, 11223a85b45SMichal Kazior enum nl80211_channel_type *compat) 11323a85b45SMichal Kazior { 11423a85b45SMichal Kazior /* 11523a85b45SMichal Kazior * start out with chantype1 being the result, 11623a85b45SMichal Kazior * overwriting later if needed 11723a85b45SMichal Kazior */ 11823a85b45SMichal Kazior if (compat) 11923a85b45SMichal Kazior *compat = chantype1; 12023a85b45SMichal Kazior 12123a85b45SMichal Kazior switch (chantype1) { 1220aaffa9bSJohannes Berg case NL80211_CHAN_NO_HT: 12323a85b45SMichal Kazior if (compat) 12423a85b45SMichal Kazior *compat = chantype2; 12523a85b45SMichal Kazior break; 1260aaffa9bSJohannes Berg case NL80211_CHAN_HT20: 1270aaffa9bSJohannes Berg /* 1280aaffa9bSJohannes Berg * allow any change that doesn't go to no-HT 1290aaffa9bSJohannes Berg * (if it already is no-HT no change is needed) 1300aaffa9bSJohannes Berg */ 13123a85b45SMichal Kazior if (chantype2 == NL80211_CHAN_NO_HT) 1320aaffa9bSJohannes Berg break; 13323a85b45SMichal Kazior if (compat) 13423a85b45SMichal Kazior *compat = chantype2; 1350aaffa9bSJohannes Berg break; 1360aaffa9bSJohannes Berg case NL80211_CHAN_HT40PLUS: 1370aaffa9bSJohannes Berg case NL80211_CHAN_HT40MINUS: 1380aaffa9bSJohannes Berg /* allow smaller bandwidth and same */ 13923a85b45SMichal Kazior if (chantype2 == NL80211_CHAN_NO_HT) 1400aaffa9bSJohannes Berg break; 14123a85b45SMichal Kazior if (chantype2 == NL80211_CHAN_HT20) 1420aaffa9bSJohannes Berg break; 14323a85b45SMichal Kazior if (chantype2 == chantype1) 1440aaffa9bSJohannes Berg break; 14523a85b45SMichal Kazior return false; 1460aaffa9bSJohannes Berg } 1470aaffa9bSJohannes Berg 14823a85b45SMichal Kazior return true; 14923a85b45SMichal Kazior } 15023a85b45SMichal Kazior 15123a85b45SMichal Kazior bool ieee80211_set_channel_type(struct ieee80211_local *local, 15223a85b45SMichal Kazior struct ieee80211_sub_if_data *sdata, 15323a85b45SMichal Kazior enum nl80211_channel_type chantype) 15423a85b45SMichal Kazior { 15523a85b45SMichal Kazior enum nl80211_channel_type superchan; 15623a85b45SMichal Kazior enum nl80211_channel_type compatchan; 15723a85b45SMichal Kazior 15823a85b45SMichal Kazior superchan = ieee80211_get_superchan(local, sdata); 15923a85b45SMichal Kazior if (!ieee80211_channel_types_are_compatible(superchan, chantype, 16023a85b45SMichal Kazior &compatchan)) 16123a85b45SMichal Kazior return false; 16223a85b45SMichal Kazior 16323a85b45SMichal Kazior local->_oper_channel_type = compatchan; 1640aaffa9bSJohannes Berg 1650aaffa9bSJohannes Berg if (sdata) 1660aaffa9bSJohannes Berg sdata->vif.bss_conf.channel_type = chantype; 1670aaffa9bSJohannes Berg 16823a85b45SMichal Kazior return true; 1690aaffa9bSJohannes Berg 1700aaffa9bSJohannes Berg } 171