diff options
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r-- | net/mac80211/work.c | 138 |
1 files changed, 106 insertions, 32 deletions
diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 36305e0d06ef..d2e7f0e86677 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #define IEEE80211_AUTH_MAX_TRIES 3 | 30 | #define IEEE80211_AUTH_MAX_TRIES 3 |
31 | #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) | 31 | #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) |
32 | #define IEEE80211_ASSOC_MAX_TRIES 3 | 32 | #define IEEE80211_ASSOC_MAX_TRIES 3 |
33 | #define IEEE80211_MAX_PROBE_TRIES 5 | ||
34 | 33 | ||
35 | enum work_action { | 34 | enum work_action { |
36 | WORK_ACT_MISMATCH, | 35 | WORK_ACT_MISMATCH, |
@@ -66,17 +65,9 @@ static void run_again(struct ieee80211_local *local, | |||
66 | mod_timer(&local->work_timer, timeout); | 65 | mod_timer(&local->work_timer, timeout); |
67 | } | 66 | } |
68 | 67 | ||
69 | static void work_free_rcu(struct rcu_head *head) | ||
70 | { | ||
71 | struct ieee80211_work *wk = | ||
72 | container_of(head, struct ieee80211_work, rcu_head); | ||
73 | |||
74 | kfree(wk); | ||
75 | } | ||
76 | |||
77 | void free_work(struct ieee80211_work *wk) | 68 | void free_work(struct ieee80211_work *wk) |
78 | { | 69 | { |
79 | call_rcu(&wk->rcu_head, work_free_rcu); | 70 | kfree_rcu(wk, rcu_head); |
80 | } | 71 | } |
81 | 72 | ||
82 | static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, | 73 | static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, |
@@ -126,12 +117,6 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, | |||
126 | 117 | ||
127 | /* determine capability flags */ | 118 | /* determine capability flags */ |
128 | 119 | ||
129 | if (ieee80211_disable_40mhz_24ghz && | ||
130 | sband->band == IEEE80211_BAND_2GHZ) { | ||
131 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
132 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
133 | } | ||
134 | |||
135 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | 120 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { |
136 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | 121 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
137 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { | 122 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { |
@@ -205,9 +190,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
205 | struct sk_buff *skb; | 190 | struct sk_buff *skb; |
206 | struct ieee80211_mgmt *mgmt; | 191 | struct ieee80211_mgmt *mgmt; |
207 | u8 *pos, qos_info; | 192 | u8 *pos, qos_info; |
208 | const u8 *ies; | ||
209 | size_t offset = 0, noffset; | 193 | size_t offset = 0, noffset; |
210 | int i, len, count, rates_len, supp_rates_len; | 194 | int i, count, rates_len, supp_rates_len; |
211 | u16 capab; | 195 | u16 capab; |
212 | struct ieee80211_supported_band *sband; | 196 | struct ieee80211_supported_band *sband; |
213 | u32 rates = 0; | 197 | u32 rates = 0; |
@@ -292,7 +276,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
292 | } | 276 | } |
293 | 277 | ||
294 | /* SSID */ | 278 | /* SSID */ |
295 | ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len); | 279 | pos = skb_put(skb, 2 + wk->assoc.ssid_len); |
296 | *pos++ = WLAN_EID_SSID; | 280 | *pos++ = WLAN_EID_SSID; |
297 | *pos++ = wk->assoc.ssid_len; | 281 | *pos++ = wk->assoc.ssid_len; |
298 | memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); | 282 | memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); |
@@ -302,7 +286,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
302 | if (supp_rates_len > 8) | 286 | if (supp_rates_len > 8) |
303 | supp_rates_len = 8; | 287 | supp_rates_len = 8; |
304 | 288 | ||
305 | len = sband->n_bitrates; | ||
306 | pos = skb_put(skb, supp_rates_len + 2); | 289 | pos = skb_put(skb, supp_rates_len + 2); |
307 | *pos++ = WLAN_EID_SUPP_RATES; | 290 | *pos++ = WLAN_EID_SUPP_RATES; |
308 | *pos++ = supp_rates_len; | 291 | *pos++ = supp_rates_len; |
@@ -874,6 +857,44 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, | |||
874 | kfree_skb(skb); | 857 | kfree_skb(skb); |
875 | } | 858 | } |
876 | 859 | ||
860 | static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, | ||
861 | enum nl80211_channel_type oper_ct) | ||
862 | { | ||
863 | switch (wk_ct) { | ||
864 | case NL80211_CHAN_NO_HT: | ||
865 | return true; | ||
866 | case NL80211_CHAN_HT20: | ||
867 | if (oper_ct != NL80211_CHAN_NO_HT) | ||
868 | return true; | ||
869 | return false; | ||
870 | case NL80211_CHAN_HT40MINUS: | ||
871 | case NL80211_CHAN_HT40PLUS: | ||
872 | return (wk_ct == oper_ct); | ||
873 | } | ||
874 | WARN_ON(1); /* shouldn't get here */ | ||
875 | return false; | ||
876 | } | ||
877 | |||
878 | static enum nl80211_channel_type | ||
879 | ieee80211_calc_ct(enum nl80211_channel_type wk_ct, | ||
880 | enum nl80211_channel_type oper_ct) | ||
881 | { | ||
882 | switch (wk_ct) { | ||
883 | case NL80211_CHAN_NO_HT: | ||
884 | return oper_ct; | ||
885 | case NL80211_CHAN_HT20: | ||
886 | if (oper_ct != NL80211_CHAN_NO_HT) | ||
887 | return oper_ct; | ||
888 | return wk_ct; | ||
889 | case NL80211_CHAN_HT40MINUS: | ||
890 | case NL80211_CHAN_HT40PLUS: | ||
891 | return wk_ct; | ||
892 | } | ||
893 | WARN_ON(1); /* shouldn't get here */ | ||
894 | return wk_ct; | ||
895 | } | ||
896 | |||
897 | |||
877 | static void ieee80211_work_timer(unsigned long data) | 898 | static void ieee80211_work_timer(unsigned long data) |
878 | { | 899 | { |
879 | struct ieee80211_local *local = (void *) data; | 900 | struct ieee80211_local *local = (void *) data; |
@@ -924,18 +945,52 @@ static void ieee80211_work_work(struct work_struct *work) | |||
924 | } | 945 | } |
925 | 946 | ||
926 | if (!started && !local->tmp_channel) { | 947 | if (!started && !local->tmp_channel) { |
948 | bool on_oper_chan; | ||
949 | bool tmp_chan_changed = false; | ||
950 | bool on_oper_chan2; | ||
951 | enum nl80211_channel_type wk_ct; | ||
952 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); | ||
953 | |||
954 | /* Work with existing channel type if possible. */ | ||
955 | wk_ct = wk->chan_type; | ||
956 | if (wk->chan == local->hw.conf.channel) | ||
957 | wk_ct = ieee80211_calc_ct(wk->chan_type, | ||
958 | local->hw.conf.channel_type); | ||
959 | |||
960 | if (local->tmp_channel) | ||
961 | if ((local->tmp_channel != wk->chan) || | ||
962 | (local->tmp_channel_type != wk_ct)) | ||
963 | tmp_chan_changed = true; | ||
964 | |||
965 | local->tmp_channel = wk->chan; | ||
966 | local->tmp_channel_type = wk_ct; | ||
927 | /* | 967 | /* |
928 | * TODO: could optimize this by leaving the | 968 | * Leave the station vifs in awake mode if they |
929 | * station vifs in awake mode if they | 969 | * happen to be on the same channel as |
930 | * happen to be on the same channel as | 970 | * the requested channel. |
931 | * the requested channel | ||
932 | */ | 971 | */ |
933 | ieee80211_offchannel_stop_beaconing(local); | 972 | on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); |
934 | ieee80211_offchannel_stop_station(local); | 973 | if (on_oper_chan != on_oper_chan2) { |
974 | if (on_oper_chan2) { | ||
975 | /* going off oper channel, PS too */ | ||
976 | ieee80211_offchannel_stop_vifs(local, | ||
977 | true); | ||
978 | ieee80211_hw_config(local, 0); | ||
979 | } else { | ||
980 | /* going on channel, but leave PS | ||
981 | * off-channel. */ | ||
982 | ieee80211_hw_config(local, 0); | ||
983 | ieee80211_offchannel_return(local, | ||
984 | true, | ||
985 | false); | ||
986 | } | ||
987 | } else if (tmp_chan_changed) | ||
988 | /* Still off-channel, but on some other | ||
989 | * channel, so update hardware. | ||
990 | * PS should already be off-channel. | ||
991 | */ | ||
992 | ieee80211_hw_config(local, 0); | ||
935 | 993 | ||
936 | local->tmp_channel = wk->chan; | ||
937 | local->tmp_channel_type = wk->chan_type; | ||
938 | ieee80211_hw_config(local, 0); | ||
939 | started = true; | 994 | started = true; |
940 | wk->timeout = jiffies; | 995 | wk->timeout = jiffies; |
941 | } | 996 | } |
@@ -1005,15 +1060,34 @@ static void ieee80211_work_work(struct work_struct *work) | |||
1005 | continue; | 1060 | continue; |
1006 | if (wk->chan != local->tmp_channel) | 1061 | if (wk->chan != local->tmp_channel) |
1007 | continue; | 1062 | continue; |
1008 | if (wk->chan_type != local->tmp_channel_type) | 1063 | if (ieee80211_work_ct_coexists(wk->chan_type, |
1064 | local->tmp_channel_type)) | ||
1009 | continue; | 1065 | continue; |
1010 | remain_off_channel = true; | 1066 | remain_off_channel = true; |
1011 | } | 1067 | } |
1012 | 1068 | ||
1013 | if (!remain_off_channel && local->tmp_channel) { | 1069 | if (!remain_off_channel && local->tmp_channel) { |
1070 | bool on_oper_chan = ieee80211_cfg_on_oper_channel(local); | ||
1014 | local->tmp_channel = NULL; | 1071 | local->tmp_channel = NULL; |
1015 | ieee80211_hw_config(local, 0); | 1072 | /* If tmp_channel wasn't operating channel, then |
1016 | ieee80211_offchannel_return(local, true); | 1073 | * we need to go back on-channel. |
1074 | * NOTE: If we can ever be here while scannning, | ||
1075 | * or if the hw_config() channel config logic changes, | ||
1076 | * then we may need to do a more thorough check to see if | ||
1077 | * we still need to do a hardware config. Currently, | ||
1078 | * we cannot be here while scanning, however. | ||
1079 | */ | ||
1080 | if (ieee80211_cfg_on_oper_channel(local) && !on_oper_chan) | ||
1081 | ieee80211_hw_config(local, 0); | ||
1082 | |||
1083 | /* At the least, we need to disable offchannel_ps, | ||
1084 | * so just go ahead and run the entire offchannel | ||
1085 | * return logic here. We *could* skip enabling | ||
1086 | * beaconing if we were already on-oper-channel | ||
1087 | * as a future optimization. | ||
1088 | */ | ||
1089 | ieee80211_offchannel_return(local, true, true); | ||
1090 | |||
1017 | /* give connection some time to breathe */ | 1091 | /* give connection some time to breathe */ |
1018 | run_again(local, jiffies + HZ/2); | 1092 | run_again(local, jiffies + HZ/2); |
1019 | } | 1093 | } |