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 ieee80211_dfs_cac_cancel(local); 42 43 if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { 44 mutex_lock(&local->sta_mtx); 45 list_for_each_entry(sta, &local->sta_list, list) { 46 set_sta_flag(sta, WLAN_STA_BLOCK_BA); 47 ieee80211_sta_tear_down_BA_sessions( 48 sta, AGG_STOP_LOCAL_REQUEST); 49 } 50 mutex_unlock(&local->sta_mtx); 51 } 52 53 ieee80211_stop_queues_by_reason(hw, 54 IEEE80211_QUEUE_STOP_REASON_SUSPEND); 55 56 /* flush out all packets */ 57 synchronize_net(); 58 59 drv_flush(local, false); 60 61 local->quiescing = true; 62 /* make quiescing visible to timers everywhere */ 63 mb(); 64 65 flush_workqueue(local->workqueue); 66 67 /* Don't try to run timers while suspended. */ 68 del_timer_sync(&local->sta_cleanup); 69 70 /* 71 * Note that this particular timer doesn't need to be 72 * restarted at resume. 73 */ 74 cancel_work_sync(&local->dynamic_ps_enable_work); 75 del_timer_sync(&local->dynamic_ps_timer); 76 77 local->wowlan = wowlan && local->open_count; 78 if (local->wowlan) { 79 int err = drv_suspend(local, wowlan); 80 if (err < 0) { 81 local->quiescing = false; 82 local->wowlan = false; 83 if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { 84 mutex_lock(&local->sta_mtx); 85 list_for_each_entry(sta, 86 &local->sta_list, list) { 87 clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 88 } 89 mutex_unlock(&local->sta_mtx); 90 } 91 ieee80211_wake_queues_by_reason(hw, 92 IEEE80211_QUEUE_STOP_REASON_SUSPEND); 93 return err; 94 } else if (err > 0) { 95 WARN_ON(err != 1); 96 local->wowlan = false; 97 } else { 98 list_for_each_entry(sdata, &local->interfaces, list) 99 if (ieee80211_sdata_running(sdata)) 100 ieee80211_quiesce(sdata); 101 goto suspend; 102 } 103 } 104 105 /* disable keys */ 106 list_for_each_entry(sdata, &local->interfaces, list) 107 ieee80211_disable_keys(sdata); 108 109 /* tear down aggregation sessions and remove STAs */ 110 mutex_lock(&local->sta_mtx); 111 list_for_each_entry(sta, &local->sta_list, list) { 112 if (sta->uploaded) { 113 enum ieee80211_sta_state state; 114 115 state = sta->sta_state; 116 for (; state > IEEE80211_STA_NOTEXIST; state--) 117 WARN_ON(drv_sta_state(local, sta->sdata, sta, 118 state, state - 1)); 119 } 120 121 mesh_plink_quiesce(sta); 122 } 123 mutex_unlock(&local->sta_mtx); 124 125 /* remove all interfaces */ 126 list_for_each_entry(sdata, &local->interfaces, list) { 127 static u8 zero_addr[ETH_ALEN] = {}; 128 u32 changed = 0; 129 130 if (!ieee80211_sdata_running(sdata)) 131 continue; 132 133 switch (sdata->vif.type) { 134 case NL80211_IFTYPE_AP_VLAN: 135 case NL80211_IFTYPE_MONITOR: 136 /* skip these */ 137 continue; 138 case NL80211_IFTYPE_STATION: 139 if (sdata->vif.bss_conf.assoc) 140 changed = BSS_CHANGED_ASSOC | 141 BSS_CHANGED_BSSID | 142 BSS_CHANGED_IDLE; 143 break; 144 case NL80211_IFTYPE_AP: 145 case NL80211_IFTYPE_ADHOC: 146 case NL80211_IFTYPE_MESH_POINT: 147 if (sdata->vif.bss_conf.enable_beacon) 148 changed = BSS_CHANGED_BEACON_ENABLED; 149 break; 150 default: 151 break; 152 } 153 154 ieee80211_quiesce(sdata); 155 156 sdata->suspend_bss_conf = sdata->vif.bss_conf; 157 memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); 158 sdata->vif.bss_conf.idle = true; 159 if (sdata->suspend_bss_conf.bssid) 160 sdata->vif.bss_conf.bssid = zero_addr; 161 162 /* disable beaconing or remove association */ 163 ieee80211_bss_info_change_notify(sdata, changed); 164 165 if (sdata->vif.type == NL80211_IFTYPE_AP && 166 rcu_access_pointer(sdata->u.ap.beacon)) 167 drv_stop_ap(local, sdata); 168 169 if (local->use_chanctx) { 170 struct ieee80211_chanctx_conf *conf; 171 172 mutex_lock(&local->chanctx_mtx); 173 conf = rcu_dereference_protected( 174 sdata->vif.chanctx_conf, 175 lockdep_is_held(&local->chanctx_mtx)); 176 if (conf) { 177 ctx = container_of(conf, 178 struct ieee80211_chanctx, 179 conf); 180 drv_unassign_vif_chanctx(local, sdata, ctx); 181 } 182 183 mutex_unlock(&local->chanctx_mtx); 184 } 185 drv_remove_interface(local, sdata); 186 } 187 188 sdata = rtnl_dereference(local->monitor_sdata); 189 if (sdata) { 190 if (local->use_chanctx) { 191 struct ieee80211_chanctx_conf *conf; 192 193 mutex_lock(&local->chanctx_mtx); 194 conf = rcu_dereference_protected( 195 sdata->vif.chanctx_conf, 196 lockdep_is_held(&local->chanctx_mtx)); 197 if (conf) { 198 ctx = container_of(conf, 199 struct ieee80211_chanctx, 200 conf); 201 drv_unassign_vif_chanctx(local, sdata, ctx); 202 } 203 204 mutex_unlock(&local->chanctx_mtx); 205 } 206 207 drv_remove_interface(local, sdata); 208 } 209 210 mutex_lock(&local->chanctx_mtx); 211 list_for_each_entry(ctx, &local->chanctx_list, list) 212 drv_remove_chanctx(local, ctx); 213 mutex_unlock(&local->chanctx_mtx); 214 215 /* stop hardware - this must stop RX */ 216 if (local->open_count) 217 ieee80211_stop_device(local); 218 219 suspend: 220 local->suspended = true; 221 /* need suspended to be visible before quiescing is false */ 222 barrier(); 223 local->quiescing = false; 224 225 return 0; 226 } 227 228 /* 229 * __ieee80211_resume() is a static inline which just calls 230 * ieee80211_reconfig(), which is also needed for hardware 231 * hang/firmware failure/etc. recovery. 232 */ 233 234 void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, 235 struct cfg80211_wowlan_wakeup *wakeup, 236 gfp_t gfp) 237 { 238 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 239 240 cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp); 241 } 242 EXPORT_SYMBOL(ieee80211_report_wowlan_wakeup); 243