118334Speter// SPDX-License-Identifier: GPL-2.0 2132718Skan/* 318334Speter * Portions 418334Speter * Copyright (C) 2020-2021, 2023 Intel Corporation 518334Speter */ 618334Speter#include <net/mac80211.h> 718334Speter#include <net/rtnetlink.h> 818334Speter 918334Speter#include "ieee80211_i.h" 1018334Speter#include "mesh.h" 1118334Speter#include "driver-ops.h" 1218334Speter#include "led.h" 1318334Speter 1418334Speterstatic void ieee80211_sched_scan_cancel(struct ieee80211_local *local) 1518334Speter{ 16169689Skan if (ieee80211_request_sched_scan_stop(local)) 1718334Speter return; 18132718Skan cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0); 1950397Sobrien} 20132718Skan 21132718Skanint __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) 2218334Speter{ 2318334Speter struct ieee80211_local *local = hw_to_local(hw); 2418334Speter struct ieee80211_sub_if_data *sdata; 2518334Speter struct sta_info *sta; 2618334Speter 2718334Speter if (!local->open_count) 2818334Speter goto suspend; 29132718Skan 3018334Speter local->suspending = true; 3118334Speter mb(); /* make suspending visible before any cancellation */ 3218334Speter 3318334Speter ieee80211_scan_cancel(local); 3418334Speter 3518334Speter ieee80211_dfs_cac_cancel(local); 3618334Speter 37117395Skan ieee80211_roc_purge(local, NULL); 3890075Sobrien 3918334Speter ieee80211_del_virtual_monitor(local); 4018334Speter 4118334Speter if (ieee80211_hw_check(hw, AMPDU_AGGREGATION) && 4218334Speter !(wowlan && wowlan->any)) { 4318334Speter lockdep_assert_wiphy(local->hw.wiphy); 44132718Skan list_for_each_entry(sta, &local->sta_list, list) { 4518334Speter set_sta_flag(sta, WLAN_STA_BLOCK_BA); 4690075Sobrien ieee80211_sta_tear_down_BA_sessions( 4790075Sobrien sta, AGG_STOP_LOCAL_REQUEST); 4890075Sobrien } 4990075Sobrien } 5018334Speter 5118334Speter /* keep sched_scan only in case of 'any' trigger */ 5218334Speter if (!(wowlan && wowlan->any)) 5318334Speter ieee80211_sched_scan_cancel(local); 54117395Skan 5518334Speter ieee80211_stop_queues_by_reason(hw, 5618334Speter IEEE80211_MAX_QUEUE_MAP, 5718334Speter IEEE80211_QUEUE_STOP_REASON_SUSPEND, 58132718Skan false); 5918334Speter 6018334Speter /* flush out all packets */ 6190075Sobrien synchronize_net(); 6218334Speter 6318334Speter ieee80211_flush_queues(local, NULL, true); 6418334Speter 6590075Sobrien local->quiescing = true; 6618334Speter /* make quiescing visible to timers everywhere */ 6790075Sobrien mb(); 6818334Speter 6918334Speter flush_workqueue(local->workqueue); 7018334Speter 7190075Sobrien /* Don't try to run timers while suspended. */ 7218334Speter del_timer_sync(&local->sta_cleanup); 7318334Speter 7418334Speter /* 7518334Speter * Note that this particular timer doesn't need to be 7618334Speter * restarted at resume. 77132718Skan */ 7818334Speter wiphy_work_cancel(local->hw.wiphy, &local->dynamic_ps_enable_work); 7918334Speter del_timer_sync(&local->dynamic_ps_timer); 8090075Sobrien 8118334Speter local->wowlan = wowlan; 8218334Speter if (local->wowlan) { 8318334Speter int err; 8418334Speter 8518334Speter /* Drivers don't expect to suspend while some operations like 8618334Speter * authenticating or associating are in progress. It doesn't 8718334Speter * make sense anyway to accept that, since the authentication 8818334Speter * or association would never finish since the driver can't do 8918334Speter * that on its own. 9018334Speter * Thus, clean up in-progress auth/assoc first. 9118334Speter */ 9218334Speter list_for_each_entry(sdata, &local->interfaces, list) { 9318334Speter if (!ieee80211_sdata_running(sdata)) 9418334Speter continue; 9518334Speter if (sdata->vif.type != NL80211_IFTYPE_STATION) 9618334Speter continue; 9718334Speter ieee80211_mgd_quiesce(sdata); 9818334Speter /* If suspended during TX in progress, and wowlan 9990075Sobrien * is enabled (connection will be active) there 10018334Speter * can be a race where the driver is put out 10190075Sobrien * of power-save due to TX and during suspend 10218334Speter * dynamic_ps_timer is cancelled and TX packet 10318334Speter * is flushed, leaving the driver in ACTIVE even 10418334Speter * after resuming until dynamic_ps_timer puts 10518334Speter * driver back in DOZE. 10650397Sobrien */ 10718334Speter if (sdata->u.mgd.associated && 10818334Speter sdata->u.mgd.powersave && 109132718Skan !(local->hw.conf.flags & IEEE80211_CONF_PS)) { 11018334Speter local->hw.conf.flags |= IEEE80211_CONF_PS; 11118334Speter ieee80211_hw_config(local, 11218334Speter IEEE80211_CONF_CHANGE_PS); 11318334Speter } 11418334Speter } 11518334Speter 11618334Speter err = drv_suspend(local, wowlan); 11718334Speter if (err < 0) { 11818334Speter local->quiescing = false; 11918334Speter local->wowlan = false; 12018334Speter if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { 12118334Speter lockdep_assert_wiphy(local->hw.wiphy); 12218334Speter list_for_each_entry(sta, 12318334Speter &local->sta_list, list) { 12418334Speter clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 12518334Speter } 12618334Speter } 12718334Speter ieee80211_wake_queues_by_reason(hw, 12818334Speter IEEE80211_MAX_QUEUE_MAP, 12918334Speter IEEE80211_QUEUE_STOP_REASON_SUSPEND, 13018334Speter false); 13118334Speter return err; 13218334Speter } else if (err > 0) { 13318334Speter WARN_ON(err != 1); 13418334Speter /* cfg80211 will call back into mac80211 to disconnect 13518334Speter * all interfaces, allow that to proceed properly 13618334Speter */ 13718334Speter ieee80211_wake_queues_by_reason(hw, 13818334Speter IEEE80211_MAX_QUEUE_MAP, 13918334Speter IEEE80211_QUEUE_STOP_REASON_SUSPEND, 14018334Speter false); 14118334Speter return err; 14218334Speter } else { 14318334Speter goto suspend; 14418334Speter } 145132718Skan } 14618334Speter 14718334Speter /* remove all interfaces that were created in the driver */ 14890075Sobrien list_for_each_entry(sdata, &local->interfaces, list) { 14918334Speter if (!ieee80211_sdata_running(sdata)) 15018334Speter continue; 15118334Speter switch (sdata->vif.type) { 15218334Speter case NL80211_IFTYPE_AP_VLAN: 15318334Speter case NL80211_IFTYPE_MONITOR: 15490075Sobrien continue; 15518334Speter case NL80211_IFTYPE_STATION: 15690075Sobrien ieee80211_mgd_quiesce(sdata); 15718334Speter break; 15818334Speter default: 15918334Speter break; 16018334Speter } 16118334Speter 162132718Skan wiphy_delayed_work_flush(local->hw.wiphy, 16318334Speter &sdata->dec_tailroom_needed_wk); 16418334Speter drv_remove_interface(local, sdata); 16590075Sobrien } 16618334Speter 16718334Speter /* 16818334Speter * We disconnected on all interfaces before suspend, all channel 16918334Speter * contexts should be released. 17018334Speter */ 17118334Speter WARN_ON(!list_empty(&local->chanctx_list)); 17218334Speter 17318334Speter /* stop hardware - this must stop RX */ 17418334Speter ieee80211_stop_device(local); 17518334Speter 17618334Speter suspend: 17718334Speter local->suspended = true; 17818334Speter /* need suspended to be visible before quiescing is false */ 17918334Speter barrier(); 18018334Speter local->quiescing = false; 18118334Speter local->suspending = false; 18218334Speter 18318334Speter return 0; 18418334Speter} 18518334Speter 18618334Speter/* 18718334Speter * __ieee80211_resume() is a static inline which just calls 18818334Speter * ieee80211_reconfig(), which is also needed for hardware 18918334Speter * hang/firmware failure/etc. recovery. 19018334Speter */ 19118334Speter 19218334Spetervoid ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, 19318334Speter struct cfg80211_wowlan_wakeup *wakeup, 19418334Speter gfp_t gfp) 19518334Speter{ 19618334Speter struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 19718334Speter 19818334Speter cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp); 19950397Sobrien} 20018334SpeterEXPORT_SYMBOL(ieee80211_report_wowlan_wakeup); 20118334Speter