]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
wifi: rtw89: wow: add WoWLAN net-detect support
authorChin-Yen Lee <timlee@realtek.com>
Mon, 5 Aug 2024 09:00:26 +0000 (17:00 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Fri, 9 Aug 2024 00:39:49 +0000 (08:39 +0800)
Net-detect is an option of WoWLAN to allow the device to
be woken up from suspend mode when configured network is detected.

When user enables net-detect and lets the device enter suspend
state, WoWLAN firmware will periodically scan until beacon or
probe response of configured networks are received. If configured
networks are detected, WoWLAN firmware will trigger resume process.

Signed-off-by: Chin-Yen Lee <timlee@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240805090028.27768-4-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.c
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
drivers/net/wireless/realtek/rtw89/mac.h
drivers/net/wireless/realtek/rtw89/mac_be.c
drivers/net/wireless/realtek/rtw89/wow.c
drivers/net/wireless/realtek/rtw89/wow.h

index a71c1147d3ce96b72bf71699e51dc8ded82f9d7d..7b28f2c2a08eb42c96f187772a6d62376ec39e0a 100644 (file)
@@ -4688,6 +4688,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
 
 #ifdef CONFIG_PM
        hw->wiphy->wowlan = rtwdev->chip->wowlan_stub;
+       hw->wiphy->max_sched_scan_ssids = RTW89_SCANOFLD_MAX_SSID;
 #endif
 
        hw->wiphy->tid_config_support.vif |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL);
index ecef3b154f3ef486104da8939eb40f8b8758e51e..f40475a94d11afd13a6de7dac3de64085f67e4c4 100644 (file)
@@ -927,10 +927,12 @@ enum rtw89_sc_offset {
        RTW89_SC_40_LOWER       = 10,
 };
 
+/* only mgd features can be added to the enum */
 enum rtw89_wow_flags {
        RTW89_WOW_FLAG_EN_MAGIC_PKT,
        RTW89_WOW_FLAG_EN_REKEY_PKT,
        RTW89_WOW_FLAG_EN_DISCONNECT,
+       RTW89_WOW_FLAG_EN_PATTERN,
        RTW89_WOW_FLAG_NUM,
 };
 
@@ -5308,6 +5310,10 @@ struct rtw89_wow_param {
        u8 gtk_alg;
        u8 ptk_keyidx;
        u8 akm;
+
+       bool pno_inited;
+       struct list_head pno_pkt_list;
+       struct cfg80211_sched_scan_request *nd_config;
 };
 
 struct rtw89_mcc_limit {
index e545d2a7fb49d4d205e0dc259b2616a772d1cfe8..2a55f47a0ac85562995419996a3284b42e2058ab 100644 (file)
@@ -4808,9 +4808,10 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num,
        return 0;
 }
 
-int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev,
-                             struct rtw89_scan_option *option,
-                             struct rtw89_vif *rtwvif)
+int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,
+                                struct rtw89_scan_option *option,
+                                struct rtw89_vif *rtwvif,
+                                bool wowlan)
 {
        struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;
        struct rtw89_chan *op = &rtwdev->scan_info.op_chan;
@@ -4838,7 +4839,10 @@ int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev,
                                   RTW89_H2C_SCANOFLD_W1_TARGET_CH_MODE) |
                  le32_encode_bits(RTW89_SCAN_IMMEDIATE,
                                   RTW89_H2C_SCANOFLD_W1_START_MODE) |
