Commit 44bc17f7 authored by Chin-Yen Lee's avatar Chin-Yen Lee Committed by Kalle Valo
Browse files

rtw88: support wowlan feature for 8822c



Wake on WLAN(wowlan) is a feature which allows devices
to be woken up from suspend state through wlan events.

When user enables wowlan feature and then let the device
enter suspend state, wowlan firmware will be loaded by
the driver and periodically monitors wifi packets.
Power consumption of wifi chip will be reduced in this
state.

If wowlan firmware detects that specific wlan event
happens, it will issue wakeup signal to trigger resume
process. Driver will load normal firmware and let wifi
chip return to the original state.

Currently supported wlan events include receiving magic packet,
rekey packet and deauth packet, and disconnecting from AP.

Signed-off-by: default avatarChin-Yen Lee <timlee@realtek.com>
Signed-off-by: default avatarYan-Hsuan Chuang <yhchuang@realtek.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent c8e5695e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ rtw88-y += main.o \
	   ps.o \
	   sec.o \
	   bf.o \
	   wow.o \
	   regd.o

rtw88-$(CONFIG_RTW88_8822BE)	+= rtw8822b.o rtw8822b_table.o
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ enum rtw_debug_mask {
	RTW_DBG_DEBUGFS		= 0x00000200,
	RTW_DBG_PS		= 0x00000400,
	RTW_DBG_BF		= 0x00000800,
	RTW_DBG_WOW		= 0x00001000,

	RTW_DBG_ALL		= 0xffffffff
};
+86 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include "sec.h"
#include "debug.h"
#include "util.h"
#include "wow.h"

static void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev,
				      struct sk_buff *skb)
@@ -482,6 +483,91 @@ void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev)
	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}

void rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable)
{
	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
	struct rtw_fw_wow_keep_alive_para mode = {
		.adopt = true,
		.pkt_type = KEEP_ALIVE_NULL_PKT,
		.period = 5,
	};

	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_KEEP_ALIVE);
	SET_KEEP_ALIVE_ENABLE(h2c_pkt, enable);
	SET_KEEP_ALIVE_ADOPT(h2c_pkt, mode.adopt);
	SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, mode.pkt_type);
	SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, mode.period);

	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}

void rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable)
{
	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
	struct rtw_fw_wow_disconnect_para mode = {
		.adopt = true,
		.period = 30,
		.retry_count = 5,
	};

	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_DISCONNECT_DECISION);

	if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) {
		SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, enable);
		SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, mode.adopt);
		SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, mode.period);
		SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, mode.retry_count);
	}

	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}

void rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable)
{
	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
	u8 h2c_pkt[H2C_PKT_SIZE] = {0};

	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WOWLAN);

	SET_WOWLAN_FUNC_ENABLE(h2c_pkt, enable);
	if (rtw_wow_mgd_linked(rtwdev)) {
		if (test_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags))
			SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, enable);
		if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags))
			SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, enable);
		if (test_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags))
			SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, enable);
	}

	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}

void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev,
				     u8 pairwise_key_enc,
				     u8 group_key_enc)
{
	u8 h2c_pkt[H2C_PKT_SIZE] = {0};

	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_AOAC_GLOBAL_INFO);

	SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, pairwise_key_enc);
	SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, group_key_enc);

	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}

void rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable)
{
	u8 h2c_pkt[H2C_PKT_SIZE] = {0};

	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_REMOTE_WAKE_CTRL);

	SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, enable);

	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}

