diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-12-23 07:15:42 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-12-28 16:55:03 -0500 |
commit | b8bc4b0aa9bfba755c64b11b8f60e6cfab25dc9d (patch) | |
tree | 7f95b0c7d4f847e3c624b67899c7a62f6b73ce26 /net/mac80211/work.c | |
parent | 9588bbd5529461a3dacd435bf239c84c3508f569 (diff) |
mac80211: support remain-on-channel command
This implements the new remain-on-channel cfg80211
command in mac80211, extending the work interface.
Also change the work purge code to be able to clean
up events properly (pretending they timed out.)
Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r-- | net/mac80211/work.c | 134 |
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 | ||
541 | static enum work_action __must_check | ||
542 | ieee80211_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 | |||
541 | static void ieee80211_auth_challenge(struct ieee80211_work *wk, | 579 | static 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) | |||
900 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) | 943 | void 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 | |||
1007 | int 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 | |||
1046 | int 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 | } | ||