-                 le32_encode_bits(RTW89_SCAN_ONCE, RTW89_H2C_SCANOFLD_W1_SCAN_TYPE);
+                 le32_encode_bits(option->repeat, RTW89_H2C_SCANOFLD_W1_SCAN_TYPE);
+
+       h2c->w2 = le32_encode_bits(option->norm_pd, RTW89_H2C_SCANOFLD_W2_NORM_PD) |
+                 le32_encode_bits(option->slow_pd, RTW89_H2C_SCANOFLD_W2_SLOW_PD);
 
        if (option->target_ch_mode) {
                h2c->w1 |= le32_encode_bits(op->band_width,
@@ -4894,7 +4898,8 @@ static void rtw89_scan_get_6g_disabled_chan(struct rtw89_dev *rtwdev,
 
 int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
                                 struct rtw89_scan_option *option,
-                                struct rtw89_vif *rtwvif)
+                                struct rtw89_vif *rtwvif,
+                                bool wowlan)
 {
        struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
        struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;
@@ -4930,11 +4935,13 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
 
        memset(probe_id, RTW89_SCANOFLD_PKT_NONE, sizeof(probe_id));
 
-       list_for_each_entry(pkt_info, &scan_info->pkt_list[NL80211_BAND_6GHZ], list) {
-               if (pkt_info->wildcard_6ghz) {
-                       /* Provide wildcard as template */
-                       probe_id[NL80211_BAND_6GHZ] = pkt_info->id;
-                       break;
+       if (!wowlan) {
+               list_for_each_entry(pkt_info, &scan_info->pkt_list[NL80211_BAND_6GHZ], list) {
+                       if (pkt_info->wildcard_6ghz) {
+                               /* Provide wildcard as template */
+                               probe_id[NL80211_BAND_6GHZ] = pkt_info->id;
+                               break;
+                       }
                }
        }
 
@@ -4973,7 +4980,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
                                   RTW89_H2C_SCANOFLD_BE_W6_CHAN_PROHIB_LOW);
        h2c->w7 = le32_encode_bits(option->prohib_chan >> 32,
                                   RTW89_H2C_SCANOFLD_BE_W7_CHAN_PROHIB_HIGH);
-       if (req->no_cck) {
+       if (!wowlan && req->no_cck) {
                h2c->w0 |= le32_encode_bits(true, RTW89_H2C_SCANOFLD_BE_W0_PROBE_WITH_RATE);
                h2c->w8 = le32_encode_bits(RTW89_HW_RATE_OFDM6,
                                           RTW89_H2C_SCANOFLD_BE_W8_PROBE_RATE_2GHZ) |
@@ -5966,6 +5973,56 @@ out:
        return ret;
 }
 
+static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev,
+                                      int chan_type, int ssid_num,
+                                      struct rtw89_mac_chinfo *ch_info)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct rtw89_pktofld_info *info;
+       u8 probe_count = 0;
+
+       ch_info->notify_action = RTW89_SCANOFLD_DEBUG_MASK;
+       ch_info->dfs_ch = chan_type == RTW89_CHAN_DFS;
+       ch_info->bw = RTW89_SCAN_WIDTH;
+       ch_info->tx_pkt = true;
+       ch_info->cfg_tx_pwr = false;
+       ch_info->tx_pwr_idx = 0;
+       ch_info->tx_null = false;
+       ch_info->pause_data = false;
+       ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE;
+
+       if (ssid_num) {
+               list_for_each_entry(info, &rtw_wow->pno_pkt_list, list) {
+                       if (info->channel_6ghz &&
+                           ch_info->pri_ch != info->channel_6ghz)
+                               continue;
+                       else if (info->channel_6ghz && probe_count != 0)
+                               ch_info->period += RTW89_CHANNEL_TIME_6G;
+
+                       if (info->wildcard_6ghz)
+                               continue;
+
+                       ch_info->pkt_id[probe_count++] = info->id;
+                       if (probe_count >= RTW89_SCANOFLD_MAX_SSID)
+                               break;
+               }
+               ch_info->num_pkt = probe_count;
+       }
+
+       switch (chan_type) {
+       case RTW89_CHAN_DFS:
+               if (ch_info->ch_band != RTW89_BAND_6G)
+                       ch_info->period = max_t(u8, ch_info->period,
+                                               RTW89_DFS_CHAN_TIME);
+               ch_info->dwell_time = RTW89_DWELL_TIME;
+               break;
+       case RTW89_CHAN_ACTIVE:
+               break;
+       default:
+               rtw89_err(rtwdev, "Channel type out of bound\n");
+       }
+}
+
 static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type,
                                   int ssid_num,
                                   struct rtw89_mac_chinfo *ch_info)
@@ -6044,6 +6101,45 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type,
        }
 }
 