static u8 rtw_get_rsvd_page_location(struct rtw_dev *rtwdev,
				     enum rtw_rsvd_packet_type type)
{
+69 −0
Original line number Diff line number Diff line
@@ -100,6 +100,23 @@ struct rtw_rsvd_page {
	bool add_txdesc;
};

enum rtw_keep_alive_pkt_type {
	KEEP_ALIVE_NULL_PKT = 0,
	KEEP_ALIVE_ARP_RSP = 1,
};

struct rtw_fw_wow_keep_alive_para {
	bool adopt;
	u8 pkt_type;
	u8 period;		/* unit: sec */
};

struct rtw_fw_wow_disconnect_para {
	bool adopt;
	u8 period;		/* unit: sec */
	u8 retry_count;
};

struct rtw_fw_hdr {
	__le16 signature;
	u8 category;
@@ -198,6 +215,11 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define H2C_CMD_QUERY_BT_MP_INFO	0x67
#define H2C_CMD_BT_WIFI_CONTROL		0x69

#define H2C_CMD_KEEP_ALIVE		0x03
#define H2C_CMD_DISCONNECT_DECISION	0x04
#define H2C_CMD_WOWLAN			0x80
#define H2C_CMD_REMOTE_WAKE_CTRL	0x81
#define H2C_CMD_AOAC_GLOBAL_INFO	0x82
#define SET_H2C_CMD_ID_CLASS(h2c_pkt, value)				       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(7, 0))

@@ -301,6 +323,45 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, value)                              \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(23, 16))

#define SET_KEEP_ALIVE_ENABLE(h2c_pkt, value)				       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
#define SET_KEEP_ALIVE_ADOPT(h2c_pkt, value)				       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(9))
#define SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, value)				       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(10))
#define SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16))

#define SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
#define SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(9))
#define SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, value)		       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16))
#define SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, value)		       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24))

#define SET_WOWLAN_FUNC_ENABLE(h2c_pkt, value)				       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
#define SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(10))
#define SET_WOWLAN_UNICAST_PKT_ENABLE(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(11))
#define SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(14))
#define SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(15))

#define SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, value)			       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
#define SET_REMOTE_WAKE_CTRL_NLO_OFFLOAD_EN(h2c_pkt, value)		       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(12))

#define SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, value)		       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8))
#define SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, value)		       \
	le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16))

static inline struct rtw_c2h_cmd *get_c2h_from_skb(struct sk_buff *skb)
{
	u32 pkt_offset;
@@ -340,4 +401,12 @@ int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev,
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);
void rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable);
void rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable);
void rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable);
void rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable);
void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev,
				     u8 pairwise_key_enc,
				     u8 group_key_enc);

#endif
+44 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "reg.h"
#include "bf.h"
#include "debug.h"
#include "wow.h"

static void rtw_ops_tx(struct ieee80211_hw *hw,
		       struct ieee80211_tx_control *control,
@@ -735,6 +736,44 @@ static int rtw_ops_set_bitrate_mask(struct ieee80211_hw *hw,
	return 0;
}

#ifdef CONFIG_PM
static int rtw_ops_suspend(struct ieee80211_hw *hw,
			   struct cfg80211_wowlan *wowlan)
{
	struct rtw_dev *rtwdev = hw->priv;
	int ret;

	mutex_lock(&rtwdev->mutex);
	ret = rtw_wow_suspend(rtwdev, wowlan);
	if (ret)
		rtw_err(rtwdev, "failed to suspend for wow %d\n", ret);
	mutex_unlock(&rtwdev->mutex);

	return ret ? 1 : 0;
}

static int rtw_ops_resume(struct ieee80211_hw *hw)
{
	struct rtw_dev *rtwdev = hw->priv;
	int ret;

	mutex_lock(&rtwdev->mutex);
	ret = rtw_wow_resume(rtwdev);
	if (ret)
		rtw_err(rtwdev, "failed to resume for wow %d\n", ret);
	mutex_unlock(&rtwdev->mutex);

	return ret ? 1 : 0;
}

static void rtw_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled)
{
	struct rtw_dev *rtwdev = hw->priv;

	device_set_wakeup_enable(rtwdev->dev, enabled);
}
#endif

const struct ieee80211_ops rtw_ops = {
	.tx			= rtw_ops_tx,
	.wake_tx_queue		= rtw_ops_wake_tx_queue,
@@ -757,5 +796,10 @@ const struct ieee80211_ops rtw_ops = {
	.sta_statistics		= rtw_ops_sta_statistics,
	.flush			= rtw_ops_flush,
	.set_bitrate_mask	= rtw_ops_set_bitrate_mask,
#ifdef CONFIG_PM
	.suspend		= rtw_ops_suspend,
	.resume			= rtw_ops_resume,
	.set_wakeup		= rtw_ops_set_wakeup,
#endif
};
EXPORT_SYMBOL(rtw_ops);
Loading