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