+static void rtw89_pno_scan_add_chan_be(struct rtw89_dev *rtwdev, int chan_type,
+                                      int ssid_num,
+                                      struct rtw89_mac_chinfo_be *ch_info)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct rtw89_pktofld_info *info;
+       u8 probe_count = 0, i;
+
+       ch_info->notify_action = RTW89_SCANOFLD_DEBUG_MASK;
+       ch_info->dfs_ch = chan_type == RTW89_CHAN_DFS;
+       ch_info->bw = RTW89_SCAN_WIDTH;
+       ch_info->tx_null = false;
+       ch_info->pause_data = false;
+       ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE;
+
+       if (ssid_num) {
+               list_for_each_entry(info, &rtw_wow->pno_pkt_list, list) {
+                       ch_info->pkt_id[probe_count++] = info->id;
+                       if (probe_count >= RTW89_SCANOFLD_MAX_SSID)
+                               break;
+               }
+       }
+
+       for (i = probe_count; i < RTW89_SCANOFLD_MAX_SSID; i++)
+               ch_info->pkt_id[i] = RTW89_SCANOFLD_PKT_NONE;
+
+       switch (chan_type) {
+       case RTW89_CHAN_DFS:
+               ch_info->period = max_t(u8, ch_info->period, RTW89_DFS_CHAN_TIME);
+               ch_info->dwell_time = RTW89_DWELL_TIME;
+               break;
+       case RTW89_CHAN_ACTIVE:
+               break;
+       default:
+               rtw89_warn(rtwdev, "Channel type out of bound\n");
+               break;
+       }
+}
+
 static void rtw89_hw_scan_add_chan_be(struct rtw89_dev *rtwdev, int chan_type,
                                      int ssid_num,
                                      struct rtw89_mac_chinfo_be *ch_info)
@@ -6106,8 +6202,58 @@ static void rtw89_hw_scan_add_chan_be(struct rtw89_dev *rtwdev, int chan_type,
        }
 }
 
-int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev,
-                               struct rtw89_vif *rtwvif, bool connected)
+int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
+                                   struct rtw89_vif *rtwvif)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config;
+       struct rtw89_mac_chinfo *ch_info, *tmp;
+       struct ieee80211_channel *channel;
+       struct list_head chan_list;
+       int list_len;
+       enum rtw89_chan_type type;
+       int ret = 0;
+       u32 idx;
+
+       INIT_LIST_HEAD(&chan_list);
+       for (idx = 0, list_len = 0;
+            idx < nd_config->n_channels && list_len < RTW89_SCAN_LIST_LIMIT;
+            idx++, list_len++) {
+               channel = nd_config->channels[idx];
+               ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
+               if (!ch_info) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ch_info->period = RTW89_CHANNEL_TIME;
+               ch_info->ch_band = rtw89_nl80211_to_hw_band(channel->band);
+               ch_info->central_ch = channel->hw_value;
+               ch_info->pri_ch = channel->hw_value;
+               ch_info->is_psc = cfg80211_channel_is_psc(channel);
+
+               if (channel->flags &
+                   (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR))
+                       type = RTW89_CHAN_DFS;
+               else
+                       type = RTW89_CHAN_ACTIVE;
+
+               rtw89_pno_scan_add_chan_ax(rtwdev, type, nd_config->n_match_sets, ch_info);
+               list_add_tail(&ch_info->list, &chan_list);
+       }
+       ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list);
+
+out:
+       list_for_each_entry_safe(ch_info, tmp, &chan_list, list) {
+               list_del(&ch_info->list);
+               kfree(ch_info);
+       }
+
+       return ret;
+}
+
+int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
+                                  struct rtw89_vif *rtwvif, bool connected)
 {
        struct cfg80211_scan_request *req = rtwvif->scan_req;
        struct rtw89_mac_chinfo *ch_info, *tmp;
@@ -6183,6 +6329,58 @@ out:
        return ret;
 }
 
+int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
+                                   struct rtw89_vif *rtwvif)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config;
+       struct rtw89_mac_chinfo_be *ch_info, *tmp;
+       struct ieee80211_channel *channel;
+       struct list_head chan_list;
+       enum rtw89_chan_type type;
+       int list_len, ret;
+       u32 idx;
+
+       INIT_LIST_HEAD(&chan_list);
+
+       for (idx = 0, list_len = 0;
+            idx < nd_config->n_channels && list_len < RTW89_SCAN_LIST_LIMIT;
+            idx++, list_len++) {
+               channel = nd_config->channels[idx];
+               ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
+               if (!ch_info) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ch_info->period = RTW89_CHANNEL_TIME;
+               ch_info->ch_band = rtw89_nl80211_to_hw_band(channel->band);
+               ch_info->central_ch = channel->hw_value;
+               ch_info->pri_ch = channel->hw_value;
+               ch_info->is_psc = cfg80211_channel_is_psc(channel);
+
+               if (channel->flags &
+                   (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR))
+                       type = RTW89_CHAN_DFS;
+               else
+                       type = RTW89_CHAN_ACTIVE;
+
+               rtw89_pno_scan_add_chan_be(rtwdev, type,
+                                          nd_config->n_match_sets, ch_info);
+               list_add_tail(&ch_info->list, &chan_list);
+       }
+
+       ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &chan_list);
+
+out:
+       list_for_each_entry_safe(ch_info, tmp, &chan_list, list) {
+               list_del(&ch_info->list);
+               kfree(ch_info);
+       }
+
+       return ret;
+}
+
 int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
                                   struct rtw89_vif *rtwvif, bool connected)
 {
@@ -6392,7 +6590,7 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
                opt.opch_end = connected ? 0 : RTW89_CHAN_INVALID;
        }
 
