xref: /linux/net/mac80211/pm.c (revision b77e0ce62d63a761ffb7f7245a215a49f5921c2f)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Portions
4  * Copyright (C) 2020-2021 Intel Corporation
5  */
6 #include <net/mac80211.h>
7 #include <net/rtnetlink.h>
8 
9 #include "ieee80211_i.h"
10 #include "mesh.h"
11 #include "driver-ops.h"
12 #include "led.h"
13 
14 static void ieee80211_sched_scan_cancel(struct ieee80211_local *local)
15 {
16 	if (ieee80211_request_sched_scan_stop(local))
17 		return;
18 	cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
19 }
20 
21 int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
22 {
23 	struct ieee80211_local *local = hw_to_local(hw);
24 	struct ieee80211_sub_if_data *sdata;
25 	struct sta_info *sta;
26 
27 	if (!local->open_count)
28 		goto suspend;
29 
30 	ieee80211_scan_cancel(local);
31 
32 	ieee80211_dfs_cac_cancel(local);
33 
34 	ieee80211_roc_purge(local, NULL);
35 
36 	ieee80211_del_virtual_monitor(local);
37 
38 	if (ieee80211_hw_check(hw, AMPDU_AGGREGATION) &&
39 	    !(wowlan && wowlan->any)) {
40 		mutex_lock(&local->sta_mtx);
41 		list_for_each_entry(sta, &local->sta_list, list) {
42 			set_sta_flag(sta, WLAN_STA_BLOCK_BA);
43 			ieee80211_sta_tear_down_BA_sessions(
44 					sta, AGG_STOP_LOCAL_REQUEST);
45 		}
46 		mutex_unlock(&local->sta_mtx);
47 	}
48 
49 	/* keep sched_scan only in case of 'any' trigger */
50 	if (!(wowlan && wowlan->any))
51 		ieee80211_sched_scan_cancel(local);
52 
53 	ieee80211_stop_queues_by_reason(hw,
54 					IEEE80211_MAX_QUEUE_MAP,
55 					IEEE80211_QUEUE_STOP_REASON_SUSPEND,
56 					false);
57 
58 	/* flush out all packets */
59 	synchronize_net();
60 
61 	ieee80211_flush_queues(local, NULL, true);
62 
63 	local->quiescing = true;
64 	/* make quiescing visible to timers everywhere */
65 	mb();
66 
67 	flush_workqueue(local->workqueue);
68 
69 	/* Don't try to run timers while suspended. */
70 	del_timer_sync(&local->sta_cleanup);
71 
72 	 /*
73 	 * Note that this particular timer doesn't need to be
74 	 * restarted at resume.
75 	 */
76 	cancel_work_sync(&local->dynamic_ps_enable_work);
77 	del_timer_sync(&local->dynamic_ps_timer);
78 
79 	local->wowlan = wowlan;
80 	if (local->wowlan) {
81 		int err;
82 
83 		/* Drivers don't expect to suspend while some operations like
84 		 * authenticating or associating are in progress. It doesn't
85 		 * make sense anyway to accept that, since the authentication
86 		 * or association would never finish since the driver can't do
87 		 * that on its own.
88 		 * Thus, clean up in-progress auth/assoc first.
89 		 */
90 		list_for_each_entry(sdata, &local->interfaces, list) {
91 			if (!ieee80211_sdata_running(sdata))
92 				continue;
93 			if (sdata->vif.type != NL80211_IFTYPE_STATION)
94 				continue;
95 			ieee80211_mgd_quiesce(sdata);
96 			/* If suspended during TX in progress, and wowlan
97 			 * is enabled (connection will be active) there
98 			 * can be a race where the driver is put out
99 			 * of power-save due to TX and during suspend
100 			 * dynamic_ps_timer is cancelled and TX packet
101 			 * is flushed, leaving the driver in ACTIVE even
102 			 * after resuming until dynamic_ps_timer puts
103 			 * driver back in DOZE.
104 			 */
105 			if (sdata->u.mgd.associated &&
106 			    sdata->u.mgd.powersave &&
107 			     !(local->hw.conf.flags & IEEE80211_CONF_PS)) {
108 				local->hw.conf.flags |= IEEE80211_CONF_PS;
109 				ieee80211_hw_config(local,
110 						    IEEE80211_CONF_CHANGE_PS);
111 			}
112 		}
113 
114 		err = drv_suspend(local, wowlan);
115 		if (err < 0) {
116 			local->quiescing = false;
117 			local->wowlan = false;
118 			if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) {
119 				mutex_lock(&local->sta_mtx);
120 				list_for_each_entry(sta,
121 						    &local->sta_list, list) {
122 					clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
123 				}
124 				mutex_unlock(&local->sta_mtx);
125 			}
126 			ieee80211_wake_queues_by_reason(hw,
127 					IEEE80211_MAX_QUEUE_MAP,
128 					IEEE80211_QUEUE_STOP_REASON_SUSPEND,
129 					false);
130 			return err;
131 		} else if (err > 0) {
132 			WARN_ON(err != 1);
133 			/* cfg80211 will call back into mac80211 to disconnect
134 			 * all interfaces, allow that to proceed properly
135 			 */
136 			ieee80211_wake_queues_by_reason(hw,
137 					IEEE80211_MAX_QUEUE_MAP,
138 					IEEE80211_QUEUE_STOP_REASON_SUSPEND,
139 					false);
140 			return err;
141 		} else {
142 			goto suspend;
143 		}
144 	}
145 
146 	/* remove all interfaces that were created in the driver */
147 	list_for_each_entry(sdata, &local->interfaces, list) {
148 		if (!ieee80211_sdata_running(sdata))
149 			continue;
150 		switch (sdata->vif.type) {
151 		case NL80211_IFTYPE_AP_VLAN:
152 		case NL80211_IFTYPE_MONITOR:
153 			continue;
154 		case NL80211_IFTYPE_STATION:
155 			ieee80211_mgd_quiesce(sdata);
156 			break;
157 		default:
158 			break;
159 		}
160 
161 		flush_delayed_work(&sdata->dec_tailroom_needed_wk);
162 		drv_remove_interface(local, sdata);
163 	}
164 
165 	/*
166 	 * We disconnected on all interfaces before suspend, all channel
167 	 * contexts should be released.
168 	 */
169 	WARN_ON(!list_empty(&local->chanctx_list));
170 
171 	/* stop hardware - this must stop RX */
172 	ieee80211_stop_device(local);
173 
174  suspend:
175 	local->suspended = true;
176 	/* need suspended to be visible before quiescing is false */
177 	barrier();
178 	local->quiescing = false;
179 
180 	return 0;
181 }
182 
183 /*
184  * __ieee80211_resume() is a static inline which just calls
185  * ieee80211_reconfig(), which is also needed for hardware
186  * hang/firmware failure/etc. recovery.
187  */
188 
189 void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
190 				    struct cfg80211_wowlan_wakeup *wakeup,
191 				    gfp_t gfp)
192 {
193 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
194 
195 	cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp);
196 }
197 EXPORT_SYMBOL(ieee80211_report_wowlan_wakeup);
198