aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-12-23 07:15:42 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-12-28 16:55:03 -0500
commitb8bc4b0aa9bfba755c64b11b8f60e6cfab25dc9d (patch)
tree7f95b0c7d4f847e3c624b67899c7a62f6b73ce26 /net
parent9588bbd5529461a3dacd435bf239c84c3508f569 (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.c24
-rw-r--r--net/mac80211/ieee80211_i.h15
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/mlme.c3
-rw-r--r--net/mac80211/offchannel.c8
-rw-r--r--net/mac80211/work.c134
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
1444static 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
1457static 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
1444struct cfg80211_ops mac80211_config_ops = { 1466struct 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
227enum ieee80211_work_type { 227enum 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);
1162void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); 1171void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata);
1163ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, 1172ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
1164 struct sk_buff *skb); 1173 struct sk_buff *skb);
1174int 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);
1178int 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
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}