-       ret = mac->scan_offload(rtwdev, &opt, rtwvif);
+       ret = mac->scan_offload(rtwdev, &opt, rtwvif, false);
 out:
        return ret;
 }
@@ -6642,6 +6840,57 @@ fail:
        return ret;
 }
 
+int rtw89_fw_h2c_cfg_pno(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+                        bool enable)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config;
+       struct rtw89_h2c_cfg_nlo *h2c;
+       u32 len = sizeof(*h2c);
+       struct sk_buff *skb;
+       int ret, i;
+
+       skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+       if (!skb) {
+               rtw89_err(rtwdev, "failed to alloc skb for nlo\n");
+               return -ENOMEM;
+       }
+
+       skb_put(skb, len);
+       h2c = (struct rtw89_h2c_cfg_nlo *)skb->data;
+
+       h2c->w0 = le32_encode_bits(enable, RTW89_H2C_NLO_W0_ENABLE) |
+                 le32_encode_bits(enable, RTW89_H2C_NLO_W0_IGNORE_CIPHER) |
+                 le32_encode_bits(rtwvif->mac_id, RTW89_H2C_NLO_W0_MACID);
+
+       if (enable) {
+               h2c->nlo_cnt = nd_config->n_match_sets;
+               for (i = 0 ; i < nd_config->n_match_sets; i++) {
+                       h2c->ssid_len[i] = nd_config->match_sets[i].ssid.ssid_len;
+                       memcpy(h2c->ssid[i], nd_config->match_sets[i].ssid.ssid,
+                              nd_config->match_sets[i].ssid.ssid_len);
+               }
+       }
+
+       rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+                             H2C_CAT_MAC,
+                             H2C_CL_MAC_WOW,
+                             H2C_FUNC_NLO, 0, 1,
+                             len);
+
+       ret = rtw89_h2c_tx(rtwdev, skb, false);
+       if (ret) {
+               rtw89_err(rtwdev, "failed to send h2c\n");
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       dev_kfree_skb_any(skb);
+       return ret;
+}
+
 int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
                            bool enable)
 {
index 1bbdf0613ca4eaade5898da1a2b97997947e5b02..d45355e5b2e8bff37907f1642759cee7f91b2936 100644 (file)
@@ -1898,6 +1898,24 @@ struct rtw89_h2c_wow_global {
 #define RTW89_H2C_WOW_GLOBAL_W0_PAIRWISE_SEC_ALGO GENMASK(23, 16)
 #define RTW89_H2C_WOW_GLOBAL_W0_GROUP_SEC_ALGO GENMASK(31, 24)
 
+#define RTW89_MAX_SUPPORT_NL_NUM       16
+struct rtw89_h2c_cfg_nlo {
+       __le32 w0;
+       u8 nlo_cnt;
+       u8 rsvd[3];
+       __le32 patterncheck;
+       __le32 rsvd1;
+       __le32 rsvd2;
+       u8 ssid_len[RTW89_MAX_SUPPORT_NL_NUM];
+       u8 chiper[RTW89_MAX_SUPPORT_NL_NUM];
+       u8 rsvd3[24];
+       u8 ssid[RTW89_MAX_SUPPORT_NL_NUM][IEEE80211_MAX_SSID_LEN];
+} __packed;
+
+#define RTW89_H2C_NLO_W0_ENABLE BIT(0)
+#define RTW89_H2C_NLO_W0_IGNORE_CIPHER BIT(2)
+#define RTW89_H2C_NLO_W0_MACID GENMASK(31, 24)
+
 static inline void RTW89_SET_WOW_WAKEUP_CTRL_PATTERN_MATCH_ENABLE(void *h2c, u32 val)
 {
        le32p_replace_bits((__le32 *)h2c, val, BIT(0));
@@ -2093,6 +2111,10 @@ enum rtw89_scan_mode {
 
 enum rtw89_scan_type {
        RTW89_SCAN_ONCE,
+       RTW89_SCAN_NORMAL,
+       RTW89_SCAN_NORMAL_SLOW,
+       RTW89_SCAN_SEAMLESS,
+       RTW89_SCAN_MAX,
 };
 
 static inline void RTW89_SET_FWCMD_CXHDR_TYPE(void *cmd, u8 val)
@@ -3958,6 +3980,7 @@ enum rtw89_wow_h2c_func {
        H2C_FUNC_WOW_GLOBAL             = 0x2,
        H2C_FUNC_GTK_OFLD               = 0x3,
        H2C_FUNC_ARP_OFLD               = 0x4,
+       H2C_FUNC_NLO                    = 0x7,
        H2C_FUNC_WAKEUP_CTRL            = 0x8,
        H2C_FUNC_WOW_CAM_UPD            = 0xC,
        H2C_FUNC_AOAC_REPORT_REQ        = 0xD,
@@ -4412,12 +4435,14 @@ int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num,
                                   struct list_head *chan_list);
 int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num,
                                      struct list_head *chan_list);
-int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev,
-                             struct rtw89_scan_option *opt,
-                             struct rtw89_vif *vif);
+int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,
+                                struct rtw89_scan_option *opt,
+                                struct rtw89_vif *vif,
+                                bool wowlan);
 int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
                                 struct rtw89_scan_option *opt,
-                                struct rtw89_vif *vif);
+                                struct rtw89_vif *vif,
+                                bool wowlan);
 int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev,
                        struct rtw89_fw_h2c_rf_reg_info *info,
                        u16 len, u8 page);
