aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/work.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r--net/mac80211/work.c134
1 files changed, 130 insertions, 4 deletions
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 0b8c31c600aa..0acea7cf714a 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -538,6 +538,44 @@ ieee80211_associate(struct ieee80211_work *wk)
538 return WORK_ACT_NONE; 538 return WORK_ACT_NONE;
539} 539}
540 540
541static enum work_action __must_check
542ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
543{
544 struct ieee80211_sub_if_data *sdata = wk->sdata;
545 struct ieee80211_local *local = sdata->local;
546
547 /*
548 * First time we run, do nothing -- the generic code will
549 * have switched to the right channel etc.
550 */
551 if (wk->timeout != wk->remain.timeout) {
552 wk->timeout = wk->remain.timeout;
553 return WORK_ACT_NONE;
554 }
555
556 /*
557 * We are done serving the remain-on-channel command; kill the work
558 * item to allow idle state to be entered again. In addition, clear the
559 * temporary channel information to allow operational channel to be
560 * used.
561 */
562 list_del(&wk->list);
563 free_work(wk);
564
565 if (local->tmp_channel) {
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 }
575
576 return WORK_ACT_NONE;
577}
578
541static void ieee80211_auth_challenge(struct ieee80211_work *wk, 579static void ieee80211_auth_challenge(struct ieee80211_work *wk,
542 struct ieee80211_mgmt *mgmt, 580 struct ieee80211_mgmt *mgmt,
543 size_t len) 581 size_t len)
@@ -825,6 +863,8 @@ static void ieee80211_work_work(struct work_struct *work)
825 /* nothing */ 863 /* nothing */
826 rma = WORK_ACT_NONE; 864 rma = WORK_ACT_NONE;
827 break; 865 break;
866 case IEEE80211_WORK_ABORT:
867 rma = WORK_ACT_TIMEOUT;
828 case IEEE80211_WORK_DIRECT_PROBE: 868 case IEEE80211_WORK_DIRECT_PROBE:
829 rma = ieee80211_direct_probe(wk); 869 rma = ieee80211_direct_probe(wk);
830 break; 870 break;
@@ -834,6 +874,9 @@ static void ieee80211_work_work(struct work_struct *work)
834 case IEEE80211_WORK_ASSOC: 874 case IEEE80211_WORK_ASSOC:
835 rma = ieee80211_associate(wk); 875 rma = ieee80211_associate(wk);
836 break; 876 break;
877 case IEEE80211_WORK_REMAIN_ON_CHANNEL:
878 rma = ieee80211_remain_on_channel_timeout(wk);
879 break;
837 } 880 }
838 881
839 switch (rma) { 882 switch (rma) {
@@ -900,14 +943,25 @@ void ieee80211_work_init(struct ieee80211_local *local)
900void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) 943void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
901{ 944{
902 struct ieee80211_local *local = sdata->local; 945 struct ieee80211_local *local = sdata->local;
903 struct ieee80211_work *wk, *tmp; 946 struct ieee80211_work *wk;
904 947
905 mutex_lock(&local->work_mtx); 948 mutex_lock(&local->work_mtx);
906 list_for_each_entry_safe(wk, tmp, &local->work_list, list) { 949 list_for_each_entry(wk, &local->work_list, list) {
907 if (wk->sdata != sdata) 950 if (wk->sdata != sdata)
908 continue; 951 continue;
909 list_del(&wk->list); 952 wk->type = IEEE80211_WORK_ABORT;
910 free_work(wk); 953 }
954 mutex_unlock(&local->work_mtx);
955
956 /* run cleanups etc. */
957 ieee80211_work_work(&local->work_work);
958
959 mutex_lock(&local->work_mtx);
960 list_for_each_entry(wk, &local->work_list, list) {
961 if (wk->sdata != sdata)
962 continue;
963 WARN_ON(1);
964 break;
911 } 965 }
912 mutex_unlock(&local->work_mtx); 966 mutex_unlock(&local->work_mtx);
913} 967}
@@ -949,3 +1003,75 @@ ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
949 1003
950 return RX_CONTINUE; 1004 return RX_CONTINUE;
951} 1005}
1006
1007int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1008 struct ieee80211_channel *chan,
1009 enum nl80211_channel_type channel_type,
1010 unsigned int duration, u64 *cookie)
1011{
1012 struct ieee80211_local *local = sdata->local;
1013 struct ieee80211_work *wk;
1014
1015 wk = kzalloc(sizeof(*wk), GFP_KERNEL);
1016 if (!wk)
1017 return -ENOMEM;
1018
1019 wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
1020 wk->chan = chan;
1021 wk->sdata = sdata;
1022
1023 wk->remain.timeout = jiffies + msecs_to_jiffies(duration);
1024
1025 *cookie = (u64)wk;
1026
1027 ieee80211_add_work(wk);
1028
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;
1044}
1045
1046int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1047 u64 cookie)
1048{
1049 struct ieee80211_local *local = sdata->local;
1050 struct ieee80211_work *wk, *tmp;
1051 bool found = false;
1052
1053 mutex_lock(&local->work_mtx);
1054 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
1055 if ((u64)wk == cookie) {
1056 found = true;
1057 list_del(&wk->list);
1058 free_work(wk);
1059 break;
1060 }
1061 }
1062 mutex_unlock(&local->work_mtx);
1063
1064 if (!found)
1065 return -ENOENT;
1066
1067 if (sdata->local->tmp_channel) {
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
1076 return 0;
1077}