diff options
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r-- | net/mac80211/work.c | 153 |
1 files changed, 20 insertions, 133 deletions
diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 6c53b6d1002b..c6dd01a05291 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c | |||
@@ -94,7 +94,8 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, | |||
94 | 94 | ||
95 | /* frame sending functions */ | 95 | /* frame sending functions */ |
96 | 96 | ||
97 | static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, | 97 | static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, |
98 | struct sk_buff *skb, const u8 *ht_info_ie, | ||
98 | struct ieee80211_supported_band *sband, | 99 | struct ieee80211_supported_band *sband, |
99 | struct ieee80211_channel *channel, | 100 | struct ieee80211_channel *channel, |
100 | enum ieee80211_smps_mode smps) | 101 | enum ieee80211_smps_mode smps) |
@@ -102,8 +103,10 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, | |||
102 | struct ieee80211_ht_info *ht_info; | 103 | struct ieee80211_ht_info *ht_info; |
103 | u8 *pos; | 104 | u8 *pos; |
104 | u32 flags = channel->flags; | 105 | u32 flags = channel->flags; |
105 | u16 cap = sband->ht_cap.cap; | 106 | u16 cap; |
106 | __le16 tmp; | 107 | struct ieee80211_sta_ht_cap ht_cap; |
108 | |||
109 | BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); | ||
107 | 110 | ||
108 | if (!sband->ht_cap.ht_supported) | 111 | if (!sband->ht_cap.ht_supported) |
109 | return; | 112 | return; |
@@ -114,9 +117,13 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, | |||
114 | if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info)) | 117 | if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info)) |
115 | return; | 118 | return; |
116 | 119 | ||
120 | memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); | ||
121 | ieee80211_apply_htcap_overrides(sdata, &ht_cap); | ||
122 | |||
117 | ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2); | 123 | ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2); |
118 | 124 | ||
119 | /* determine capability flags */ | 125 | /* determine capability flags */ |
126 | cap = ht_cap.cap; | ||
120 | 127 | ||
121 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | 128 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { |
122 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | 129 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
@@ -154,34 +161,8 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, | |||
154 | } | 161 | } |
155 | 162 | ||
156 | /* reserve and fill IE */ | 163 | /* reserve and fill IE */ |
157 | |||
158 | pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | 164 | pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); |
159 | *pos++ = WLAN_EID_HT_CAPABILITY; | 165 | ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); |
160 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
161 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | ||
162 | |||
163 | /* capability flags */ | ||
164 | tmp = cpu_to_le16(cap); | ||
165 | memcpy(pos, &tmp, sizeof(u16)); | ||
166 | pos += sizeof(u16); | ||
167 | |||
168 | /* AMPDU parameters */ | ||
169 | *pos++ = sband->ht_cap.ampdu_factor | | ||
170 | (sband->ht_cap.ampdu_density << | ||
171 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
172 | |||
173 | /* MCS set */ | ||
174 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | ||
175 | pos += sizeof(sband->ht_cap.mcs); | ||
176 | |||
177 | /* extended capabilities */ | ||
178 | pos += sizeof(__le16); | ||
179 | |||
180 | /* BF capabilities */ | ||
181 | pos += sizeof(__le32); | ||
182 | |||
183 | /* antenna selection */ | ||
184 | pos += sizeof(u8); | ||
185 | } | 166 | } |
186 | 167 | ||
187 | static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | 168 | static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, |
@@ -356,7 +337,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
356 | 337 | ||
357 | if (wk->assoc.use_11n && wk->assoc.wmm_used && | 338 | if (wk->assoc.use_11n && wk->assoc.wmm_used && |
358 | local->hw.queues >= 4) | 339 | local->hw.queues >= 4) |
359 | ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie, | 340 | ieee80211_add_ht_ie(sdata, skb, wk->assoc.ht_information_ie, |
360 | sband, wk->chan, wk->assoc.smps); | 341 | sband, wk->chan, wk->assoc.smps); |
361 | 342 | ||
362 | /* if present, add any custom non-vendor IEs that go after HT */ | 343 | /* if present, add any custom non-vendor IEs that go after HT */ |
@@ -881,44 +862,6 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, | |||
881 | kfree_skb(skb); | 862 | kfree_skb(skb); |
882 | } | 863 | } |
883 | 864 | ||
884 | static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, | ||
885 | enum nl80211_channel_type oper_ct) | ||
886 | { | ||
887 | switch (wk_ct) { | ||
888 | case NL80211_CHAN_NO_HT: | ||
889 | return true; | ||
890 | case NL80211_CHAN_HT20: | ||
891 | if (oper_ct != NL80211_CHAN_NO_HT) | ||
892 | return true; | ||
893 | return false; | ||
894 | case NL80211_CHAN_HT40MINUS: | ||
895 | case NL80211_CHAN_HT40PLUS: | ||
896 | return (wk_ct == oper_ct); | ||
897 | } | ||
898 | WARN_ON(1); /* shouldn't get here */ | ||
899 | return false; | ||
900 | } | ||
901 | |||
902 | static enum nl80211_channel_type | ||
903 | ieee80211_calc_ct(enum nl80211_channel_type wk_ct, | ||
904 | enum nl80211_channel_type oper_ct) | ||
905 | { | ||
906 | switch (wk_ct) { | ||
907 | case NL80211_CHAN_NO_HT: | ||
908 | return oper_ct; | ||
909 | case NL80211_CHAN_HT20: | ||
910 | if (oper_ct != NL80211_CHAN_NO_HT) | ||
911 | return oper_ct; | ||
912 | return wk_ct; | ||
913 | case NL80211_CHAN_HT40MINUS: | ||
914 | case NL80211_CHAN_HT40PLUS: | ||
915 | return wk_ct; | ||
916 | } | ||
917 | WARN_ON(1); /* shouldn't get here */ | ||
918 | return wk_ct; | ||
919 | } | ||
920 | |||
921 | |||
922 | static void ieee80211_work_timer(unsigned long data) | 865 | static void ieee80211_work_timer(unsigned long data) |
923 | { | 866 | { |
924 | struct ieee80211_local *local = (void *) data; | 867 | struct ieee80211_local *local = (void *) data; |
@@ -969,51 +912,12 @@ static void ieee80211_work_work(struct work_struct *work) | |||
969 | } | 912 | } |
970 | 913 | ||
971 | if (!started && !local->tmp_channel) { | 914 | if (!started && !local->tmp_channel) { |
972 | bool on_oper_chan; | 915 | ieee80211_offchannel_stop_vifs(local, true); |
973 | bool tmp_chan_changed = false; | ||
974 | bool on_oper_chan2; | ||
975 | enum nl80211_channel_type wk_ct; | ||
976 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); | ||
977 | |||
978 | /* Work with existing channel type if possible. */ | ||
979 | wk_ct = wk->chan_type; | ||
980 | if (wk->chan == local->hw.conf.channel) | ||
981 | wk_ct = ieee80211_calc_ct(wk->chan_type, | ||
982 | local->hw.conf.channel_type); | ||
983 | |||
984 | if (local->tmp_channel) | ||
985 | if ((local->tmp_channel != wk->chan) || | ||
986 | (local->tmp_channel_type != wk_ct)) | ||
987 | tmp_chan_changed = true; | ||
988 | 916 | ||
989 | local->tmp_channel = wk->chan; | 917 | local->tmp_channel = wk->chan; |
990 | local->tmp_channel_type = wk_ct; | 918 | local->tmp_channel_type = wk->chan_type; |
991 | /* | 919 | |
992 | * Leave the station vifs in awake mode if they | 920 | ieee80211_hw_config(local, 0); |
993 | * happen to be on the same channel as | ||
994 | * the requested channel. | ||
995 | */ | ||
996 | on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); | ||
997 | if (on_oper_chan != on_oper_chan2) { | ||
998 | if (on_oper_chan2) { | ||
999 | /* going off oper channel, PS too */ | ||
1000 | ieee80211_offchannel_stop_vifs(local, | ||
1001 | true); | ||
1002 | ieee80211_hw_config(local, 0); | ||
1003 | } else { | ||
1004 | /* going on channel, but leave PS | ||
1005 | * off-channel. */ | ||
1006 | ieee80211_hw_config(local, 0); | ||
1007 | ieee80211_offchannel_return(local, | ||
1008 | true, | ||
1009 | false); | ||
1010 | } | ||
1011 | } else if (tmp_chan_changed) | ||
1012 | /* Still off-channel, but on some other | ||
1013 | * channel, so update hardware. | ||
1014 | * PS should already be off-channel. | ||
1015 | */ | ||
1016 | ieee80211_hw_config(local, 0); | ||
1017 | 921 | ||
1018 | started = true; | 922 | started = true; |
1019 | wk->timeout = jiffies; | 923 | wk->timeout = jiffies; |
@@ -1082,34 +986,17 @@ static void ieee80211_work_work(struct work_struct *work) | |||
1082 | list_for_each_entry(wk, &local->work_list, list) { | 986 | list_for_each_entry(wk, &local->work_list, list) { |
1083 | if (!wk->started) | 987 | if (!wk->started) |
1084 | continue; | 988 | continue; |
1085 | if (wk->chan != local->tmp_channel) | 989 | if (wk->chan != local->tmp_channel || |
1086 | continue; | 990 | wk->chan_type != local->tmp_channel_type) |
1087 | if (!ieee80211_work_ct_coexists(wk->chan_type, | ||
1088 | local->tmp_channel_type)) | ||
1089 | continue; | 991 | continue; |
1090 | remain_off_channel = true; | 992 | remain_off_channel = true; |
1091 | } | 993 | } |
1092 | 994 | ||
1093 | if (!remain_off_channel && local->tmp_channel) { | 995 | if (!remain_off_channel && local->tmp_channel) { |
1094 | local->tmp_channel = NULL; | 996 | local->tmp_channel = NULL; |
1095 | /* If tmp_channel wasn't operating channel, then | 997 | ieee80211_hw_config(local, 0); |
1096 | * we need to go back on-channel. | ||
1097 | * NOTE: If we can ever be here while scannning, | ||
1098 | * or if the hw_config() channel config logic changes, | ||
1099 | * then we may need to do a more thorough check to see if | ||
1100 | * we still need to do a hardware config. Currently, | ||
1101 | * we cannot be here while scanning, however. | ||
1102 | */ | ||
1103 | if (!ieee80211_cfg_on_oper_channel(local)) | ||
1104 | ieee80211_hw_config(local, 0); | ||
1105 | 998 | ||
1106 | /* At the least, we need to disable offchannel_ps, | 999 | ieee80211_offchannel_return(local, true); |
1107 | * so just go ahead and run the entire offchannel | ||
1108 | * return logic here. We *could* skip enabling | ||
1109 | * beaconing if we were already on-oper-channel | ||
1110 | * as a future optimization. | ||
1111 | */ | ||
1112 | ieee80211_offchannel_return(local, true, true); | ||
1113 | 1000 | ||
1114 | /* give connection some time to breathe */ | 1001 | /* give connection some time to breathe */ |
1115 | run_again(local, jiffies + HZ/2); | 1002 | run_again(local, jiffies + HZ/2); |