@@ -4470,10 +4495,14 @@ void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
 int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
                          bool enable);
 void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif);
-int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev,
-                               struct rtw89_vif *rtwvif, bool connected);
+int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
+                                  struct rtw89_vif *rtwvif, bool connected);
+int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
+                                   struct rtw89_vif *rtwvif);
 int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
                                   struct rtw89_vif *rtwvif, bool connected);
+int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
+                                   struct rtw89_vif *rtwvif);
 int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev);
 int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev,
                          const struct rtw89_pkt_drop_params *params);
@@ -4486,6 +4515,8 @@ int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
                            bool enable);
 int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev,
                                 struct rtw89_vif *rtwvif, bool enable);
+int rtw89_fw_h2c_cfg_pno(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+                        bool enable);
 int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
                            bool enable);
 int rtw89_fw_h2c_arp_offload(struct rtw89_dev *rtwdev,
index 149fb1b162394cdaf2b1956efd1eb4055d8e6bb1..89875f59d722b2abdf7e3815e08bc22824b59573 100644 (file)
@@ -6527,8 +6527,9 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {
 
        .is_txq_empty = mac_is_txq_empty_ax,
 
-       .add_chan_list = rtw89_hw_scan_add_chan_list,
-       .scan_offload = rtw89_fw_h2c_scan_offload,
+       .add_chan_list = rtw89_hw_scan_add_chan_list_ax,
+       .add_chan_list_pno = rtw89_pno_scan_add_chan_list_ax,
+       .scan_offload = rtw89_fw_h2c_scan_offload_ax,
 
        .wow_config_mac = rtw89_wow_config_mac_ax,
 };
index 02052fbbec1ad4209d615fd4e7b5254a56d78d7a..9d3be36ffb6eebd8c734f79eb9042cab00b55fc5 100644 (file)
@@ -1006,9 +1006,12 @@ struct rtw89_mac_gen_def {
 
        int (*add_chan_list)(struct rtw89_dev *rtwdev,
                             struct rtw89_vif *rtwvif, bool connected);
+       int (*add_chan_list_pno)(struct rtw89_dev *rtwdev,
+                                struct rtw89_vif *rtwvif);
        int (*scan_offload)(struct rtw89_dev *rtwdev,
                            struct rtw89_scan_option *option,
-                           struct rtw89_vif *rtwvif);
+                           struct rtw89_vif *rtwvif,
+                           bool wowlan);
 
        int (*wow_config_mac)(struct rtw89_dev *rtwdev, bool enable_wow);
 };
