1: d8321b34479c ! 1: 0243ea038a1a wifi: rtw88: download the beacon the reserved page was built with @@ Commit message keeps fetching its own.  Signed-off-by: Mehmet Fide  + Reviewed-by: Luka Gejak  Acked-by: Ping-Ke Shih   ## drivers/net/wireless/realtek/rtw88/fw.c ## 2: 5e1d57015f2a ! 2: 209585bd5f65 wifi: rtw88: support channel switch in AP mode @@ Commit message and ieee80211_csa_finish() is called once it completes. IBSS, which the flag enables too, shares the page and the work.  - The work is a wiphy delayed work of the interface; the wiphy lock - serializes it with the mac80211 state it reads and the driver mutex - with the page build. It is cancelled when the AP stops, when the - interface goes away and on WoWLAN suspend, and a hardware restart, - which replays add_interface with the work still armed, does not - initialise it again. The other page downloads - that can run during a countdown, the TIM update and the PG backup on - set_key, are skipped then, since every beacon fetch advances the - countdown. A hardware scan is refused while a switch is announced: it + The work is a wiphy delayed work of the device and csa_vif names the + interface it counts down for; the wiphy lock serializes the work with + the mac80211 state it reads and the driver mutex with the page build. + The countdown is cancelled when the AP stops, when the interface goes + away, on WoWLAN suspend and before a firmware recovery restarts the + hardware. The other page downloads that can run + during a countdown, the TIM update, the PG backup on set_key and the + one on association, are skipped then, since every beacon fetch + advances the countdown; the next countdown download, at most one + beacon interval later, carries their changes. A scan is refused while + a switch is announced, before the fallback to a software scan: it would take the AP off the channel its stations count down to.  Signed-off-by: Mehmet Fide  @@ drivers/net/wireless/realtek/rtw88/fw.c: int rtw_fw_download_rsvd_page(struct rt return ret; }  -+static struct ieee80211_vif *rtw_fw_beacon_vif(struct rtw_dev *rtwdev) ++bool rtw_fw_csa_active(struct rtw_dev *rtwdev)  +{ -+ struct rtw_rsvd_page *rsvd_pkt; ++ return rtwdev->csa_vif && rtwdev->csa_vif->bss_conf.csa_active; ++}  + -+ rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list, -+ struct rtw_rsvd_page, build_list); -+ if (!rsvd_pkt || rsvd_pkt->type != RSVD_BEACON) -+ return NULL; ++void rtw_fw_csa_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) ++{ ++ u32 interval = ieee80211_tu_to_usec(vif->bss_conf.beacon_int);  + -+ return rtwvif_to_vif(rsvd_pkt->rtwvif); ++ rtwdev->csa_vif = vif; ++ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->csa_beacon_work, ++ usecs_to_jiffies(interval));  +}  + -+bool rtw_fw_csa_active(struct rtw_dev *rtwdev) ++void rtw_fw_csa_stop(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)  +{ -+ struct ieee80211_vif *vif = rtw_fw_beacon_vif(rtwdev); ++ if (!vif || rtwdev->csa_vif != vif) ++ return;  + -+ return vif && vif->bss_conf.csa_active; ++ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->csa_beacon_work); ++ rtwdev->csa_vif = NULL;  +}  + void rtw_fw_update_beacon_work(struct work_struct *work) @@ drivers/net/wireless/realtek/rtw88/fw.c: int rtw_fw_download_rsvd_page(struct rt  +  +void rtw_fw_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)  +{ -+ struct rtw_vif *rtwvif = container_of(work, struct rtw_vif, ++ struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,  + csa_beacon_work.work); -+ struct rtw_dev *rtwdev = wiphy_to_ieee80211_hw(wiphy)->priv; -+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); ++ struct ieee80211_vif *vif = rtwdev->csa_vif;  + unsigned int delay;  +  + lockdep_assert_wiphy(wiphy);  +  + mutex_lock(&rtwdev->mutex);  + -+ if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) -+ goto out; -+ -+ if (!vif->bss_conf.csa_active) ++ if (!vif->bss_conf.csa_active) { ++ rtwdev->csa_vif = NULL;  + goto out; ++ }  +  + delay = ieee80211_tu_to_usec(vif->bss_conf.beacon_int);  + @@ drivers/net/wireless/realtek/rtw88/fw.c: int rtw_fw_download_rsvd_page(struct rt  + rtw_fw_download_rsvd_page(rtwdev);  + rtw_send_rsvd_page_h2c(rtwdev);  + -+ wiphy_delayed_work_queue(wiphy, &rtwvif->csa_beacon_work, ++ wiphy_delayed_work_queue(wiphy, &rtwdev->csa_beacon_work,  + usecs_to_jiffies(delay));  + } else { ++ rtwdev->csa_vif = NULL;  + ieee80211_csa_finish(vif, 0);  + }  + @@ drivers/net/wireless/realtek/rtw88/fw.h: void rtw_add_rsvd_page_pno(struct rtw_d struct rtw_vif *rtwvif); int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev);  +bool rtw_fw_csa_active(struct rtw_dev *rtwdev); ++void rtw_fw_csa_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif); ++void rtw_fw_csa_stop(struct rtw_dev *rtwdev, struct ieee80211_vif *vif); void rtw_fw_update_beacon_work(struct work_struct *work);  +void rtw_fw_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev); @@ drivers/net/wireless/realtek/rtw88/fw.h: void rtw_add_rsvd_page_pno(struct rtw_d u32 offset, u32 size, u32 *buf);  ## drivers/net/wireless/realtek/rtw88/mac80211.c ## -@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_add_interface(struct ieee80211_hw *hw, - memset(&rtwvif->bfee, 0, sizeof(struct rtw_bfee)); - rtw_txq_init(rtwdev, vif->txq); - INIT_LIST_HEAD(&rtwvif->rsvd_page_list); -+ if (!test_bit(RTW_FLAG_RESTARTING, rtwdev->flags)) -+ wiphy_delayed_work_init(&rtwvif->csa_beacon_work, -+ rtw_fw_csa_beacon_work); -  - mutex_lock(&rtwdev->mutex); -   @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_ops_remove_interface(struct ieee80211_hw *hw, rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %pM mac_id %d on port %d\n", vif->addr, rtwvif->mac_id, rtwvif->port);  -+ wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->csa_beacon_work); ++ rtw_fw_csa_stop(rtwdev, vif);  + mutex_lock(&rtwdev->mutex);  rtw_leave_lps_deep(rtwdev); +@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw, + if (vif->cfg.assoc) { + rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH); +  +- rtw_fw_download_rsvd_page(rtwdev); +- rtw_send_rsvd_page_h2c(rtwdev); ++ if (!rtw_fw_csa_active(rtwdev)) { ++ rtw_fw_download_rsvd_page(rtwdev); ++ rtw_send_rsvd_page_h2c(rtwdev); ++ } + rtw_fw_default_port(rtwdev, rtwvif); + rtw_coex_media_status_notify(rtwdev, vif->cfg.assoc); + if (rtw_bf_support)  @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw, rtw_set_dtim_period(rtwdev, conf->dtim_period); rtw_fw_download_rsvd_page(rtwdev); rtw_send_rsvd_page_h2c(rtwdev); -+ if (conf->csa_active) { -+ u32 interval = ieee80211_tu_to_usec(conf->beacon_int); -+ -+ wiphy_delayed_work_queue(hw->wiphy, -+ &rtwvif->csa_beacon_work, -+ usecs_to_jiffies(interval)); -+ } ++ if (conf->csa_active) ++ rtw_fw_csa_start(rtwdev, vif); }  if (changed & BSS_CHANGED_BEACON_ENABLED) {  @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_ops_stop_ap(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link_conf) { -+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_dev *rtwdev = hw->priv;  -+ wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->csa_beacon_work); ++ rtw_fw_csa_stop(rtwdev, vif);  + mutex_lock(&rtwdev->mutex); rtw_write32_clr(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD); @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_set_tim(struct  + struct ieee80211_vif *vif,  + struct cfg80211_chan_def *chandef)  +{ -+ u32 interval = ieee80211_tu_to_usec(vif->bss_conf.beacon_int); -+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; ++ struct rtw_dev *rtwdev = hw->priv;  + -+ wiphy_delayed_work_queue(hw->wiphy, &rtwvif->csa_beacon_work, -+ usecs_to_jiffies(interval)); ++ rtw_fw_csa_start(rtwdev, vif);  +}  + static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_set_key(struct rtw_fw_download_rsvd_page(rtwdev);  out: -@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_get_antenna(struct ieee80211_hw *hw, - } -  - #ifdef CONFIG_PM -+static void rtw_csa_cancel_iter(void *data, struct ieee80211_vif *vif) -+{ -+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; -+ struct rtw_dev *rtwdev = data; -+ -+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwvif->csa_beacon_work); -+} -+ - static int rtw_ops_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan) - {  @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_suspend(struct ieee80211_hw *hw, int ret;  mutex_lock(&rtwdev->mutex); -+ rtw_iterate_vifs(rtwdev, rtw_csa_cancel_iter, rtwdev); ++ rtw_fw_csa_stop(rtwdev, rtwdev->csa_vif); ret = rtw_wow_suspend(rtwdev, wowlan); if (ret) rtw_err(rtwdev, "failed to suspend for wow %d\n", ret); -@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_reconfig_complete(struct ieee80211_hw *hw, - mutex_unlock(&rtwdev->mutex); - } -  -+static void rtw_csa_active_iter(void *data, struct ieee80211_vif *vif) -+{ -+ bool *csa_active = data; -+ -+ if (vif->bss_conf.csa_active) -+ *csa_active = true; -+} -+ - static int rtw_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_scan_request *req) - { +@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw_dev *rtwdev = hw->priv; -+ bool csa_active = false; int ret;  - if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) -@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - return -EBUSY; +- if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) +- return 1; ++ mutex_lock(&rtwdev->mutex);  - mutex_lock(&rtwdev->mutex); +- if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) +- return -EBUSY; ++ if (rtw_fw_csa_active(rtwdev)) { ++ ret = -EBUSY; ++ goto out; ++ }  + -+ rtw_iterate_vifs(rtwdev, rtw_csa_active_iter, &csa_active); -+ if (csa_active) { -+ mutex_unlock(&rtwdev->mutex); -+ return -EBUSY; ++ if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) { ++ ret = 1; ++ goto out;  + }  + ++ if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) { ++ ret = -EBUSY; ++ goto out; ++ } +  +- mutex_lock(&rtwdev->mutex); rtw_hw_scan_start(rtwdev, vif, req); ret = rtw_hw_scan_offload(rtwdev, vif, true); if (ret) { + rtw_hw_scan_abort(rtwdev); + rtw_err(rtwdev, "HW scan failed with status: %d\n", ret); + } ++out: + mutex_unlock(&rtwdev->mutex); +  + return ret;  @@ drivers/net/wireless/realtek/rtw88/mac80211.c: const struct ieee80211_ops rtw_ops = { .sta_add = rtw_ops_sta_add, .sta_remove = rtw_ops_sta_remove, @@ drivers/net/wireless/realtek/rtw88/mac80211.c: const struct ieee80211_ops rtw_op .can_aggregate_in_amsdu = rtw_ops_can_aggregate_in_amsdu,  ## drivers/net/wireless/realtek/rtw88/main.c ## +@@ drivers/net/wireless/realtek/rtw88/main.c: static void rtw_fw_recovery_work(struct work_struct *work) + struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, + fw_recovery_work); +  ++ wiphy_lock(rtwdev->hw->wiphy); ++ rtw_fw_csa_stop(rtwdev, rtwdev->csa_vif); ++ wiphy_unlock(rtwdev->hw->wiphy); ++ + mutex_lock(&rtwdev->mutex); + __fw_recovery_work(rtwdev); + mutex_unlock(&rtwdev->mutex); +@@ drivers/net/wireless/realtek/rtw88/main.c: int rtw_core_init(struct rtw_dev *rtwdev) + INIT_WORK(&rtwdev->ips_work, rtw_ips_work); + INIT_WORK(&rtwdev->fw_recovery_work, rtw_fw_recovery_work); + INIT_WORK(&rtwdev->update_beacon_work, rtw_fw_update_beacon_work); ++ wiphy_delayed_work_init(&rtwdev->csa_beacon_work, ++ rtw_fw_csa_beacon_work); + INIT_WORK(&rtwdev->ba_work, rtw_txq_ba_work); + skb_queue_head_init(&rtwdev->c2h_queue); + skb_queue_head_init(&rtwdev->coex.queue);  @@ drivers/net/wireless/realtek/rtw88/main.c: int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) hw->wiphy->available_antennas_rx = hal->antenna_rx;  @@ drivers/net/wireless/realtek/rtw88/main.c: int rtw_register_hw(struct rtw_dev *r hw->wiphy->max_scan_ssids = RTW_SCAN_MAX_SSIDS;  ## drivers/net/wireless/realtek/rtw88/main.h ## -@@ drivers/net/wireless/realtek/rtw88/main.h: struct rtw_vif { - struct rtw_traffic_stats stats; -  - struct rtw_bfee bfee; -+ +@@ drivers/net/wireless/realtek/rtw88/main.h: struct rtw_dev { + struct work_struct ips_work; + struct work_struct fw_recovery_work; + struct work_struct update_beacon_work;  + struct wiphy_delayed_work csa_beacon_work; - }; ++ struct ieee80211_vif *csa_vif;  - struct rtw_regulatory { + /* used to protect txqs list */ + spinlock_t txq_lock;