xref: /linux/net/mac80211/offchannel.c (revision cb299ba8b5ef2239429484072fea394cd7581bd7)
1 /*
2  * Off-channel operation helpers
3  *
4  * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
5  * Copyright 2004, Instant802 Networks, Inc.
6  * Copyright 2005, Devicescape Software, Inc.
7  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
8  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
9  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15 #include <net/mac80211.h>
16 #include "ieee80211_i.h"
17 
18 /*
19  * inform AP that we will go to sleep so that it will buffer the frames
20  * while we scan
21  */
22 static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
23 {
24 	struct ieee80211_local *local = sdata->local;
25 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
26 
27 	local->offchannel_ps_enabled = false;
28 
29 	/* FIXME: what to do when local->pspolling is true? */
30 
31 	del_timer_sync(&local->dynamic_ps_timer);
32 	del_timer_sync(&ifmgd->bcn_mon_timer);
33 	del_timer_sync(&ifmgd->conn_mon_timer);
34 
35 	cancel_work_sync(&local->dynamic_ps_enable_work);
36 
37 	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
38 		local->offchannel_ps_enabled = true;
39 		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
40 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
41 	}
42 
43 	if (!(local->offchannel_ps_enabled) ||
44 	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
45 		/*
46 		 * If power save was enabled, no need to send a nullfunc
47 		 * frame because AP knows that we are sleeping. But if the
48 		 * hardware is creating the nullfunc frame for power save
49 		 * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
50 		 * enabled) and power save was enabled, the firmware just
51 		 * sent a null frame with power save disabled. So we need
52 		 * to send a new nullfunc frame to inform the AP that we
53 		 * are again sleeping.
54 		 */
55 		ieee80211_send_nullfunc(local, sdata, 1);
56 }
57 
58 /* inform AP that we are awake again, unless power save is enabled */
59 static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
60 {
61 	struct ieee80211_local *local = sdata->local;
62 
63 	if (!local->ps_sdata)
64 		ieee80211_send_nullfunc(local, sdata, 0);
65 	else if (local->offchannel_ps_enabled) {
66 		/*
67 		 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
68 		 * will send a nullfunc frame with the powersave bit set
69 		 * even though the AP already knows that we are sleeping.
70 		 * This could be avoided by sending a null frame with power
71 		 * save bit disabled before enabling the power save, but
72 		 * this doesn't gain anything.
73 		 *
74 		 * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
75 		 * to send a nullfunc frame because AP already knows that
76 		 * we are sleeping, let's just enable power save mode in
77 		 * hardware.
78 		 */
79 		local->hw.conf.flags |= IEEE80211_CONF_PS;
80 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
81 	} else if (local->hw.conf.dynamic_ps_timeout > 0) {
82 		/*
83 		 * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
84 		 * had been running before leaving the operating channel,
85 		 * restart the timer now and send a nullfunc frame to inform
86 		 * the AP that we are awake.
87 		 */
88 		ieee80211_send_nullfunc(local, sdata, 0);
89 		mod_timer(&local->dynamic_ps_timer, jiffies +
90 			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
91 	}
92 
93 	ieee80211_sta_reset_beacon_monitor(sdata);
94 	ieee80211_sta_reset_conn_monitor(sdata);
95 }
96 
97 void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
98 {
99 	struct ieee80211_sub_if_data *sdata;
100 
101 	mutex_lock(&local->iflist_mtx);
102 	list_for_each_entry(sdata, &local->interfaces, list) {
103 		if (!ieee80211_sdata_running(sdata))
104 			continue;
105 
106 		/* disable beaconing */
107 		if (sdata->vif.type == NL80211_IFTYPE_AP ||
108 		    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
109 		    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
110 			ieee80211_bss_info_change_notify(
111 				sdata, BSS_CHANGED_BEACON_ENABLED);
112 
113 		/*
114 		 * only handle non-STA interfaces here, STA interfaces
115 		 * are handled in ieee80211_offchannel_stop_station(),
116 		 * e.g., from the background scan state machine.
117 		 *
118 		 * In addition, do not stop monitor interface to allow it to be
119 		 * used from user space controlled off-channel operations.
120 		 */
121 		if (sdata->vif.type != NL80211_IFTYPE_STATION &&
122 		    sdata->vif.type != NL80211_IFTYPE_MONITOR) {
123 			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
124 			netif_tx_stop_all_queues(sdata->dev);
125 		}
126 	}
127 	mutex_unlock(&local->iflist_mtx);
128 }
129 
130 void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
131 {
132 	struct ieee80211_sub_if_data *sdata;
133 
134 	/*
135 	 * notify the AP about us leaving the channel and stop all STA interfaces
136 	 */
137 	mutex_lock(&local->iflist_mtx);
138 	list_for_each_entry(sdata, &local->interfaces, list) {
139 		if (!ieee80211_sdata_running(sdata))
140 			continue;
141 
142 		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
143 			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
144 			netif_tx_stop_all_queues(sdata->dev);
145 			if (sdata->u.mgd.associated)
146 				ieee80211_offchannel_ps_enable(sdata);
147 		}
148 	}
149 	mutex_unlock(&local->iflist_mtx);
150 }
151 
152 void ieee80211_offchannel_return(struct ieee80211_local *local,
153 				 bool enable_beaconing)
154 {
155 	struct ieee80211_sub_if_data *sdata;
156 
157 	mutex_lock(&local->iflist_mtx);
158 	list_for_each_entry(sdata, &local->interfaces, list) {
159 		if (!ieee80211_sdata_running(sdata))
160 			continue;
161 
162 		/* Tell AP we're back */
163 		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
164 			if (sdata->u.mgd.associated)
165 				ieee80211_offchannel_ps_disable(sdata);
166 		}
167 
168 		if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
169 			clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
170 			/*
171 			 * This may wake up queues even though the driver
172 			 * currently has them stopped. This is not very
173 			 * likely, since the driver won't have gotten any
174 			 * (or hardly any) new packets while we weren't
175 			 * on the right channel, and even if it happens
176 			 * it will at most lead to queueing up one more
177 			 * packet per queue in mac80211 rather than on
178 			 * the interface qdisc.
179 			 */
180 			netif_tx_wake_all_queues(sdata->dev);
181 		}
182 
183 		/* re-enable beaconing */
184 		if (enable_beaconing &&
185 		    (sdata->vif.type == NL80211_IFTYPE_AP ||
186 		     sdata->vif.type == NL80211_IFTYPE_ADHOC ||
187 		     sdata->vif.type == NL80211_IFTYPE_MESH_POINT))
188 			ieee80211_bss_info_change_notify(
189 				sdata, BSS_CHANGED_BEACON_ENABLED);
190 	}
191 	mutex_unlock(&local->iflist_mtx);
192 }
193