index f212b67771d50de6aa7c551eed23018de7367a7c..31f0a5225b115ec9f045474d4644afe1d334d360 100644 (file)
@@ -2599,6 +2599,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {
        .is_txq_empty = mac_is_txq_empty_be,
 
        .add_chan_list = rtw89_hw_scan_add_chan_list_be,
+       .add_chan_list_pno = rtw89_pno_scan_add_chan_list_be,
        .scan_offload = rtw89_fw_h2c_scan_offload_be,
 
        .wow_config_mac = rtw89_wow_config_mac_be,
index fa7fd657105127e8f9cdf168d65efdc6e23e72b5..3bf563998ddf13f4909bb64917ff71e2b4fac242 100644 (file)
@@ -794,17 +794,22 @@ static void rtw89_wow_vif_iter(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvi
        struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
        struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
 
-       /* Current wowlan function support setting of only one STATION vif.
-        * So when one suitable vif is found, stop the iteration.
+       /* Current WoWLAN function support setting of only vif in
+        * infra mode or no link mode. When one suitable vif is found,
+        * stop the iteration.
         */
        if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION)
                return;
 
        switch (rtwvif->net_type) {
        case RTW89_NET_TYPE_INFRA:
-               rtw_wow->wow_vif = vif;
+               if (rtw_wow_has_mgd_features(rtwdev))
+                       rtw_wow->wow_vif = vif;
                break;
        case RTW89_NET_TYPE_NO_LINK:
+               if (rtw_wow->pno_inited)
+                       rtw_wow->wow_vif = vif;
+               break;
        default:
                break;
        }
@@ -1038,6 +1043,23 @@ static void rtw89_wow_clear_wakeups(struct rtw89_dev *rtwdev)
        rtw_wow->wow_vif = NULL;
        rtw89_core_release_all_bits_map(rtw_wow->flags, RTW89_WOW_FLAG_NUM);
        rtw_wow->pattern_cnt = 0;
+       rtw_wow->pno_inited = false;
+}
+
+static void rtw89_wow_init_pno(struct rtw89_dev *rtwdev,
+                              struct cfg80211_sched_scan_request *nd_config)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+
+       if (!nd_config->n_match_sets || !nd_config->n_channels)
+               return;
+
+       rtw_wow->nd_config = nd_config;
+       rtw_wow->pno_inited = true;
+
+       INIT_LIST_HEAD(&rtw_wow->pno_pkt_list);
+
+       rtw89_debug(rtwdev, RTW89_DBG_WOW, "WOW: net-detect is enabled\n");
 }
 
 static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev,
@@ -1050,6 +1072,11 @@ static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev,
                set_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags);
        if (wowlan->magic_pkt)
                set_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags);
+       if (wowlan->n_patterns && wowlan->patterns)
+               set_bit(RTW89_WOW_FLAG_EN_PATTERN, rtw_wow->flags);
+
+       if (wowlan->nd_config)
+               rtw89_wow_init_pno(rtwdev, wowlan->nd_config);
 
        rtw89_for_each_rtwvif(rtwdev, rtwvif)
                rtw89_wow_vif_iter(rtwdev, rtwvif);
@@ -1061,6 +1088,34 @@ static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev,
        return rtw89_wow_parse_patterns(rtwdev, rtwvif, wowlan);
 }
 
+static int rtw89_wow_cfg_wake_pno(struct rtw89_dev *rtwdev, bool wow)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
+       struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv;
+       int ret;
+
+       ret = rtw89_fw_h2c_cfg_pno(rtwdev, rtwvif, true);
+       if (ret) {
+               rtw89_err(rtwdev, "failed to config pno\n");
+               return ret;
+       }
+
+       ret = rtw89_fw_h2c_wow_wakeup_ctrl(rtwdev, rtwvif, wow);
+       if (ret) {
+               rtw89_err(rtwdev, "failed to fw wow wakeup ctrl\n");
+               return ret;
+       }
+
+       ret = rtw89_fw_h2c_wow_global(rtwdev, rtwvif, wow);
+       if (ret) {
+               rtw89_err(rtwdev, "failed to fw wow global\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int rtw89_wow_cfg_wake(struct rtw89_dev *rtwdev, bool wow)
 {
        struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
@@ -1321,100 +1376,236 @@ static int rtw89_wow_disable_trx_post(struct rtw89_dev *rtwdev)
        return ret;
 }
 
-static int rtw89_wow_fw_start(struct rtw89_dev *rtwdev)
+static void rtw89_fw_release_pno_pkt_list(struct rtw89_dev *rtwdev,
+                                         struct rtw89_vif *rtwvif)
 {
        struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
-       struct rtw89_vif *rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv;
+       struct list_head *pkt_list = &rtw_wow->pno_pkt_list;
+       struct rtw89_pktofld_info *info, *tmp;
+
+       list_for_each_entry_safe(info, tmp, pkt_list, list) {
+               rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id);
+               list_del(&info->list);
+               kfree(info);
+       }
+}
+
+static int rtw89_pno_scan_update_probe_req(struct rtw89_dev *rtwdev,
+                                          struct rtw89_vif *rtwvif)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config;
+       u8 num = nd_config->n_match_sets, i;
+       struct rtw89_pktofld_info *info;
+       struct sk_buff *skb;
        int ret;
 
-       rtw89_wow_pattern_write(rtwdev);
-       rtw89_wow_construct_key_info(rtwdev);
+       for (i = 0; i < num; i++) {
+               skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr,
+                                            nd_config->match_sets[i].ssid.ssid,
+                                            nd_config->match_sets[i].ssid.ssid_len,
+                                            nd_config->ie_len);
+               if (!skb)
+                       return -ENOMEM;
+
+               skb_put_data(skb, nd_config->ie, nd_config->ie_len);
+
+               info = kzalloc(sizeof(*info), GFP_KERNEL);
+               if (!info) {
+                       kfree_skb(skb);
+                       rtw89_fw_release_pno_pkt_list(rtwdev, rtwvif);
+                       return -ENOMEM;
+               }
 
-       ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, true);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to enable keep alive\n");
-               return ret;
+               ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &info->id, skb);
+               if (ret) {
+                       kfree_skb(skb);
+                       kfree(info);
+                       rtw89_fw_release_pno_pkt_list(rtwdev, rtwvif);
+                       return ret;
+               }
+
+               list_add_tail(&info->list, &rtw_wow->pno_pkt_list);
+               kfree_skb(skb);
        }
 
