1: 553d4178355a = 1: 03704776a73c wifi: rtw88: download the beacon the reserved page was built with 2: a3b2c672a61f < -: ------------ wifi: rtw88: update the beacon from a wiphy work 3: f434e2535709 ! 2: 75a286ddcfa5 wifi: rtw88: support channel switch in AP mode @@ Commit message wifi: rtw88: support channel switch in AP mode  hostapd's CHAN_SWITCH is refused because the driver does not announce - channel switch support, so an AP on rtw88 can only change its channel - by being torn down and started again. + channel switch support, so an rtw88 AP can only change its channel by + being torn down and started again.  Declare WIPHY_FLAG_HAS_CHANNEL_SWITCH and implement - ieee80211_ops::channel_switch_beacon: the firmware repeats the beacon - held in the first reserved page, so while a switch is announced the - page is downloaded again every beacon interval to renew the countdown, - and ieee80211_csa_finish() is called once it completes. IBSS, which - the flag enables too, shares the page and the work. + channel_switch_beacon: the firmware repeats the beacon held in the + first reserved page, so while a switch is announced the page is + downloaded again every beacon interval to advance the countdown, and + ieee80211_csa_finish() is called once it completes. IBSS shares the + page and the work.  - 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. + The work is a delayed work of the device and csa_vif names the + interface, both under the driver mutex. The countdown is cancelled when + the AP stops, when the interface goes away, on WoWLAN suspend and in + rtw_core_stop(); the BSS_CHANGED_BEACON replay after a hardware + restart resumes it. The other page downloads during a countdown, the + TIM update, the PG backup and the one on association, are skipped + since every beacon fetch advances it; the next countdown download + carries their changes. A scan is refused: 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   +bool rtw_fw_csa_active(struct rtw_dev *rtwdev)  +{ -+ lockdep_assert_wiphy(rtwdev->hw->wiphy); ++ lockdep_assert_held(&rtwdev->mutex);  + -+ return rtwdev->csa_vif && rtwdev->csa_vif->bss_conf.csa_active; ++ return !!rtwdev->csa_vif;  +}  + -+void rtw_fw_csa_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) ++/* paced from the first download: a late one does not delay the next */ ++static void rtw_fw_csa_queue(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)  +{  + u32 interval = ieee80211_tu_to_usec(vif->bss_conf.beacon_int); ++ s64 delay; ++ ++ rtwdev->csa_due = ktime_add_us(rtwdev->csa_due, interval); ++ delay = ktime_us_delta(rtwdev->csa_due, ktime_get()); ++ ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->csa_beacon_work, ++ usecs_to_jiffies(max(delay, 0))); ++}  + -+ lockdep_assert_wiphy(rtwdev->hw->wiphy); ++void rtw_fw_csa_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) ++{ ++ lockdep_assert_held(&rtwdev->mutex); ++ ++ if (rtwdev->csa_vif == vif) ++ return;  +  + rtwdev->csa_vif = vif; -+ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->csa_beacon_work, -+ usecs_to_jiffies(interval)); ++ rtwdev->csa_due = ktime_get(); ++ rtw_fw_csa_queue(rtwdev, vif);  +}  +  +void rtw_fw_csa_stop(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)  +{ -+ lockdep_assert_wiphy(rtwdev->hw->wiphy); ++ lockdep_assert_held(&rtwdev->mutex);  +  + if (!vif || rtwdev->csa_vif != vif)  + return;  + -+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->csa_beacon_work);  + rtwdev->csa_vif = NULL; ++ cancel_delayed_work(&rtwdev->csa_beacon_work);  +}  + - void rtw_fw_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) + void rtw_fw_update_beacon_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, -@@ drivers/net/wireless/realtek/rtw88/fw.c: void rtw_fw_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) + update_beacon_work);  mutex_lock(&rtwdev->mutex); -  -- if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) -+ if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags) || -+ rtw_fw_csa_active(rtwdev)) - goto out; -  - rtw_fw_download_rsvd_page(rtwdev); -@@ drivers/net/wireless/realtek/rtw88/fw.c: void rtw_fw_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) - mutex_unlock(&rtwdev->mutex); - } -  -+void rtw_fw_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) +- rtw_fw_download_rsvd_page(rtwdev); +- rtw_send_rsvd_page_h2c(rtwdev); ++ ++ /* a fetch advances the countdown, the next download carries the TIM */ ++ if (!rtw_fw_csa_active(rtwdev)) { ++ rtw_fw_download_rsvd_page(rtwdev); ++ rtw_send_rsvd_page_h2c(rtwdev); ++ } ++ ++ mutex_unlock(&rtwdev->mutex); ++} ++ ++void rtw_fw_csa_beacon_work(struct work_struct *work)  +{  + struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,  + csa_beacon_work.work); -+ struct ieee80211_vif *vif = rtwdev->csa_vif; -+ unsigned int delay; -+ -+ lockdep_assert_wiphy(wiphy); ++ struct ieee80211_vif *vif;  +  + mutex_lock(&rtwdev->mutex);  + ++ vif = rtwdev->csa_vif; ++ if (!vif) ++ goto out; ++  + if (!vif->bss_conf.csa_active) {  + rtwdev->csa_vif = NULL;  + goto out;  + }  + -+ delay = ieee80211_tu_to_usec(vif->bss_conf.beacon_int); -+  + if (!ieee80211_beacon_cntdwn_is_complete(vif, 0)) {  + rtw_fw_download_rsvd_page(rtwdev);  + rtw_send_rsvd_page_h2c(rtwdev); -+ -+ wiphy_delayed_work_queue(wiphy, &rtwdev->csa_beacon_work, -+ usecs_to_jiffies(delay)); ++ rtw_fw_csa_queue(rtwdev, vif);  + } else {  + rtwdev->csa_vif = NULL;  + ieee80211_csa_finish(vif, 0);  + }  +  +out: -+ mutex_unlock(&rtwdev->mutex); -+} -+ - static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size, - u32 *buf, u32 residue, u16 start_pg) - { + mutex_unlock(&rtwdev->mutex); + } +   ## drivers/net/wireless/realtek/rtw88/fw.h ##  @@ drivers/net/wireless/realtek/rtw88/fw.h: void rtw_add_rsvd_page_pno(struct rtw_dev *rtwdev, @@ drivers/net/wireless/realtek/rtw88/fw.h: void rtw_add_rsvd_page_pno(struct rtw_d  +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 wiphy *wiphy, struct wiphy_work *work); -+void rtw_fw_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); + void rtw_fw_update_beacon_work(struct work_struct *work); ++void rtw_fw_csa_beacon_work(struct work_struct *work); void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev); int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev, u32 offset, u32 size, u32 *buf);  ## drivers/net/wireless/realtek/rtw88/mac80211.c ##  @@ 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);  -+ rtw_fw_csa_stop(rtwdev, vif); -+ mutex_lock(&rtwdev->mutex);  ++ rtw_fw_csa_stop(rtwdev, vif); ++ rtw_leave_lps_deep(rtwdev); +  + rtw_txq_cleanup(rtwdev, vif->txq);  @@ 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); -+ /* a station of the STA+AP combination: keep the AP's countdown */ ++ /* a station associating while the AP counts down */  + if (!rtw_fw_csa_active(rtwdev)) {  + rtw_fw_download_rsvd_page(rtwdev);  + rtw_send_rsvd_page_h2c(rtwdev); @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_ops_bss_info_chan rtw_set_dtim_period(rtwdev, conf->dtim_period); rtw_fw_download_rsvd_page(rtwdev); rtw_send_rsvd_page_h2c(rtwdev); ++ /* restart replay resumes it, a beacon without CSA ends it */  + if (conf->csa_active)  + rtw_fw_csa_start(rtwdev, vif); ++ else ++ rtw_fw_csa_stop(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 rtw_dev *rtwdev = hw->priv;  -+ rtw_fw_csa_stop(rtwdev, vif); -+ mutex_lock(&rtwdev->mutex); ++ rtw_fw_csa_stop(rtwdev, vif); rtw_write32_clr(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD); rtw_write16(rtwdev, REG_ATIMWND, ATIMWND_DEFAULT); + rtwdev->ap_active = false;  @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, return 0; } @@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_set_tim(struct  +{  + struct rtw_dev *rtwdev = hw->priv;  + ++ mutex_lock(&rtwdev->mutex);  + rtw_fw_csa_start(rtwdev, vif); ++ mutex_unlock(&rtwdev->mutex);  +}  + 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_hw_scan(struct   + guard(mutex)(&rtwdev->mutex);  + ++ /* a scan takes the AP off the channel its stations count down to */  + if (rtw_fw_csa_active(rtwdev))  + return -EBUSY;  + @@ 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); +@@ drivers/net/wireless/realtek/rtw88/main.c: void rtw_core_stop(struct rtw_dev *rtwdev) +  + clear_bit(RTW_FLAG_RUNNING, rtwdev->flags); + clear_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags); ++ rtwdev->csa_vif = NULL;  -+ 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); +  + cancel_work_sync(&rtwdev->c2h_work); + cancel_work_sync(&rtwdev->update_beacon_work); ++ cancel_delayed_work_sync(&rtwdev->csa_beacon_work); + cancel_delayed_work_sync(&rtwdev->watch_dog_work); + cancel_delayed_work_sync(&coex->bt_relink_work); + cancel_delayed_work_sync(&coex->bt_reenable_work);  @@ 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); - wiphy_work_init(&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->update_beacon_work, rtw_fw_update_beacon_work); ++ INIT_DELAYED_WORK(&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.h  @@ drivers/net/wireless/realtek/rtw88/main.h: struct rtw_dev { struct work_struct ips_work; struct work_struct fw_recovery_work; - struct wiphy_work update_beacon_work; -+ struct wiphy_delayed_work csa_beacon_work; + struct work_struct update_beacon_work; ++ struct delayed_work csa_beacon_work;  + struct ieee80211_vif *csa_vif; ++ ktime_t csa_due;  /* used to protect txqs list */ spinlock_t txq_lock;