diff options
-rw-r--r-- | net/mac80211/ieee80211_i.h | 7 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 14 | ||||
-rw-r--r-- | net/mac80211/work.c | 135 |
3 files changed, 88 insertions, 68 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 23547ebacf3d..a27921ee6e63 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -255,13 +255,15 @@ struct ieee80211_work { | |||
255 | struct sk_buff *skb); | 255 | struct sk_buff *skb); |
256 | 256 | ||
257 | struct ieee80211_channel *chan; | 257 | struct ieee80211_channel *chan; |
258 | /* XXX: chan type? -- right now not really needed */ | 258 | enum nl80211_channel_type chan_type; |
259 | 259 | ||
260 | unsigned long timeout; | 260 | unsigned long timeout; |
261 | enum ieee80211_work_type type; | 261 | enum ieee80211_work_type type; |
262 | 262 | ||
263 | u8 filter_ta[ETH_ALEN]; | 263 | u8 filter_ta[ETH_ALEN]; |
264 | 264 | ||
265 | bool started; | ||
266 | |||
265 | union { | 267 | union { |
266 | struct { | 268 | struct { |
267 | int tries; | 269 | int tries; |
@@ -286,7 +288,8 @@ struct ieee80211_work { | |||
286 | bool wmm_used, use_11n; | 288 | bool wmm_used, use_11n; |
287 | } assoc; | 289 | } assoc; |
288 | struct { | 290 | struct { |
289 | unsigned long timeout; | 291 | u32 duration; |
292 | bool started; | ||
290 | } remain; | 293 | } remain; |
291 | }; | 294 | }; |
292 | 295 | ||
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 32d6e6614f9f..72920ee07885 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -1105,6 +1105,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, | |||
1105 | else | 1105 | else |
1106 | ieee80211_set_wmm_default(sdata); | 1106 | ieee80211_set_wmm_default(sdata); |
1107 | 1107 | ||
1108 | local->oper_channel = wk->chan; | ||
1109 | |||
1108 | if (elems.ht_info_elem && elems.wmm_param && | 1110 | if (elems.ht_info_elem && elems.wmm_param && |
1109 | (sdata->local->hw.queues >= 4) && | 1111 | (sdata->local->hw.queues >= 4) && |
1110 | !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 1112 | !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) |
@@ -1797,15 +1799,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
1797 | wk->sdata = sdata; | 1799 | wk->sdata = sdata; |
1798 | wk->done = ieee80211_probe_auth_done; | 1800 | wk->done = ieee80211_probe_auth_done; |
1799 | 1801 | ||
1800 | /* | ||
1801 | * XXX: if still associated need to tell AP that we're going | ||
1802 | * to sleep and then change channel etc. | ||
1803 | * For now switch channel here, later will be handled | ||
1804 | * by submitting this as an off-channel work item. | ||
1805 | */ | ||
1806 | sdata->local->oper_channel = req->bss->channel; | ||
1807 | ieee80211_hw_config(sdata->local, 0); | ||
1808 | |||
1809 | ieee80211_add_work(wk); | 1802 | ieee80211_add_work(wk); |
1810 | return 0; | 1803 | return 0; |
1811 | } | 1804 | } |
@@ -1929,9 +1922,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
1929 | else | 1922 | else |
1930 | ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; | 1923 | ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; |
1931 | 1924 | ||
1932 | sdata->local->oper_channel = req->bss->channel; | ||
1933 | ieee80211_hw_config(sdata->local, 0); | ||
1934 | |||
1935 | ieee80211_add_work(wk); | 1925 | ieee80211_add_work(wk); |
1936 | return 0; | 1926 | return 0; |
1937 | } | 1927 | } |
diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 0acea7cf714a..0bffb6a42534 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c | |||
@@ -541,39 +541,22 @@ ieee80211_associate(struct ieee80211_work *wk) | |||
541 | static enum work_action __must_check | 541 | static enum work_action __must_check |
542 | ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) | 542 | ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) |
543 | { | 543 | { |
544 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
545 | struct ieee80211_local *local = sdata->local; | ||
546 | |||
547 | /* | 544 | /* |
548 | * First time we run, do nothing -- the generic code will | 545 | * First time we run, do nothing -- the generic code will |
549 | * have switched to the right channel etc. | 546 | * have switched to the right channel etc. |
550 | */ | 547 | */ |
551 | if (wk->timeout != wk->remain.timeout) { | 548 | if (!wk->remain.started) { |
552 | wk->timeout = wk->remain.timeout; | 549 | wk->remain.started = true; |
553 | return WORK_ACT_NONE; | 550 | wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); |
554 | } | ||
555 | 551 | ||
556 | /* | 552 | cfg80211_ready_on_channel(wk->sdata->dev, (u64)wk, wk->chan, |
557 | * We are done serving the remain-on-channel command; kill the work | 553 | wk->chan_type, wk->remain.duration, |
558 | * item to allow idle state to be entered again. In addition, clear the | 554 | GFP_KERNEL); |
559 | * temporary channel information to allow operational channel to be | ||
560 | * used. | ||
561 | */ | ||
562 | list_del(&wk->list); | ||
563 | free_work(wk); | ||
564 | 555 | ||
565 | if (local->tmp_channel) { | 556 | return WORK_ACT_NONE; |
566 | cfg80211_remain_on_channel_expired(sdata->dev, (u64)wk, | ||
567 | local->tmp_channel, | ||
568 | local->tmp_channel_type, | ||
569 | GFP_KERNEL); | ||
570 | |||
571 | local->tmp_channel = NULL; | ||
572 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
573 | ieee80211_offchannel_return(local, true); | ||
574 | } | 557 | } |
575 | 558 | ||
576 | return WORK_ACT_NONE; | 559 | return WORK_ACT_TIMEOUT; |
577 | } | 560 | } |
578 | 561 | ||
579 | static void ieee80211_auth_challenge(struct ieee80211_work *wk, | 562 | static void ieee80211_auth_challenge(struct ieee80211_work *wk, |
@@ -799,7 +782,7 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, | |||
799 | break; | 782 | break; |
800 | case WORK_DONE_REQUEUE: | 783 | case WORK_DONE_REQUEUE: |
801 | synchronize_rcu(); | 784 | synchronize_rcu(); |
802 | wk->timeout = jiffies; /* run again directly */ | 785 | wk->started = false; /* restart */ |
803 | mutex_lock(&local->work_mtx); | 786 | mutex_lock(&local->work_mtx); |
804 | list_add_tail(&wk->list, &local->work_list); | 787 | list_add_tail(&wk->list, &local->work_list); |
805 | mutex_unlock(&local->work_mtx); | 788 | mutex_unlock(&local->work_mtx); |
@@ -827,6 +810,7 @@ static void ieee80211_work_work(struct work_struct *work) | |||
827 | struct ieee80211_work *wk, *tmp; | 810 | struct ieee80211_work *wk, *tmp; |
828 | LIST_HEAD(free_work); | 811 | LIST_HEAD(free_work); |
829 | enum work_action rma; | 812 | enum work_action rma; |
813 | bool remain_off_channel = false; | ||
830 | 814 | ||
831 | if (local->scanning) | 815 | if (local->scanning) |
832 | return; | 816 | return; |
@@ -847,6 +831,34 @@ static void ieee80211_work_work(struct work_struct *work) | |||
847 | mutex_lock(&local->work_mtx); | 831 | mutex_lock(&local->work_mtx); |
848 | 832 | ||
849 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { | 833 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { |
834 | /* mark work as started if it's on the current off-channel */ | ||
835 | if (!wk->started && local->tmp_channel && | ||
836 | wk->chan == local->tmp_channel && | ||
837 | wk->chan_type == local->tmp_channel_type) { | ||
838 | wk->started = true; | ||
839 | } | ||
840 | |||
841 | if (!wk->started && !local->tmp_channel) { | ||
842 | /* | ||
843 | * TODO: could optimize this by leaving the | ||
844 | * station vifs in awake mode if they | ||
845 | * happen to be on the same channel as | ||
846 | * the requested channel | ||
847 | */ | ||
848 | ieee80211_offchannel_stop_beaconing(local); | ||
849 | ieee80211_offchannel_stop_station(local); | ||
850 | |||
851 | local->tmp_channel = wk->chan; | ||
852 | local->tmp_channel_type = wk->chan_type; | ||
853 | ieee80211_hw_config(local, 0); | ||
854 | wk->started = true; | ||
855 | wk->timeout = jiffies; | ||
856 | } | ||
857 | |||
858 | /* don't try to work with items that aren't started */ | ||
859 | if (!wk->started) | ||
860 | continue; | ||
861 | |||
850 | if (time_is_after_jiffies(wk->timeout)) { | 862 | if (time_is_after_jiffies(wk->timeout)) { |
851 | /* | 863 | /* |
852 | * This work item isn't supposed to be worked on | 864 | * This work item isn't supposed to be worked on |
@@ -881,7 +893,8 @@ static void ieee80211_work_work(struct work_struct *work) | |||
881 | 893 | ||
882 | switch (rma) { | 894 | switch (rma) { |
883 | case WORK_ACT_NONE: | 895 | case WORK_ACT_NONE: |
884 | /* no action required */ | 896 | /* might have changed the timeout */ |
897 | run_again(local, wk->timeout); | ||
885 | break; | 898 | break; |
886 | case WORK_ACT_TIMEOUT: | 899 | case WORK_ACT_TIMEOUT: |
887 | list_del_rcu(&wk->list); | 900 | list_del_rcu(&wk->list); |
@@ -893,6 +906,24 @@ static void ieee80211_work_work(struct work_struct *work) | |||
893 | } | 906 | } |
894 | } | 907 | } |
895 | 908 | ||
909 | list_for_each_entry(wk, &local->work_list, list) { | ||
910 | if (!wk->started) | ||
911 | continue; | ||
912 | if (wk->chan != local->tmp_channel) | ||
913 | continue; | ||
914 | if (wk->chan_type != local->tmp_channel_type) | ||
915 | continue; | ||
916 | remain_off_channel = true; | ||
917 | } | ||
918 | |||
919 | if (!remain_off_channel && local->tmp_channel) { | ||
920 | local->tmp_channel = NULL; | ||
921 | ieee80211_hw_config(local, 0); | ||
922 | ieee80211_offchannel_return(local, true); | ||
923 | /* give connection some time to breathe */ | ||
924 | run_again(local, jiffies + HZ/2); | ||
925 | } | ||
926 | |||
896 | if (list_empty(&local->work_list) && local->scan_req) | 927 | if (list_empty(&local->work_list) && local->scan_req) |
897 | ieee80211_queue_delayed_work(&local->hw, | 928 | ieee80211_queue_delayed_work(&local->hw, |
898 | &local->scan_work, | 929 | &local->scan_work, |
@@ -900,6 +931,8 @@ static void ieee80211_work_work(struct work_struct *work) | |||
900 | 931 | ||
901 | mutex_unlock(&local->work_mtx); | 932 | mutex_unlock(&local->work_mtx); |
902 | 933 | ||
934 | ieee80211_recalc_idle(local); | ||
935 | |||
903 | list_for_each_entry_safe(wk, tmp, &free_work, list) { | 936 | list_for_each_entry_safe(wk, tmp, &free_work, list) { |
904 | wk->done(wk, NULL); | 937 | wk->done(wk, NULL); |
905 | list_del(&wk->list); | 938 | list_del(&wk->list); |
@@ -920,7 +953,7 @@ void ieee80211_add_work(struct ieee80211_work *wk) | |||
920 | if (WARN_ON(!wk->done)) | 953 | if (WARN_ON(!wk->done)) |
921 | return; | 954 | return; |
922 | 955 | ||
923 | wk->timeout = jiffies; | 956 | wk->started = false; |
924 | 957 | ||
925 | local = wk->sdata->local; | 958 | local = wk->sdata->local; |
926 | mutex_lock(&local->work_mtx); | 959 | mutex_lock(&local->work_mtx); |
@@ -950,6 +983,8 @@ void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) | |||
950 | if (wk->sdata != sdata) | 983 | if (wk->sdata != sdata) |
951 | continue; | 984 | continue; |
952 | wk->type = IEEE80211_WORK_ABORT; | 985 | wk->type = IEEE80211_WORK_ABORT; |
986 | wk->started = true; | ||
987 | wk->timeout = jiffies; | ||
953 | } | 988 | } |
954 | mutex_unlock(&local->work_mtx); | 989 | mutex_unlock(&local->work_mtx); |
955 | 990 | ||
@@ -1004,12 +1039,24 @@ ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1004 | return RX_CONTINUE; | 1039 | return RX_CONTINUE; |
1005 | } | 1040 | } |
1006 | 1041 | ||
1042 | static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, | ||
1043 | struct sk_buff *skb) | ||
1044 | { | ||
1045 | /* | ||
1046 | * We are done serving the remain-on-channel command. | ||
1047 | */ | ||
1048 | cfg80211_remain_on_channel_expired(wk->sdata->dev, (u64)wk, | ||
1049 | wk->chan, wk->chan_type, | ||
1050 | GFP_KERNEL); | ||
1051 | |||
1052 | return WORK_DONE_DESTROY; | ||
1053 | } | ||
1054 | |||
1007 | int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, | 1055 | int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, |
1008 | struct ieee80211_channel *chan, | 1056 | struct ieee80211_channel *chan, |
1009 | enum nl80211_channel_type channel_type, | 1057 | enum nl80211_channel_type channel_type, |
1010 | unsigned int duration, u64 *cookie) | 1058 | unsigned int duration, u64 *cookie) |
1011 | { | 1059 | { |
1012 | struct ieee80211_local *local = sdata->local; | ||
1013 | struct ieee80211_work *wk; | 1060 | struct ieee80211_work *wk; |
1014 | 1061 | ||
1015 | wk = kzalloc(sizeof(*wk), GFP_KERNEL); | 1062 | wk = kzalloc(sizeof(*wk), GFP_KERNEL); |
@@ -1018,28 +1065,16 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, | |||
1018 | 1065 | ||
1019 | wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; | 1066 | wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; |
1020 | wk->chan = chan; | 1067 | wk->chan = chan; |
1068 | wk->chan_type = channel_type; | ||
1021 | wk->sdata = sdata; | 1069 | wk->sdata = sdata; |
1070 | wk->done = ieee80211_remain_done; | ||
1022 | 1071 | ||
1023 | wk->remain.timeout = jiffies + msecs_to_jiffies(duration); | 1072 | wk->remain.duration = duration; |
1024 | 1073 | ||
1025 | *cookie = (u64)wk; | 1074 | *cookie = (u64)wk; |
1026 | 1075 | ||
1027 | ieee80211_add_work(wk); | 1076 | ieee80211_add_work(wk); |
1028 | 1077 | ||
1029 | /* | ||
1030 | * TODO: could optimize this by leaving the station vifs in awake mode | ||
1031 | * if they happen to be on the same channel as the requested channel | ||
1032 | */ | ||
1033 | ieee80211_offchannel_stop_beaconing(local); | ||
1034 | ieee80211_offchannel_stop_station(local); | ||
1035 | |||
1036 | sdata->local->tmp_channel = chan; | ||
1037 | sdata->local->tmp_channel_type = channel_type; | ||
1038 | ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
1039 | |||
1040 | cfg80211_ready_on_channel(sdata->dev, (u64)wk, chan, channel_type, | ||
1041 | duration, GFP_KERNEL); | ||
1042 | |||
1043 | return 0; | 1078 | return 0; |
1044 | } | 1079 | } |
1045 | 1080 | ||
@@ -1053,9 +1088,8 @@ int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, | |||
1053 | mutex_lock(&local->work_mtx); | 1088 | mutex_lock(&local->work_mtx); |
1054 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { | 1089 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { |
1055 | if ((u64)wk == cookie) { | 1090 | if ((u64)wk == cookie) { |
1091 | wk->timeout = jiffies; | ||
1056 | found = true; | 1092 | found = true; |
1057 | list_del(&wk->list); | ||
1058 | free_work(wk); | ||
1059 | break; | 1093 | break; |
1060 | } | 1094 | } |
1061 | } | 1095 | } |
@@ -1064,14 +1098,7 @@ int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, | |||
1064 | if (!found) | 1098 | if (!found) |
1065 | return -ENOENT; | 1099 | return -ENOENT; |
1066 | 1100 | ||
1067 | if (sdata->local->tmp_channel) { | 1101 | ieee80211_queue_work(&local->hw, &local->work_work); |
1068 | sdata->local->tmp_channel = NULL; | ||
1069 | ieee80211_hw_config(sdata->local, | ||
1070 | IEEE80211_CONF_CHANGE_CHANNEL); | ||
1071 | ieee80211_offchannel_return(sdata->local, true); | ||
1072 | } | ||
1073 | |||
1074 | ieee80211_recalc_idle(local); | ||
1075 | 1102 | ||
1076 | return 0; | 1103 | return 0; |
1077 | } | 1104 | } |