wifi: rtw89: split scan including lots of channels
authorPo-Hao Huang <phhuang@realtek.com>
Thu, 8 Sep 2022 05:12:57 +0000 (13:12 +0800)
committerKalle Valo <kvalo@kernel.org>
Mon, 12 Sep 2022 11:52:33 +0000 (14:52 +0300)
The size limit of H2C commands is 2048. With regulatory that enables
U-NII-6 ~ UNII-8 channels, channel list length combining with channel info
length will exceed that. Split the channel list to parts and do scan
multiple times to workaround that.

Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220908051257.25353-10-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/fw.h
drivers/net/wireless/realtek/rtw89/mac.c

index c223fcc..bd2c09e 100644 (file)
@@ -3318,6 +3318,7 @@ struct rtw89_hw_scan_info {
        u8 op_chan;
        u8 op_bw;
        u8 op_band;
+       u32 last_chan_idx;
 };
 
 enum rtw89_phy_bb_gain_band {
@@ -3744,6 +3745,16 @@ static inline struct ieee80211_vif *rtwvif_to_vif(struct rtw89_vif *rtwvif)
        return container_of(p, struct ieee80211_vif, drv_priv);
 }
 
+static inline struct ieee80211_vif *rtwvif_to_vif_safe(struct rtw89_vif *rtwvif)
+{
+       return rtwvif ? rtwvif_to_vif(rtwvif) : NULL;
+}
+
+static inline struct rtw89_vif *vif_to_rtwvif_safe(struct ieee80211_vif *vif)
+{
+       return vif ? (struct rtw89_vif *)vif->drv_priv : NULL;
+}
+
 static inline struct ieee80211_sta *rtwsta_to_sta(struct rtw89_sta *rtwsta)
 {
        void *p = rtwsta;
index 1dfb470..f93d65c 100644 (file)
@@ -2451,13 +2451,16 @@ static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev,
        struct ieee80211_channel *channel;
        struct list_head chan_list;
        bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN;
-       int list_len = req->n_channels, off_chan_time = 0;
+       int list_len, off_chan_time = 0;
        enum rtw89_chan_type type;
-       int ret = 0, i;
+       int ret = 0;
+       u32 idx;
 
        INIT_LIST_HEAD(&chan_list);
-       for (i = 0; i < req->n_channels; i++) {
-               channel = req->channels[i];
+       for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0;
+            idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT;
+            idx++, list_len++) {
+               channel = req->channels[idx];
                ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
                if (!ch_info) {
                        ret = -ENOMEM;
@@ -2498,6 +2501,7 @@ static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev,
                list_add_tail(&ch_info->list, &chan_list);
                off_chan_time += ch_info->period;
        }
+       rtwdev->scan_info.last_chan_idx = idx;
        ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list);
 
 out:
@@ -2532,6 +2536,7 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
        u8 mac_addr[ETH_ALEN];
 
        rtwdev->scan_info.scanning_vif = vif;
+       rtwdev->scan_info.last_chan_idx = 0;
        rtwvif->scan_ies = &scan_req->ies;
        rtwvif->scan_req = req;
        ieee80211_stop_queues(rtwdev->hw);
@@ -2579,6 +2584,7 @@ void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
        rtwvif = (struct rtw89_vif *)vif->drv_priv;
        rtwvif->scan_req = NULL;
        rtwvif->scan_ies = NULL;
+       rtwdev->scan_info.last_chan_idx = 0;
        rtwdev->scan_info.scanning_vif = NULL;
 
        if (rtwvif->net_type != RTW89_NET_TYPE_NO_LINK)
index fc968e2..a055ea5 100644 (file)
@@ -188,6 +188,7 @@ struct rtw89_h2creg_sch_tx_en {
        u16 rsvd:15;
 } __packed;
 
+#define RTW89_H2C_MAX_SIZE 2048
 #define RTW89_CHANNEL_TIME 45
 #define RTW89_DFS_CHAN_TIME 105
 #define RTW89_OFF_CHAN_TIME 100
@@ -198,6 +199,9 @@ struct rtw89_h2creg_sch_tx_en {
 #define RTW89_SCANOFLD_PKT_NONE 0xFF
 #define RTW89_SCANOFLD_DEBUG_MASK 0x1F
 #define RTW89_MAC_CHINFO_SIZE 24
+#define RTW89_SCAN_LIST_GUARD 4
+#define RTW89_SCAN_LIST_LIMIT \
+               ((RTW89_H2C_MAX_SIZE / RTW89_MAC_CHINFO_SIZE) - RTW89_SCAN_LIST_GUARD)
 
 struct rtw89_mac_chinfo {
        u8 period;
index 667ac3f..dd9dbe5 100644 (file)
@@ -3734,9 +3734,12 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
                           u32 len)
 {
        struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif;
+       struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif);
        struct rtw89_chan new;
        u8 reason, status, tx_fail, band, actual_period;
+       u32 last_chan = rtwdev->scan_info.last_chan_idx;
        u16 chan;
+       int ret;
 
        tx_fail = RTW89_GET_MAC_C2H_SCANOFLD_TX_FAIL(c2h->data);
        status = RTW89_GET_MAC_C2H_SCANOFLD_STATUS(c2h->data);
@@ -3758,7 +3761,16 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
                        ieee80211_stop_queues(rtwdev->hw);
                return;
        case RTW89_SCAN_END_SCAN_NOTIFY:
-               rtw89_hw_scan_complete(rtwdev, vif, false);
+               if (rtwvif && rtwvif->scan_req &&
+                   last_chan < rtwvif->scan_req->n_channels) {
+                       ret = rtw89_hw_scan_offload(rtwdev, vif, true);
+                       if (ret) {
+                               rtw89_hw_scan_abort(rtwdev, vif);
+                               rtw89_warn(rtwdev, "HW scan failed: %d\n", ret);
+                       }
+               } else {
+                       rtw89_hw_scan_complete(rtwdev, vif, false);
+               }
                break;
        case RTW89_SCAN_ENTER_CH_NOTIFY:
                rtw89_chan_create(&new, chan, chan, band, RTW89_CHANNEL_WIDTH_20);