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