• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/net/mac80211/
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 */
22static 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
30	del_timer_sync(&local->dynamic_ps_timer);
31	del_timer_sync(&ifmgd->bcn_mon_timer);
32	del_timer_sync(&ifmgd->conn_mon_timer);
33
34	cancel_work_sync(&local->dynamic_ps_enable_work);
35
36	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
37		local->offchannel_ps_enabled = true;
38		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
39		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
40	}
41
42	if (!(local->offchannel_ps_enabled) ||
43	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
44		/*
45		 * If power save was enabled, no need to send a nullfunc
46		 * frame because AP knows that we are sleeping. But if the
47		 * hardware is creating the nullfunc frame for power save
48		 * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
49		 * enabled) and power save was enabled, the firmware just
50		 * sent a null frame with power save disabled. So we need
51		 * to send a new nullfunc frame to inform the AP that we
52		 * are again sleeping.
53		 */
54		ieee80211_send_nullfunc(local, sdata, 1);
55}
56
57/* inform AP that we are awake again, unless power save is enabled */
58static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
59{
60	struct ieee80211_local *local = sdata->local;
61
62	if (!local->ps_sdata)
63		ieee80211_send_nullfunc(local, sdata, 0);
64	else if (local->offchannel_ps_enabled) {
65		/*
66		 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
67		 * will send a nullfunc frame with the powersave bit set
68		 * even though the AP already knows that we are sleeping.
69		 * This could be avoided by sending a null frame with power
70		 * save bit disabled before enabling the power save, but
71		 * this doesn't gain anything.
72		 *
73		 * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
74		 * to send a nullfunc frame because AP already knows that
75		 * we are sleeping, let's just enable power save mode in
76		 * hardware.
77		 */
78		local->hw.conf.flags |= IEEE80211_CONF_PS;
79		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
80	} else if (local->hw.conf.dynamic_ps_timeout > 0) {
81		/*
82		 * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
83		 * had been running before leaving the operating channel,
84		 * restart the timer now and send a nullfunc frame to inform
85		 * the AP that we are awake.
86		 */
87		ieee80211_send_nullfunc(local, sdata, 0);
88		mod_timer(&local->dynamic_ps_timer, jiffies +
89			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
90	}
91
92	ieee80211_sta_reset_beacon_monitor(sdata);
93	ieee80211_sta_reset_conn_monitor(sdata);
94}
95
96void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
97{
98	struct ieee80211_sub_if_data *sdata;
99
100	mutex_lock(&local->iflist_mtx);
101	list_for_each_entry(sdata, &local->interfaces, list) {
102		if (!ieee80211_sdata_running(sdata))
103			continue;
104
105		/* disable beaconing */
106		if (sdata->vif.type == NL80211_IFTYPE_AP ||
107		    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
108		    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
109			ieee80211_bss_info_change_notify(
110				sdata, BSS_CHANGED_BEACON_ENABLED);
111
112		/*
113		 * only handle non-STA interfaces here, STA interfaces
114		 * are handled in ieee80211_offchannel_stop_station(),
115		 * e.g., from the background scan state machine.
116		 *
117		 * In addition, do not stop monitor interface to allow it to be
118		 * used from user space controlled off-channel operations.
119		 */
120		if (sdata->vif.type != NL80211_IFTYPE_STATION &&
121		    sdata->vif.type != NL80211_IFTYPE_MONITOR)
122			netif_tx_stop_all_queues(sdata->dev);
123	}
124	mutex_unlock(&local->iflist_mtx);
125}
126
127void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
128{
129	struct ieee80211_sub_if_data *sdata;
130
131	/*
132	 * notify the AP about us leaving the channel and stop all STA interfaces
133	 */
134	mutex_lock(&local->iflist_mtx);
135	list_for_each_entry(sdata, &local->interfaces, list) {
136		if (!ieee80211_sdata_running(sdata))
137			continue;
138
139		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
140			netif_tx_stop_all_queues(sdata->dev);
141			if (sdata->u.mgd.associated)
142				ieee80211_offchannel_ps_enable(sdata);
143		}
144	}
145	mutex_unlock(&local->iflist_mtx);
146}
147
148void ieee80211_offchannel_return(struct ieee80211_local *local,
149				 bool enable_beaconing)
150{
151	struct ieee80211_sub_if_data *sdata;
152
153	mutex_lock(&local->iflist_mtx);
154	list_for_each_entry(sdata, &local->interfaces, list) {
155		if (!ieee80211_sdata_running(sdata))
156			continue;
157
158		/* Tell AP we're back */
159		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
160			if (sdata->u.mgd.associated)
161				ieee80211_offchannel_ps_disable(sdata);
162		}
163
164		if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
165			netif_tx_wake_all_queues(sdata->dev);
166
167		/* re-enable beaconing */
168		if (enable_beaconing &&
169		    (sdata->vif.type == NL80211_IFTYPE_AP ||
170		     sdata->vif.type == NL80211_IFTYPE_ADHOC ||
171		     sdata->vif.type == NL80211_IFTYPE_MESH_POINT))
172			ieee80211_bss_info_change_notify(
173				sdata, BSS_CHANGED_BEACON_ENABLED);
174	}
175	mutex_unlock(&local->iflist_mtx);
176}
177