-       ret = rtw89_fw_h2c_disconnect_detect(rtwdev, rtwvif, true);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to enable disconnect detect\n");
-               goto out;
+       return 0;
+}
+
+static int rtw89_pno_scan_offload(struct rtw89_dev *rtwdev, bool enable)
+{
+       const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
+       struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv;
+       struct rtw89_scan_option opt = {};
+       int ret;
+
+       if (enable) {
+               ret = rtw89_pno_scan_update_probe_req(rtwdev, rtwvif);
+               if (ret) {
+                       rtw89_err(rtwdev, "Update probe request failed\n");
+                       return ret;
+               }
+
+               ret = mac->add_chan_list_pno(rtwdev, rtwvif);
+               if (ret) {
+                       rtw89_err(rtwdev, "Update channel list failed\n");
+                       return ret;
+               }
        }
 
-       ret = rtw89_fw_h2c_wow_gtk_ofld(rtwdev, rtwvif, true);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to enable GTK offload\n");
-               goto out;
+       opt.enable = enable;
+       opt.repeat = RTW89_SCAN_NORMAL;
+       opt.norm_pd = 10; /* in unit of 100ms */
+       if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) {
+               opt.operation = enable ? RTW89_SCAN_OP_START : RTW89_SCAN_OP_STOP;
+               opt.scan_mode = RTW89_SCAN_MODE_SA;
+               opt.band = RTW89_PHY_0;
+               opt.num_macc_role = 0;
+               opt.mlo_mode = rtwdev->mlo_dbcc_mode;
+               opt.num_opch = 0;
+               opt.opch_end = RTW89_CHAN_INVALID;
        }
 
-       ret = rtw89_fw_h2c_arp_offload(rtwdev, rtwvif, true);
-       if (ret)
-               rtw89_warn(rtwdev, "wow: failed to enable arp offload\n");
+       mac->scan_offload(rtwdev, &opt, rtwvif, true);
 
