summaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorSimon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de>2013-08-28 07:41:29 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-09-26 07:27:13 -0400
commite6b7cde4d3e155f118b81f1f62f86554c529083a (patch)
treef7d4b6a41f03d286feb3b1daab02d2e8c8c92bc3 /net/mac80211/mlme.c
parent774f073461dbee0decee7524d9b930a98a3dc30c (diff)
mac80211: split off channel switch parsing function
The channel switch parsing function can be re-used for the IBSS code, put the common part into an extra function. Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de> Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de> [also move/rename chandef_downgrade] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c239
1 files changed, 16 insertions, 223 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e396a2a97319..9fce0f49cd10 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
145 return (1 << ecw) - 1; 145 return (1 << ecw) - 1;
146} 146}
147 147
148static u32 chandef_downgrade(struct cfg80211_chan_def *c)
149{
150 u32 ret;
151 int tmp;
152
153 switch (c->width) {
154 case NL80211_CHAN_WIDTH_20:
155 c->width = NL80211_CHAN_WIDTH_20_NOHT;
156 ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
157 break;
158 case NL80211_CHAN_WIDTH_40:
159 c->width = NL80211_CHAN_WIDTH_20;
160 c->center_freq1 = c->chan->center_freq;
161 ret = IEEE80211_STA_DISABLE_40MHZ |
162 IEEE80211_STA_DISABLE_VHT;
163 break;
164 case NL80211_CHAN_WIDTH_80:
165 tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
166 /* n_P40 */
167 tmp /= 2;
168 /* freq_P40 */
169 c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
170 c->width = NL80211_CHAN_WIDTH_40;
171 ret = IEEE80211_STA_DISABLE_VHT;
172 break;
173 case NL80211_CHAN_WIDTH_80P80:
174 c->center_freq2 = 0;
175 c->width = NL80211_CHAN_WIDTH_80;
176 ret = IEEE80211_STA_DISABLE_80P80MHZ |
177 IEEE80211_STA_DISABLE_160MHZ;
178 break;
179 case NL80211_CHAN_WIDTH_160:
180 /* n_P20 */
181 tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
182 /* n_P80 */
183 tmp /= 4;
184 c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
185 c->width = NL80211_CHAN_WIDTH_80;
186 ret = IEEE80211_STA_DISABLE_80P80MHZ |
187 IEEE80211_STA_DISABLE_160MHZ;
188 break;
189 default:
190 case NL80211_CHAN_WIDTH_20_NOHT:
191 WARN_ON_ONCE(1);
192 c->width = NL80211_CHAN_WIDTH_20_NOHT;
193 ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
194 break;
195 case NL80211_CHAN_WIDTH_5:
196 case NL80211_CHAN_WIDTH_10:
197 WARN_ON_ONCE(1);
198 /* keep c->width */
199 ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
200 break;
201 }
202
203 WARN_ON_ONCE(!cfg80211_chandef_valid(c));
204
205 return ret;
206}
207
208static u32 148static u32
209ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, 149ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
210 struct ieee80211_supported_band *sband, 150 struct ieee80211_supported_band *sband,
@@ -352,7 +292,7 @@ out:
352 break; 292 break;
353 } 293 }
354 294
355 ret |= chandef_downgrade(chandef); 295 ret |= ieee80211_chandef_downgrade(chandef);
356 } 296 }
357 297
358 if (chandef->width != vht_chandef.width && !tracking) 298 if (chandef->width != vht_chandef.width && !tracking)
@@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
406 */ 346 */
407 if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && 347 if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
408 chandef.width == NL80211_CHAN_WIDTH_80P80) 348 chandef.width == NL80211_CHAN_WIDTH_80P80)
409 flags |= chandef_downgrade(&chandef); 349 flags |= ieee80211_chandef_downgrade(&chandef);
410 if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && 350 if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
411 chandef.width == NL80211_CHAN_WIDTH_160) 351 chandef.width == NL80211_CHAN_WIDTH_160)
412 flags |= chandef_downgrade(&chandef); 352 flags |= ieee80211_chandef_downgrade(&chandef);
413 if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && 353 if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
414 chandef.width > NL80211_CHAN_WIDTH_20) 354 chandef.width > NL80211_CHAN_WIDTH_20)
415 flags |= chandef_downgrade(&chandef); 355 flags |= ieee80211_chandef_downgrade(&chandef);
416 356
417 if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef)) 357 if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
418 return 0; 358 return 0;
@@ -999,20 +939,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
999 struct ieee80211_local *local = sdata->local; 939 struct ieee80211_local *local = sdata->local;
1000 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 940 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
1001 struct cfg80211_bss *cbss = ifmgd->associated; 941 struct cfg80211_bss *cbss = ifmgd->associated;
1002 struct ieee80211_bss *bss;
1003 struct ieee80211_chanctx *chanctx; 942 struct ieee80211_chanctx *chanctx;
1004 enum ieee80211_band new_band; 943 enum ieee80211_band current_band;
1005 int new_freq;
1006 u8 new_chan_no;
1007 u8 count; 944 u8 count;
1008 u8 mode; 945 u8 mode;
1009 struct ieee80211_channel *new_chan;
1010 struct cfg80211_chan_def new_chandef = {}; 946 struct cfg80211_chan_def new_chandef = {};
1011 struct cfg80211_chan_def new_vht_chandef = {}; 947 int res;
1012 const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
1013 const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
1014 const struct ieee80211_ht_operation *ht_oper;
1015 int secondary_channel_offset = -1;
1016 948
1017 sdata_assert_lock(sdata); 949 sdata_assert_lock(sdata);
1018 950
@@ -1026,162 +958,23 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1026 if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) 958 if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
1027 return; 959 return;
1028 960
1029 sec_chan_offs = elems->sec_chan_offs; 961 current_band = cbss->channel->band;
1030 wide_bw_chansw_ie = elems->wide_bw_chansw_ie; 962 res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
1031 ht_oper = elems->ht_operation; 963 ifmgd->flags,
1032 964 ifmgd->associated->bssid, &count,
1033 if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | 965 &mode, &new_chandef);
1034 IEEE80211_STA_DISABLE_40MHZ)) { 966 if (res < 0)
1035 sec_chan_offs = NULL;
1036 wide_bw_chansw_ie = NULL;
1037 /* only used for bandwidth here */
1038 ht_oper = NULL;
1039 }
1040
1041 if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
1042 wide_bw_chansw_ie = NULL;
1043
1044 if (elems->ext_chansw_ie) {
1045 if (!ieee80211_operating_class_to_band(
1046 elems->ext_chansw_ie->new_operating_class,
1047 &new_band)) {
1048 sdata_info(sdata,
1049 "cannot understand ECSA IE operating class %d, disconnecting\n",
1050 elems->ext_chansw_ie->new_operating_class);
1051 ieee80211_queue_work(&local->hw,
1052 &ifmgd->csa_connection_drop_work);
1053 }
1054 new_chan_no = elems->ext_chansw_ie->new_ch_num;
1055 count = elems->ext_chansw_ie->count;
1056 mode = elems->ext_chansw_ie->mode;
1057 } else if (elems->ch_switch_ie) {
1058 new_band = cbss->channel->band;
1059 new_chan_no = elems->ch_switch_ie->new_ch_num;
1060 count = elems->ch_switch_ie->count;
1061 mode = elems->ch_switch_ie->mode;
1062 } else {
1063 /* nothing here we understand */
1064 return;
1065 }
1066
1067 bss = (void *)cbss->priv;
1068
1069 new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
1070 new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
1071 if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
1072 sdata_info(sdata,
1073 "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
1074 ifmgd->associated->bssid, new_freq);
1075 ieee80211_queue_work(&local->hw, 967 ieee80211_queue_work(&local->hw,
1076 &ifmgd->csa_connection_drop_work); 968 &ifmgd->csa_connection_drop_work);
969 if (res)
1077 return; 970 return;
1078 }
1079
1080 if (!beacon && sec_chan_offs) {
1081 secondary_channel_offset = sec_chan_offs->sec_chan_offs;
1082 } else if (beacon && ht_oper) {
1083 secondary_channel_offset =
1084 ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
1085 } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
1086 /*
1087 * If it's not a beacon, HT is enabled and the IE not present,
1088 * it's 20 MHz, 802.11-2012 8.5.2.6:
1089 * This element [the Secondary Channel Offset Element] is
1090 * present when switching to a 40 MHz channel. It may be
1091 * present when switching to a 20 MHz channel (in which
1092 * case the secondary channel offset is set to SCN).
1093 */
1094 secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
1095 }
1096
1097 switch (secondary_channel_offset) {
1098 default:
1099 /* secondary_channel_offset was present but is invalid */
1100 case IEEE80211_HT_PARAM_CHA_SEC_NONE:
1101 cfg80211_chandef_create(&new_chandef, new_chan,
1102 NL80211_CHAN_HT20);
1103 break;
1104 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
1105 cfg80211_chandef_create(&new_chandef, new_chan,
1106 NL80211_CHAN_HT40PLUS);
1107 break;
1108 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
1109 cfg80211_chandef_create(&new_chandef, new_chan,
1110 NL80211_CHAN_HT40MINUS);
1111 break;
1112 case -1:
1113 cfg80211_chandef_create(&new_chandef, new_chan,
1114 NL80211_CHAN_NO_HT);
1115 /* keep width for 5/10 MHz channels */
1116 switch (sdata->vif.bss_conf.chandef.width) {
1117 case NL80211_CHAN_WIDTH_5:
1118 case NL80211_CHAN_WIDTH_10:
1119 new_chandef.width = sdata->vif.bss_conf.chandef.width;
1120 break;
1121 default:
1122 break;
1123 }
1124 break;
1125 }
1126
1127 if (wide_bw_chansw_ie) {
1128 new_vht_chandef.chan = new_chan;
1129 new_vht_chandef.center_freq1 =
1130 ieee80211_channel_to_frequency(
1131 wide_bw_chansw_ie->new_center_freq_seg0,
1132 new_band);
1133
1134 switch (wide_bw_chansw_ie->new_channel_width) {
1135 default:
1136 /* hmmm, ignore VHT and use HT if present */
1137 case IEEE80211_VHT_CHANWIDTH_USE_HT:
1138 new_vht_chandef.chan = NULL;
1139 break;
1140 case IEEE80211_VHT_CHANWIDTH_80MHZ:
1141 new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
1142 break;
1143 case IEEE80211_VHT_CHANWIDTH_160MHZ:
1144 new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
1145 break;
1146 case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
1147 /* field is otherwise reserved */
1148 new_vht_chandef.center_freq2 =
1149 ieee80211_channel_to_frequency(
1150 wide_bw_chansw_ie->new_center_freq_seg1,
1151 new_band);
1152 new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
1153 break;
1154 }
1155 if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
1156 new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
1157 chandef_downgrade(&new_vht_chandef);
1158 if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
1159 new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
1160 chandef_downgrade(&new_vht_chandef);
1161 if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
1162 new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
1163 chandef_downgrade(&new_vht_chandef);
1164 }
1165
1166 /* if VHT data is there validate & use it */
1167 if (new_vht_chandef.chan) {
1168 if (!cfg80211_chandef_compatible(&new_vht_chandef,
1169 &new_chandef)) {
1170 sdata_info(sdata,
1171 "AP %pM CSA has inconsistent channel data, disconnecting\n",
1172 ifmgd->associated->bssid);
1173 ieee80211_queue_work(&local->hw,
1174 &ifmgd->csa_connection_drop_work);
1175 return;
1176 }
1177 new_chandef = new_vht_chandef;
1178 }
1179 971
1180 if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, 972 if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
1181 IEEE80211_CHAN_DISABLED)) { 973 IEEE80211_CHAN_DISABLED)) {
1182 sdata_info(sdata, 974 sdata_info(sdata,
1183 "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", 975 "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
1184 ifmgd->associated->bssid, new_freq, 976 ifmgd->associated->bssid,
977 new_chandef.chan->center_freq,
1185 new_chandef.width, new_chandef.center_freq1, 978 new_chandef.width, new_chandef.center_freq1,
1186 new_chandef.center_freq2); 979 new_chandef.center_freq2);
1187 ieee80211_queue_work(&local->hw, 980 ieee80211_queue_work(&local->hw,
@@ -3856,7 +3649,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
3856 return ret; 3649 return ret;
3857 3650
3858 while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { 3651 while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
3859 ifmgd->flags |= chandef_downgrade(&chandef); 3652 ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
3860 ret = ieee80211_vif_use_channel(sdata, &chandef, 3653 ret = ieee80211_vif_use_channel(sdata, &chandef,
3861 IEEE80211_CHANCTX_SHARED); 3654 IEEE80211_CHANCTX_SHARED);
3862 } 3655 }