f81fd30581d9e69cc91cc8be9a15d25e0094c2ac
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / pm.c
1 #include <net/mac80211.h>
2 #include <net/rtnetlink.h>
3
4 #include "ieee80211_i.h"
5 #include "mesh.h"
6 #include "driver-ops.h"
7 #include "led.h"
8
9 /* return value indicates whether the driver should be further notified */
10 static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
11 {
12         switch (sdata->vif.type) {
13         case NL80211_IFTYPE_STATION:
14                 ieee80211_sta_quiesce(sdata);
15                 break;
16         case NL80211_IFTYPE_ADHOC:
17                 ieee80211_ibss_quiesce(sdata);
18                 break;
19         case NL80211_IFTYPE_MESH_POINT:
20                 ieee80211_mesh_quiesce(sdata);
21                 break;
22         default:
23                 break;
24         }
25
26         cancel_work_sync(&sdata->work);
27 }
28
29 int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
30 {
31         struct ieee80211_local *local = hw_to_local(hw);
32         struct ieee80211_sub_if_data *sdata;
33         struct sta_info *sta;
34         struct ieee80211_chanctx *ctx;
35
36         if (!local->open_count)
37                 goto suspend;
38
39         ieee80211_scan_cancel(local);
40
41         if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
42                 mutex_lock(&local->sta_mtx);
43                 list_for_each_entry(sta, &local->sta_list, list) {
44                         set_sta_flag(sta, WLAN_STA_BLOCK_BA);
45                         ieee80211_sta_tear_down_BA_sessions(sta, true);
46                 }
47                 mutex_unlock(&local->sta_mtx);
48         }
49
50         ieee80211_stop_queues_by_reason(hw,
51                         IEEE80211_QUEUE_STOP_REASON_SUSPEND);
52
53         /* flush out all packets */
54         synchronize_net();
55
56         drv_flush(local, false);
57
58         local->quiescing = true;
59         /* make quiescing visible to timers everywhere */
60         mb();
61
62         flush_workqueue(local->workqueue);
63
64         /* Don't try to run timers while suspended. */
65         del_timer_sync(&local->sta_cleanup);
66
67          /*
68          * Note that this particular timer doesn't need to be
69          * restarted at resume.
70          */
71         cancel_work_sync(&local->dynamic_ps_enable_work);
72         del_timer_sync(&local->dynamic_ps_timer);
73
74         local->wowlan = wowlan && local->open_count;
75         if (local->wowlan) {
76                 int err = drv_suspend(local, wowlan);
77                 if (err < 0) {
78                         local->quiescing = false;
79                         local->wowlan = false;
80                         if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
81                                 mutex_lock(&local->sta_mtx);
82                                 list_for_each_entry(sta,
83                                                     &local->sta_list, list) {
84                                         clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
85                                 }
86                                 mutex_unlock(&local->sta_mtx);
87                         }
88                         ieee80211_wake_queues_by_reason(hw,
89                                         IEEE80211_QUEUE_STOP_REASON_SUSPEND);
90                         return err;
91                 } else if (err > 0) {
92                         WARN_ON(err != 1);
93                         local->wowlan = false;
94                 } else {
95                         list_for_each_entry(sdata, &local->interfaces, list)
96                                 if (ieee80211_sdata_running(sdata))
97                                         ieee80211_quiesce(sdata);
98                         goto suspend;
99                 }
100         }
101
102         /* disable keys */
103         list_for_each_entry(sdata, &local->interfaces, list)
104                 ieee80211_disable_keys(sdata);
105
106         /* tear down aggregation sessions and remove STAs */
107         mutex_lock(&local->sta_mtx);
108         list_for_each_entry(sta, &local->sta_list, list) {
109                 if (sta->uploaded) {
110                         enum ieee80211_sta_state state;
111
112                         state = sta->sta_state;
113                         for (; state > IEEE80211_STA_NOTEXIST; state--)
114                                 WARN_ON(drv_sta_state(local, sta->sdata, sta,
115                                                       state, state - 1));
116                 }
117
118                 mesh_plink_quiesce(sta);
119         }
120         mutex_unlock(&local->sta_mtx);
121
122         /* remove all interfaces */
123         list_for_each_entry(sdata, &local->interfaces, list) {
124                 static u8 zero_addr[ETH_ALEN] = {};
125                 u32 changed = 0;
126
127                 if (!ieee80211_sdata_running(sdata))
128                         continue;
129
130                 switch (sdata->vif.type) {
131                 case NL80211_IFTYPE_AP_VLAN:
132                 case NL80211_IFTYPE_MONITOR:
133                         /* skip these */
134                         continue;
135                 case NL80211_IFTYPE_STATION:
136                         if (sdata->vif.bss_conf.assoc)
137                                 changed = BSS_CHANGED_ASSOC |
138                                           BSS_CHANGED_BSSID |
139                                           BSS_CHANGED_IDLE;
140                         break;
141                 case NL80211_IFTYPE_AP:
142                 case NL80211_IFTYPE_ADHOC:
143                 case NL80211_IFTYPE_MESH_POINT:
144                         if (sdata->vif.bss_conf.enable_beacon)
145                                 changed = BSS_CHANGED_BEACON_ENABLED;
146                         break;
147                 default:
148                         break;
149                 }
150
151                 ieee80211_quiesce(sdata);
152
153                 sdata->suspend_bss_conf = sdata->vif.bss_conf;
154                 memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf));
155                 sdata->vif.bss_conf.idle = true;
156                 if (sdata->suspend_bss_conf.bssid)
157                         sdata->vif.bss_conf.bssid = zero_addr;
158
159                 /* disable beaconing or remove association */
160                 ieee80211_bss_info_change_notify(sdata, changed);
161
162                 if (sdata->vif.type == NL80211_IFTYPE_AP &&
163                     rcu_access_pointer(sdata->u.ap.beacon))
164                         drv_stop_ap(local, sdata);
165
166                 if (local->use_chanctx) {
167                         struct ieee80211_chanctx_conf *conf;
168
169                         mutex_lock(&local->chanctx_mtx);
170                         conf = rcu_dereference_protected(
171                                         sdata->vif.chanctx_conf,
172                                         lockdep_is_held(&local->chanctx_mtx));
173                         if (conf) {
174                                 ctx = container_of(conf,
175                                                    struct ieee80211_chanctx,
176                                                    conf);
177                                 drv_unassign_vif_chanctx(local, sdata, ctx);
178                         }
179
180                         mutex_unlock(&local->chanctx_mtx);
181                 }
182                 drv_remove_interface(local, sdata);
183         }
184
185         sdata = rtnl_dereference(local->monitor_sdata);
186         if (sdata) {
187                 if (local->use_chanctx) {
188                         struct ieee80211_chanctx_conf *conf;
189
190                         mutex_lock(&local->chanctx_mtx);
191                         conf = rcu_dereference_protected(
192                                         sdata->vif.chanctx_conf,
193                                         lockdep_is_held(&local->chanctx_mtx));
194                         if (conf) {
195                                 ctx = container_of(conf,
196                                                    struct ieee80211_chanctx,
197                                                    conf);
198                                 drv_unassign_vif_chanctx(local, sdata, ctx);
199                         }
200
201                         mutex_unlock(&local->chanctx_mtx);
202                 }
203
204                 drv_remove_interface(local, sdata);
205         }
206
207         mutex_lock(&local->chanctx_mtx);
208         list_for_each_entry(ctx, &local->chanctx_list, list)
209                 drv_remove_chanctx(local, ctx);
210         mutex_unlock(&local->chanctx_mtx);
211
212         /* stop hardware - this must stop RX */
213         if (local->open_count)
214                 ieee80211_stop_device(local);
215
216  suspend:
217         local->suspended = true;
218         /* need suspended to be visible before quiescing is false */
219         barrier();
220         local->quiescing = false;
221
222         return 0;
223 }
224
225 /*
226  * __ieee80211_resume() is a static inline which just calls
227  * ieee80211_reconfig(), which is also needed for hardware
228  * hang/firmware failure/etc. recovery.
229  */