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 | |
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')
-rw-r--r-- | net/mac80211/cfg.c | 24 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 15 | ||||
-rw-r--r-- | net/mac80211/main.c | 3 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 3 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 8 | ||||
-rw-r--r-- | net/mac80211/work.c | 134 |
6 files changed, 181 insertions, 6 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ea862dfc08ed..2e5e841e9b7b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1441,6 +1441,28 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, | |||
1441 | return -EINVAL; | 1441 | return -EINVAL; |
1442 | } | 1442 | } |
1443 | 1443 | ||
1444 | static int ieee80211_remain_on_channel(struct wiphy *wiphy, | ||
1445 | struct net_device *dev, | ||
1446 | struct ieee80211_channel *chan, | ||
1447 | enum nl80211_channel_type channel_type, | ||
1448 | unsigned int duration, | ||
1449 | u64 *cookie) | ||
1450 | { | ||
1451 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
1452 | |||
1453 | return ieee80211_wk_remain_on_channel(sdata, chan, channel_type, | ||
1454 | duration, cookie); | ||
1455 | } | ||
1456 | |||
1457 | static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, | ||
1458 | struct net_device *dev, | ||
1459 | u64 cookie) | ||
1460 | { | ||
1461 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
1462 | |||
1463 | return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); | ||
1464 | } | ||
1465 | |||
1444 | struct cfg80211_ops mac80211_config_ops = { | 1466 | struct cfg80211_ops mac80211_config_ops = { |
1445 | .add_virtual_intf = ieee80211_add_iface, | 1467 | .add_virtual_intf = ieee80211_add_iface, |
1446 | .del_virtual_intf = ieee80211_del_iface, | 1468 | .del_virtual_intf = ieee80211_del_iface, |
@@ -1487,4 +1509,6 @@ struct cfg80211_ops mac80211_config_ops = { | |||
1487 | CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) | 1509 | CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) |
1488 | .set_power_mgmt = ieee80211_set_power_mgmt, | 1510 | .set_power_mgmt = ieee80211_set_power_mgmt, |
1489 | .set_bitrate_mask = ieee80211_set_bitrate_mask, | 1511 | .set_bitrate_mask = ieee80211_set_bitrate_mask, |
1512 | .remain_on_channel = ieee80211_remain_on_channel, | ||
1513 | .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, | ||
1490 | }; | 1514 | }; |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fd912eb5309e..23547ebacf3d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -225,9 +225,11 @@ struct mesh_preq_queue { | |||
225 | }; | 225 | }; |
226 | 226 | ||
227 | enum ieee80211_work_type { | 227 | enum ieee80211_work_type { |
228 | IEEE80211_WORK_ABORT, | ||
228 | IEEE80211_WORK_DIRECT_PROBE, | 229 | IEEE80211_WORK_DIRECT_PROBE, |
229 | IEEE80211_WORK_AUTH, | 230 | IEEE80211_WORK_AUTH, |
230 | IEEE80211_WORK_ASSOC, | 231 | IEEE80211_WORK_ASSOC, |
232 | IEEE80211_WORK_REMAIN_ON_CHANNEL, | ||
231 | }; | 233 | }; |
232 | 234 | ||
233 | /** | 235 | /** |
@@ -283,6 +285,9 @@ struct ieee80211_work { | |||
283 | u8 supp_rates_len; | 285 | u8 supp_rates_len; |
284 | bool wmm_used, use_11n; | 286 | bool wmm_used, use_11n; |
285 | } assoc; | 287 | } assoc; |
288 | struct { | ||
289 | unsigned long timeout; | ||
290 | } remain; | ||
286 | }; | 291 | }; |
287 | 292 | ||
288 | int ie_len; | 293 | int ie_len; |
@@ -729,6 +734,10 @@ struct ieee80211_local { | |||
729 | enum nl80211_channel_type oper_channel_type; | 734 | enum nl80211_channel_type oper_channel_type; |
730 | struct ieee80211_channel *oper_channel, *csa_channel; | 735 | struct ieee80211_channel *oper_channel, *csa_channel; |
731 | 736 | ||
737 | /* Temporary remain-on-channel for off-channel operations */ | ||
738 | struct ieee80211_channel *tmp_channel; | ||
739 | enum nl80211_channel_type tmp_channel_type; | ||
740 | |||
732 | /* SNMP counters */ | 741 | /* SNMP counters */ |
733 | /* dot11CountersTable */ | 742 | /* dot11CountersTable */ |
734 | u32 dot11TransmittedFragmentCount; | 743 | u32 dot11TransmittedFragmentCount; |
@@ -1162,6 +1171,12 @@ void free_work(struct ieee80211_work *wk); | |||
1162 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); | 1171 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); |
1163 | ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, | 1172 | ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, |
1164 | struct sk_buff *skb); | 1173 | struct sk_buff *skb); |
1174 | int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, | ||
1175 | struct ieee80211_channel *chan, | ||
1176 | enum nl80211_channel_type channel_type, | ||
1177 | unsigned int duration, u64 *cookie); | ||
1178 | int ieee80211_wk_cancel_remain_on_channel( | ||
1179 | struct ieee80211_sub_if_data *sdata, u64 cookie); | ||
1165 | 1180 | ||
1166 | #ifdef CONFIG_MAC80211_NOINLINE | 1181 | #ifdef CONFIG_MAC80211_NOINLINE |
1167 | #define debug_noinline noinline | 1182 | #define debug_noinline noinline |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5fcd3548417e..d0a14d953f08 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -107,6 +107,9 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | |||
107 | if (scan_chan) { | 107 | if (scan_chan) { |
108 | chan = scan_chan; | 108 | chan = scan_chan; |
109 | channel_type = NL80211_CHAN_NO_HT; | 109 | channel_type = NL80211_CHAN_NO_HT; |
110 | } else if (local->tmp_channel) { | ||
111 | chan = scan_chan = local->tmp_channel; | ||
112 | channel_type = local->tmp_channel_type; | ||
110 | } else { | 113 | } else { |
111 | chan = local->oper_channel; | 114 | chan = local->oper_channel; |
112 | channel_type = local->oper_channel_type; | 115 | channel_type = local->oper_channel_type; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e44f1ed0b0da..32d6e6614f9f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -857,6 +857,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, | |||
857 | if (sdata->local->scanning) | 857 | if (sdata->local->scanning) |
858 | return; | 858 | return; |
859 | 859 | ||
860 | if (sdata->local->tmp_channel) | ||
861 | return; | ||
862 | |||
860 | mutex_lock(&ifmgd->mtx); | 863 | mutex_lock(&ifmgd->mtx); |
861 | 864 | ||
862 | if (!ifmgd->associated) | 865 | if (!ifmgd->associated) |
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 2cd880e444d1..a7bbfc40a648 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c | |||
@@ -106,9 +106,13 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) | |||
106 | /* | 106 | /* |
107 | * only handle non-STA interfaces here, STA interfaces | 107 | * only handle non-STA interfaces here, STA interfaces |
108 | * are handled in ieee80211_offchannel_stop_station(), | 108 | * are handled in ieee80211_offchannel_stop_station(), |
109 | * e.g., from the background scan state machine | 109 | * e.g., from the background scan state machine. |
110 | * | ||
111 | * In addition, do not stop monitor interface to allow it to be | ||
112 | * used from user space controlled off-channel operations. | ||
110 | */ | 113 | */ |
111 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 114 | if (sdata->vif.type != NL80211_IFTYPE_STATION && |
115 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
112 | netif_stop_queue(sdata->dev); | 116 | netif_stop_queue(sdata->dev); |
113 | } | 117 | } |
114 | mutex_unlock(&local->iflist_mtx); | 118 | mutex_unlock(&local->iflist_mtx); |
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 | } | ||