Commit c0bc1bce authored by Chin-Yen Lee's avatar Chin-Yen Lee Committed by Ping-Ke Shih
Browse files

wifi: rtw89: wow: add WoWLAN net-detect support



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: default avatarChin-Yen Lee <timlee@realtek.com>
Signed-off-by: default avatarPing-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240805090028.27768-4-pkshih@realtek.com
parent 0f683c2c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+6 −0
Original line number Diff line number Diff line
@@ -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 {
+263 −14
Original line number Diff line number Diff line
@@ -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,
int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,
				 struct rtw89_scan_option *option,
			      struct rtw89_vif *rtwvif)
				 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,6 +4935,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,

	memset(probe_id, RTW89_SCANOFLD_PKT_NONE, sizeof(probe_id));

	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 */
@@ -4937,6 +4943,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
				break;
			}
		}
	}

	h2c->w0 = le32_encode_bits(option->operation, RTW89_H2C_SCANOFLD_BE_W0_OP) |
		  le32_encode_bits(option->scan_mode,
@@ -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 @@ static int rtw89_update_6ghz_rnr_chan(struct rtw89_dev *rtwdev,
	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,7 +6202,57 @@ 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,
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;
@@ -6183,6 +6329,58 @@ int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev,
	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 @@ int rtw89_fw_h2c_disconnect_detect(struct rtw89_dev *rtwdev,
	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)
{
+37 −6
Original line number Diff line number Diff line
@@ -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,
int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,
				 struct rtw89_scan_option *opt,
			      struct rtw89_vif *vif);
				 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,
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,
+3 −2
Original line number Diff line number Diff line
@@ -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,
};
Loading