1 /* 2 * mac80211 - channel management 3 */ 4 5 #include <linux/nl80211.h> 6 #include <net/cfg80211.h> 7 #include "ieee80211_i.h" 8 9 static 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 58 enum ieee80211_chan_mode 59 ieee80211_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 71 static enum nl80211_channel_type 72 ieee80211_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 109 static bool 110 ieee80211_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 151 bool 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