diff options
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r-- | net/mac80211/work.c | 122 |
1 files changed, 103 insertions, 19 deletions
diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 36305e0d06e..e73c8cae036 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, |
@@ -126,12 +125,6 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, | |||
126 | 125 | ||
127 | /* determine capability flags */ | 126 | /* determine capability flags */ |
128 | 127 | ||
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) { | 128 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { |
136 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | 129 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
137 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { | 130 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { |
@@ -874,6 +867,44 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, | |||
874 | kfree_skb(skb); | 867 | kfree_skb(skb); |
875 | } | 868 | } |
876 | 869 | ||
870 | static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, | ||
871 | enum nl80211_channel_type oper_ct) | ||
872 | { | ||
873 | switch (wk_ct) { | ||
874 | case NL80211_CHAN_NO_HT: | ||
875 | return true; | ||
876 | case NL80211_CHAN_HT20: | ||
877 | if (oper_ct != NL80211_CHAN_NO_HT) | ||
878 | return true; | ||
879 | return false; | ||
880 | case NL80211_CHAN_HT40MINUS: | ||
881 | case NL80211_CHAN_HT40PLUS: | ||
882 | return (wk_ct == oper_ct); | ||
883 | } | ||
884 | WARN_ON(1); /* shouldn't get here */ | ||
885 | return false; | ||
886 | } | ||
887 | |||
888 | static enum nl80211_channel_type | ||
889 | ieee80211_calc_ct(enum nl80211_channel_type wk_ct, | ||
890 | enum nl80211_channel_type oper_ct) | ||
891 | { | ||
892 | switch (wk_ct) { | ||
893 | case NL80211_CHAN_NO_HT: | ||
894 | return oper_ct; | ||
895 | case NL80211_CHAN_HT20: | ||
896 | if (oper_ct != NL80211_CHAN_NO_HT) | ||
897 | return oper_ct; | ||
898 | return wk_ct; | ||
899 | case NL80211_CHAN_HT40MINUS: | ||
900 | case NL80211_CHAN_HT40PLUS: | ||
901 | return wk_ct; | ||
902 | } | ||
903 | WARN_ON(1); /* shouldn't get here */ | ||
904 | return wk_ct; | ||
905 | } | ||
906 | |||
907 | |||
877 | static void ieee80211_work_timer(unsigned long data) | 908 | static void ieee80211_work_timer(unsigned long data) |
878 | { | 909 | { |
879 | struct ieee80211_local *local = (void *) data; | 910 | struct ieee80211_local *local = (void *) data; |
@@ -924,18 +955,52 @@ static void ieee80211_work_work(struct work_struct *work) | |||
924 | } | 955 | } |
925 | 956 | ||
926 | if (!started && !local->tmp_channel) { | 957 | if (!started && !local->tmp_channel) { |
958 | bool on_oper_chan; | ||
959 | bool tmp_chan_changed = false; | ||
960 | bool on_oper_chan2; | ||
961 | enum nl80211_channel_type wk_ct; | ||
962 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); | ||
963 | |||
964 | /* Work with existing channel type if possible. */ | ||
965 | wk_ct = wk->chan_type; | ||
966 | if (wk->chan == local->hw.conf.channel) | ||
967 | wk_ct = ieee80211_calc_ct(wk->chan_type, | ||
968 | local->hw.conf.channel_type); | ||
969 | |||
970 | if (local->tmp_channel) | ||
971 | if ((local->tmp_channel != wk->chan) || | ||
972 | (local->tmp_channel_type != wk_ct)) | ||
973 | tmp_chan_changed = true; | ||
974 | |||
975 | local->tmp_channel = wk->chan; | ||
976 | local->tmp_channel_type = wk_ct; | ||
927 | /* | 977 | /* |
928 | * TODO: could optimize this by leaving the | 978 | * Leave the station vifs in awake mode if they |
929 | * station vifs in awake mode if they | 979 | * happen to be on the same channel as |
930 | * happen to be on the same channel as | 980 | * the requested channel. |
931 | * the requested channel | ||
932 | */ | 981 | */ |
933 | ieee80211_offchannel_stop_beaconing(local); | 982 | on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); |
934 | ieee80211_offchannel_stop_station(local); | 983 | if (on_oper_chan != on_oper_chan2) { |
984 | if (on_oper_chan2) { | ||
985 | /* going off oper channel, PS too */ | ||
986 | ieee80211_offchannel_stop_vifs(local, | ||
987 | true); | ||
988 | ieee80211_hw_config(local, 0); | ||
989 | } else { | ||
990 | /* going on channel, but leave PS | ||
991 | * off-channel. */ | ||
992 | ieee80211_hw_config(local, 0); | ||
993 | ieee80211_offchannel_return(local, | ||
994 | true, | ||
995 | false); | ||
996 | } | ||
997 | } else if (tmp_chan_changed) | ||
998 | /* Still off-channel, but on some other | ||
999 | * channel, so update hardware. | ||
1000 | * PS should already be off-channel. | ||
1001 | */ | ||
1002 | ieee80211_hw_config(local, 0); | ||
935 | 1003 | ||
936 | local->tmp_channel = wk->chan; | ||
937 | local->tmp_channel_type = wk->chan_type; | ||
938 | ieee80211_hw_config(local, 0); | ||
939 | started = true; | 1004 | started = true; |
940 | wk->timeout = jiffies; | 1005 | wk->timeout = jiffies; |
941 | } | 1006 | } |
@@ -1005,15 +1070,34 @@ static void ieee80211_work_work(struct work_struct *work) | |||
1005 | continue; | 1070 | continue; |
1006 | if (wk->chan != local->tmp_channel) | 1071 | if (wk->chan != local->tmp_channel) |
1007 | continue; | 1072 | continue; |
1008 | if (wk->chan_type != local->tmp_channel_type) | 1073 | if (ieee80211_work_ct_coexists(wk->chan_type, |
1074 | local->tmp_channel_type)) | ||
1009 | continue; | 1075 | continue; |
1010 | remain_off_channel = true; | 1076 | remain_off_channel = true; |
1011 | } | 1077 | } |
1012 | 1078 | ||
1013 | if (!remain_off_channel && local->tmp_channel) { | 1079 | if (!remain_off_channel && local->tmp_channel) { |
1080 | bool on_oper_chan = ieee80211_cfg_on_oper_channel(local); | ||
1014 | local->tmp_channel = NULL; | 1081 | local->tmp_channel = NULL; |
1015 | ieee80211_hw_config(local, 0); | 1082 | /* If tmp_channel wasn't operating channel, then |
1016 | ieee80211_offchannel_return(local, true); | 1083 | * we need to go back on-channel. |
1084 | * NOTE: If we can ever be here while scannning, | ||
1085 | * or if the hw_config() channel config logic changes, | ||
1086 | * then we may need to do a more thorough check to see if | ||
1087 | * we still need to do a hardware config. Currently, | ||
1088 | * we cannot be here while scanning, however. | ||
1089 | */ | ||
1090 | if (ieee80211_cfg_on_oper_channel(local) && !on_oper_chan) | ||
1091 | ieee80211_hw_config(local, 0); | ||
1092 | |||
1093 | /* At the least, we need to disable offchannel_ps, | ||
1094 | * so just go ahead and run the entire offchannel | ||
1095 | * return logic here. We *could* skip enabling | ||
1096 | * beaconing if we were already on-oper-channel | ||
1097 | * as a future optimization. | ||
1098 | */ | ||
1099 | ieee80211_offchannel_return(local, true, true); | ||
1100 | |||
1017 | /* give connection some time to breathe */ | 1101 | /* give connection some time to breathe */ |
1018 | run_again(local, jiffies + HZ/2); | 1102 | run_again(local, jiffies + HZ/2); |
1019 | } | 1103 | } |