@@ -3314,6 +3314,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 {
@@ -3740,6 +3741,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;
@@ -2405,13 +2405,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;
@@ -2452,6 +2455,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:
@@ -2486,6 +2490,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);
@@ -2533,6 +2538,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)
@@ -177,6 +177,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
@@ -187,6 +188,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;
@@ -3715,9 +3715,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);
@@ -3739,7 +3742,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);