-       ret = rtw89_wow_cfg_wake(rtwdev, true);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to config wake\n");
-               goto out;
+       return 0;
+}
+
+static int rtw89_wow_fw_start(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
+       struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv;
+       int ret;
+
+       if (rtw89_wow_no_link(rtwdev)) {
+               ret = rtw89_pno_scan_offload(rtwdev, false);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to disable pno scan offload\n");
+                       return ret;
+               }
+
+               ret = rtw89_pno_scan_offload(rtwdev, true);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to enable pno scan offload\n");
+                       return ret;
+               }
+       } else {
+               rtw89_wow_pattern_write(rtwdev);
+               rtw89_wow_construct_key_info(rtwdev);
+
+               ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, true);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to enable keep alive\n");
+                       return ret;
+               }
+
+               ret = rtw89_fw_h2c_disconnect_detect(rtwdev, rtwvif, true);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to enable disconnect detect\n");
+                       return ret;
+               }
+
+               ret = rtw89_fw_h2c_wow_gtk_ofld(rtwdev, rtwvif, true);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to enable GTK offload\n");
+                       return ret;
+               }
+
+               ret = rtw89_fw_h2c_arp_offload(rtwdev, rtwvif, true);
+               if (ret)
+                       rtw89_warn(rtwdev, "wow: failed to enable arp offload\n");
+       }
+
+       if (rtw89_wow_no_link(rtwdev)) {
+               ret = rtw89_wow_cfg_wake_pno(rtwdev, true);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to config wake PNO\n");
+                       return ret;
+               }
+       } else {
+               ret = rtw89_wow_cfg_wake(rtwdev, true);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to config wake\n");
+                       return ret;
+               }
        }
 
        ret = rtw89_wow_check_fw_status(rtwdev, true);
        if (ret) {
                rtw89_err(rtwdev, "wow: failed to check enable fw ready\n");
-               goto out;
+               return ret;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static int rtw89_wow_fw_stop(struct rtw89_dev *rtwdev)
 {
        struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
-       struct rtw89_vif *rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv;
+       struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
+       struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv;
        int ret;
 
-       rtw89_wow_pattern_clear(rtwdev);
+       if (rtw89_wow_no_link(rtwdev)) {
+               ret = rtw89_pno_scan_offload(rtwdev, false);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to disable pno scan offload\n");
+                       return ret;
+               }
 
-       ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, false);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to disable keep alive\n");
-               goto out;
-       }
+               ret = rtw89_fw_h2c_cfg_pno(rtwdev, rtwvif, false);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to disable pno\n");
+                       return ret;
+               }
 
-       ret = rtw89_fw_h2c_disconnect_detect(rtwdev, rtwvif, false);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to disable disconnect detect\n");
-               goto out;
-       }
+               rtw89_fw_release_pno_pkt_list(rtwdev, rtwvif);
+       } else {
+               rtw89_wow_pattern_clear(rtwdev);
 
-       ret = rtw89_fw_h2c_wow_gtk_ofld(rtwdev, rtwvif, false);
-       if (ret) {
-               rtw89_err(rtwdev, "wow: failed to disable GTK offload\n");
-               goto out;
-       }
+               ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, false);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to disable keep alive\n");
+                       return ret;
+               }
 
-       ret = rtw89_fw_h2c_arp_offload(rtwdev, rtwvif, false);
-       if (ret)
-               rtw89_warn(rtwdev, "wow: failed to disable arp offload\n");
+               ret = rtw89_fw_h2c_disconnect_detect(rtwdev, rtwvif, false);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to disable disconnect detect\n");
+                       return ret;
+               }
+
+               ret = rtw89_fw_h2c_wow_gtk_ofld(rtwdev, rtwvif, false);
+               if (ret) {
+                       rtw89_err(rtwdev, "wow: failed to disable GTK offload\n");
+                       return ret;
+               }
+
+               ret = rtw89_fw_h2c_arp_offload(rtwdev, rtwvif, false);
+               if (ret)
+                       rtw89_warn(rtwdev, "wow: failed to disable arp offload\n");
+
+               rtw89_wow_key_clear(rtwdev);
+               rtw89_fw_release_general_pkt_list(rtwdev, true);
+       }
 
-       rtw89_wow_key_clear(rtwdev);
-       rtw89_fw_release_general_pkt_list(rtwdev, true);
 
        ret = rtw89_wow_cfg_wake(rtwdev, false);
        if (ret) {
                rtw89_err(rtwdev, "wow: failed to disable config wake\n");
-               goto out;
+               return ret;
        }
 
        ret = rtw89_wow_check_fw_status(rtwdev, false);
        if (ret) {
                rtw89_err(rtwdev, "wow: failed to check disable fw ready\n");
-               goto out;
+               return ret;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static int rtw89_wow_enable(struct rtw89_dev *rtwdev)
index 5eff3084119b320dac6d758b4402ccafb135bb05..3fbc2b87c058acb430a539aae9879020ab5419a1 100644 (file)
@@ -111,6 +111,13 @@ static inline bool rtw89_wow_no_link(struct rtw89_dev *rtwdev)
        return rtwvif->net_type == RTW89_NET_TYPE_NO_LINK;
 }
 
+static inline bool rtw_wow_has_mgd_features(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+
+       return !bitmap_empty(rtw_wow->flags, RTW89_WOW_FLAG_NUM);
+}
+
 int rtw89_wow_suspend(struct rtw89_dev *rtwdev, struct cfg80211_wowlan *wowlan);
 int rtw89_wow_resume(struct rtw89_dev *rtwdev);
 void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb);