Skip to content

Commit

Permalink
rtw88: support wowlan feature for 8822c
Browse files Browse the repository at this point in the history
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: Chin-Yen Lee <timlee@realtek.com>
Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
Chin-Yen Lee authored and Kalle Valo committed Jan 26, 2020
1 parent c8e5695 commit 44bc17f
Show file tree
Hide file tree
Showing 13 changed files with 836 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/realtek/rtw88/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/realtek/rtw88/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
86 changes: 86 additions & 0 deletions drivers/net/wireless/realtek/rtw88/fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down
69 changes: 69 additions & 0 deletions drivers/net/wireless/realtek/rtw88/fw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 changes: 44 additions & 0 deletions drivers/net/wireless/realtek/rtw88/mac80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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);
3 changes: 3 additions & 0 deletions drivers/net/wireless/realtek/rtw88/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,9 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)

wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);

#ifdef CONFIG_PM
hw->wiphy->wowlan = rtwdev->chip->wowlan_stub;
#endif
rtw_set_supported_band(hw, rtwdev->chip);
SET_IEEE80211_PERM_ADDR(hw, rtwdev->efuse.addr);

Expand Down
18 changes: 18 additions & 0 deletions drivers/net/wireless/realtek/rtw88/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ enum rtw_flags {
RTW_FLAG_LEISURE_PS_DEEP,
RTW_FLAG_DIG_DISABLE,
RTW_FLAG_BUSY_TRAFFIC,
RTW_FLAG_WOWLAN,

NUM_OF_RTW_FLAGS,
};
Expand Down Expand Up @@ -372,6 +373,15 @@ enum rtw_snr {
RTW_SNR_NUM
};

enum rtw_wow_flags {
RTW_WOW_FLAG_EN_MAGIC_PKT,
RTW_WOW_FLAG_EN_REKEY_PKT,
RTW_WOW_FLAG_EN_DISCONNECT,

/* keep it last */
RTW_WOW_FLAG_MAX,
};

/* the power index is represented by differences, which cck-1s & ht40-1s are
* the base values, so for 1s's differences, there are only ht20 & ofdm
*/
Expand Down Expand Up @@ -907,6 +917,12 @@ struct rtw_intf_phy_para {
u16 platform;
};

struct rtw_wow_param {
struct ieee80211_vif *wow_vif;
DECLARE_BITMAP(flags, RTW_WOW_FLAG_MAX);
u8 txpause;
};

struct rtw_intf_phy_para_table {
struct rtw_intf_phy_para *usb2_para;
struct rtw_intf_phy_para *usb3_para;
Expand Down Expand Up @@ -1036,6 +1052,7 @@ struct rtw_chip_info {
u8 bfer_mu_max_num;

const char *wow_fw_name;
const struct wiphy_wowlan_support *wowlan_stub;

/* coex paras */
u32 coex_para_ver;
Expand Down Expand Up @@ -1589,6 +1606,7 @@ struct rtw_dev {
u8 mp_mode;

struct rtw_fw_state wow_fw;
struct rtw_wow_param wow;

/* hci related data, must be last */
u8 priv[0] __aligned(sizeof(void *));
Expand Down
18 changes: 18 additions & 0 deletions drivers/net/wireless/realtek/rtw88/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,23 @@ static void rtw_pci_phy_cfg(struct rtw_dev *rtwdev)
rtw_pci_link_cfg(rtwdev);
}

#ifdef CONFIG_PM
static int rtw_pci_suspend(struct device *dev)
{
return 0;
}

static int rtw_pci_resume(struct device *dev)
{
return 0;
}

static SIMPLE_DEV_PM_OPS(rtw_pm_ops, rtw_pci_suspend, rtw_pci_resume);
#define RTW_PM_OPS (&rtw_pm_ops)
#else
#define RTW_PM_OPS NULL
#endif

static int rtw_pci_claim(struct rtw_dev *rtwdev, struct pci_dev *pdev)
{
int ret;
Expand Down Expand Up @@ -1508,6 +1525,7 @@ static struct pci_driver rtw_pci_driver = {
.id_table = rtw_pci_id_table,
.probe = rtw_pci_probe,
.remove = rtw_pci_remove,
.driver.pm = RTW_PM_OPS,
};
module_pci_driver(rtw_pci_driver);

Expand Down
Loading

0 comments on commit 44bc17f

Please sign in to comment.