1: fe02d2127278 ! 1: bef2154798e7 wifi: rtw88: download the beacon the reserved page was built with @@ Commit message Besides the extra work, every beacon fetch advances the DTIM count and, while a channel switch is announced, the CSA countdown; doing it twice per update lets a countdown that starts at 2 reach 0, which mac80211 - warns about. Keep the beacon skb from the page build and download that. + warns about. Hand the beacon skb out of the page build and download + that. The hw scan, which downloads the beacon without a page build, + keeps fetching its own.  Signed-off-by: Mehmet Fide   ## drivers/net/wireless/realtek/rtw88/fw.c ## +@@ drivers/net/wireless/realtek/rtw88/fw.c: static int __rtw_build_rsvd_page_from_vifs(struct rtw_dev *rtwdev) + return 0; + } +  +-static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size) ++static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size, ++ struct sk_buff **beacon) + { + const struct rtw_chip_info *chip = rtwdev->chip; + struct ieee80211_hw *hw = rtwdev->hw;  @@ drivers/net/wireless/realtek/rtw88/fw.c: static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size) list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin, @@ drivers/net/wireless/realtek/rtw88/fw.c: static u8 *rtw_build_rsvd_page(struct r page += rtw_len_to_page(rsvd_pkt->skb->len + tx_desc_sz, page_size);  - else -- page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); -+ /* keep the beacon for rtw_download_beacon() */ -+ continue; ++ /* the caller downloads it once more on its own */ ++ *beacon = rsvd_pkt->skb; ++ } else { + page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); +- +- kfree_skb(rsvd_pkt->skb); ++ kfree_skb(rsvd_pkt->skb);  + } -+ page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); -  - kfree_skb(rsvd_pkt->skb); rsvd_pkt->skb = NULL; + } +   @@ drivers/net/wireless/realtek/rtw88/fw.c: static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size) + return NULL; + }  - static int rtw_download_beacon(struct rtw_dev *rtwdev) +-static int rtw_download_beacon(struct rtw_dev *rtwdev) ++/* the beacon the page was built with, or a fresh one for the hw scan */ ++static int rtw_download_beacon(struct rtw_dev *rtwdev, struct sk_buff *beacon) { -- struct ieee80211_hw *hw = rtwdev->hw; + struct ieee80211_hw *hw = rtwdev->hw; struct rtw_rsvd_page *rsvd_pkt; - struct sk_buff *skb; +- struct sk_buff *skb; ++ struct sk_buff *skb = beacon; int ret = 0; +  + rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list,  @@ drivers/net/wireless/realtek/rtw88/fw.c: static int rtw_download_beacon(struct rtw_dev *rtwdev) return -EINVAL; }   - skb = rtw_get_rsvd_page_skb(hw, rsvd_pkt); -+ /* the beacon kept by rtw_build_rsvd_page() */ -+ skb = rsvd_pkt->skb; ++ if (!skb) ++ skb = rtw_get_rsvd_page_skb(hw, rsvd_pkt); if (!skb) { rtw_err(rtwdev, "failed to get beacon skb\n"); -- return -ENOMEM; -+ return -ENOENT; - } -  - ret = rtw_download_drv_rsvd_page(rtwdev, skb->data, skb->len); + return -ENOMEM; +@@ drivers/net/wireless/realtek/rtw88/fw.c: static int rtw_download_beacon(struct rtw_dev *rtwdev) if (ret) rtw_err(rtwdev, "failed to download drv rsvd page\n");   - dev_kfree_skb(skb); -- ++ if (!beacon) ++ dev_kfree_skb(skb); +  return ret; }  int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev) {  - u8 *buf; -+ struct rtw_rsvd_page *rsvd_pkt; ++ struct sk_buff *beacon; u32 size;  + u8 *buf; int ret;  - buf = rtw_build_rsvd_page(rtwdev, &size); +- buf = rtw_build_rsvd_page(rtwdev, &size); ++ buf = rtw_build_rsvd_page(rtwdev, &size, &beacon); + if (!buf) { + rtw_err(rtwdev, "failed to build rsvd page pkt\n"); + return -ENOMEM;  @@ drivers/net/wireless/realtek/rtw88/fw.c: int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev) + * the beacon again to replace the TX desc header, and we will get + * a correct tx_desc for the beacon in the rsvd page. + */ +- ret = rtw_download_beacon(rtwdev); ++ ret = rtw_download_beacon(rtwdev, beacon); + if (ret) { + rtw_err(rtwdev, "failed to download beacon\n"); + goto free; + } +  free: ++ dev_kfree_skb(beacon); kfree(buf);  -+ /* free the beacon kept by rtw_build_rsvd_page() */ -+ rsvd_pkt = list_first_entry(&rtwdev->rsvd_page_list, -+ struct rtw_rsvd_page, build_list); -+ kfree_skb(rsvd_pkt->skb); -+ rsvd_pkt->skb = NULL; -+ return ret; - } -  +@@ drivers/net/wireless/realtek/rtw88/fw.c: int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, + rtw_fw_set_scan_offload(rtwdev, &cs_option, rtwvif, &chan_list); + out: + if (rtwdev->ap_active) { +- ret = rtw_download_beacon(rtwdev); ++ ret = rtw_download_beacon(rtwdev, NULL); + if (ret) + rtw_err(rtwdev, "HW scan download beacon failed\n"); + } 2: 41e42be25f20 ! 2: 48b873b2843b wifi: rtw88: support channel switch in AP mode @@ Commit message by being torn down and started again.  Declare WIPHY_FLAG_HAS_CHANNEL_SWITCH and implement - ieee80211_ops::channel_switch_beacon the way rtw89 does: the firmware - repeats the beacon held in the reserved page, so while a switch is - announced the beacon is downloaded again every beacon interval to renew - the countdown, and ieee80211_csa_finish() is called once it completes. + 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. + + The work is a wiphy delayed work of the device, like + update_beacon_work: rtw88 runs one beaconing interface, a hw restart + replays add_interface without remove_interface, and the wiphy lock + serializes it with the mac80211 state it reads. It is cancelled when + the AP stops, when the vif goes away and on WoWLAN suspend, and a + hardware scan is refused while a switch is announced, since 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: void rtw_fw_update_beacon_work(struct w mutex_unlock(&rtwdev->mutex); }  -+void rtw_fw_csa_beacon_work(struct work_struct *work) ++/* renew the countdown in the firmware's beacon page until it completes */ ++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 ieee80211_vif *vif = rtwvif_to_vif(rtwvif); -+ struct rtw_dev *rtwdev = rtwvif->rtwdev; ++ struct rtw_rsvd_page *rsvd_pkt; ++ struct ieee80211_vif *vif;  + unsigned int delay;  + ++ lockdep_assert_wiphy(wiphy); ++  + mutex_lock(&rtwdev->mutex);  + -+ if (rtwvif->net_type != RTW_NET_AP_MODE || !vif->bss_conf.csa_active) ++ if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) ++ goto out; ++ ++ 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) ++ goto out; ++ ++ vif = rtwvif_to_vif(rsvd_pkt->rtwvif); ++ if (!vif->bss_conf.csa_active)  + goto out;  +  + delay = ieee80211_tu_to_usec(vif->bss_conf.beacon_int); @@ drivers/net/wireless/realtek/rtw88/fw.c: void rtw_fw_update_beacon_work(struct w  + rtw_fw_download_rsvd_page(rtwdev);  + rtw_send_rsvd_page_h2c(rtwdev);  + -+ ieee80211_queue_delayed_work(rtwdev->hw, &rtwvif->csa_beacon_work, -+ usecs_to_jiffies(delay)); ++ wiphy_delayed_work_queue(wiphy, &rtwdev->csa_beacon_work, ++ usecs_to_jiffies(delay));  + } else {  + ieee80211_csa_finish(vif, 0);  + } @@ drivers/net/wireless/realtek/rtw88/fw.h: void rtw_add_rsvd_page_sta(struct rtw_d struct rtw_vif *rtwvif); int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev); void rtw_fw_update_beacon_work(struct work_struct *work); -+void rtw_fw_csa_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); 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 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); -+ rtwvif->rtwdev = rtwdev; -+ INIT_DELAYED_WORK(&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);  -+ cancel_delayed_work_sync(&rtwvif->csa_beacon_work); ++ if (rtwvif->net_type == RTW_NET_AP_MODE || ++ rtwvif->net_type == RTW_NET_AD_HOC) ++ wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->csa_beacon_work);  + mutex_lock(&rtwdev->mutex);  rtw_leave_lps_deep(rtwdev); +@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static void rtw_conf_tx(struct rtw_dev *rtwdev, + __rtw_conf_tx(rtwdev, rtwvif, ac); + } +  ++/* renew the channel switch countdown one beacon interval from now */ ++static void rtw_csa_beacon_queue(struct rtw_dev *rtwdev, u16 beacon_int) ++{ ++ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->csa_beacon_work, ++ usecs_to_jiffies(ieee80211_tu_to_usec(beacon_int))); ++} ++ + static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, +@@ 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); ++ /* a hw restart replays the beacon, not channel_switch_beacon */ ++ if (conf->csa_active) ++ rtw_csa_beacon_queue(rtwdev, conf->beacon_int); + } +  + 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; +  ++ wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->csa_beacon_work); ++ + mutex_lock(&rtwdev->mutex); + rtw_write32_clr(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD); + rtw_write16(rtwdev, REG_ATIMWND, ATIMWND_DEFAULT);  @@ 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 ieee80211_vif *vif,  + struct cfg80211_chan_def *chandef)  +{ -+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; ++ struct rtw_dev *rtwdev = hw->priv;  + -+ ieee80211_queue_delayed_work(hw, &rtwvif->csa_beacon_work, 0); ++ /* the beacon that starts the countdown was just downloaded */ ++ rtw_csa_beacon_queue(rtwdev, vif->bss_conf.beacon_int);  +}  + static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) +@@ drivers/net/wireless/realtek/rtw88/mac80211.c: static int rtw_ops_suspend(struct ieee80211_hw *hw, + struct rtw_dev *rtwdev = hw->priv; + int ret; +  ++ wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->csa_beacon_work); ++ + mutex_lock(&rtwdev->mutex); + ret = rtw_wow_suspend(rtwdev, wowlan); + if (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) + { + 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; +  + mutex_lock(&rtwdev->mutex); ++ ++ /* the stations count down to the new channel and expect the AP there */ ++ rtw_iterate_vifs(rtwdev, rtw_csa_active_iter, &csa_active); ++ if (csa_active) { ++ mutex_unlock(&rtwdev->mutex); ++ return -EBUSY; ++ } ++ + rtw_hw_scan_start(rtwdev, vif, req); + ret = rtw_hw_scan_offload(rtwdev, vif, true); + if (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: 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_bf_info { - }; -  - struct rtw_vif { -+ struct rtw_dev *rtwdev; - enum rtw_net_type net_type; - u16 aid; - u8 mac_id; -@@ drivers/net/wireless/realtek/rtw88/main.h: struct rtw_vif { - const struct rtw_vif_port *conf; - struct cfg80211_scan_request *scan_req; - struct ieee80211_scan_ies *scan_ies; -+ struct delayed_work csa_beacon_work; -  - struct rtw_traffic_stats stats; +@@ 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;  + /* used to protect txqs list */ + spinlock_t txq_lock;