Driver offload template frames to firmware as helpers to exchange keys and
ARP response by firmware.
First two patches are to prepare flows required by WoWLAN. Patches 5/12
and 6/12 are to access H2C command via struct instead of macros, which
don't change logic at all. That last one is to support ARP response by
firmware. Others are to maintain variant keys for variant ciphers between
mac80211/driver/firmware.
Chih-Kang Chang (10):
wifi: rtw89: wow: refine WoWLAN flows of HCI interrupts and low power
mode
wifi: rtw89: wow: parsing Auth Key Management from associate request
wifi: rtw89: wow: prepare PTK GTK info from mac80211
wifi: rtw89: use struct to access firmware command h2c_dctl_sec_cam_v1
wifi: rtw89: use struct to fill H2C of WoWLAN global configuration
wifi: rtw89: wow: construct EAPoL packet for GTK rekey offload
wifi: rtw89: wow: add GTK rekey feature related H2C commands
wifi: rtw89: wow: update latest PTK GTK info to mac80211 after resume
wifi: rtw89: wow: support 802.11w PMF IGTK rekey
wifi: rtw89: wow: support WEP cipher on WoWLAN
Chin-Yen Lee (2):
wifi: rtw89: wow: send RFK pre-nofity H2C command in WoWLAN mode
wifi: rtw89: wow: add ARP offload feature
drivers/net/wireless/realtek/rtw89/cam.c | 120 ++-
drivers/net/wireless/realtek/rtw89/cam.h | 71 +-
drivers/net/wireless/realtek/rtw89/core.c | 6 +-
drivers/net/wireless/realtek/rtw89/core.h | 51 ++
drivers/net/wireless/realtek/rtw89/fw.c | 334 +++++++-
drivers/net/wireless/realtek/rtw89/fw.h | 466 +++---------
drivers/net/wireless/realtek/rtw89/mac.c | 43 ++
drivers/net/wireless/realtek/rtw89/mac.h | 7 +
drivers/net/wireless/realtek/rtw89/mac80211.c | 26 +
drivers/net/wireless/realtek/rtw89/ps.c | 3 +-
drivers/net/wireless/realtek/rtw89/wow.c | 716 +++++++++++++++++-
drivers/net/wireless/realtek/rtw89/wow.h | 50 ++
12 files changed, 1501 insertions(+), 392 deletions(-)
--
2.25.1
From: Chih-Kang Chang <[email protected]>
After enabling packet offload, the TX will be stuck after resume from
WoWLAN mode. And the 8852c gets error messages like
rtw89_8852ce 0000:04:00.0: No busy txwd pages available
rtw89_8852ce 0000:04:00.0: queue 0 txwd 100 is not idle
rtw89_8852ce 0000:04:00.0: queue 0 txwd 101 is not idle
rtw89_8852ce 0000:04:00.0: queue 0 txwd 102 is not idle
rtw89_8852ce 0000:04:00.0: queue 0 txwd 103 is not idle
If suspend/resume many times that firmware will download failed and
disconnection.
To fix these issues, We removed the rtw89_hci_disable_intr() and
rtw89_hci_enable_intr() during rtw89_wow_swap_fw() to prevent add packet
offload can't receive c2h back due to interrupt disable. Only 8852C and
8922A needs to disable interrupt before downloading fw.
Furthermore, we avoid using low power HCI mode on WoWLAN mode, to prevent
interrupt enabled, then get interrupt and calculate RXBD mismatched due to
software RXBD index already reset but hardware RXBD index not yet.
Fixes: 5c12bb66b79d ("wifi: rtw89: refine packet offload flow")
Signed-off-by: Chih-Kang Chang <[email protected]>
Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/ps.c | 3 ++-
drivers/net/wireless/realtek/rtw89/wow.c | 12 ++++++++++--
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c
index 31290d8cb7f7..92074b73ebeb 100644
--- a/drivers/net/wireless/realtek/rtw89/ps.c
+++ b/drivers/net/wireless/realtek/rtw89/ps.c
@@ -55,7 +55,8 @@ static void rtw89_ps_power_mode_change_with_hci(struct rtw89_dev *rtwdev,
static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
{
- if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode))
+ if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode) &&
+ !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags))
rtw89_ps_power_mode_change_with_hci(rtwdev, enter);
else
rtw89_mac_power_mode_change(rtwdev, enter);
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index f7e96fcbbaba..ea555f29442d 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -458,14 +458,17 @@ static int rtw89_wow_swap_fw(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;
+ enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
const struct rtw89_chip_info *chip = rtwdev->chip;
bool include_bb = !!chip->bbmcu_nr;
+ bool disable_intr_for_dlfw = false;
struct ieee80211_sta *wow_sta;
struct rtw89_sta *rtwsta = NULL;
bool is_conn = true;
int ret;
- rtw89_hci_disable_intr(rtwdev);
+ if (chip_id == RTL8852C || chip_id == RTL8922A)
+ disable_intr_for_dlfw = true;
wow_sta = ieee80211_find_sta(wow_vif, rtwvif->bssid);
if (wow_sta)
@@ -473,12 +476,18 @@ static int rtw89_wow_swap_fw(struct rtw89_dev *rtwdev, bool wow)
else
is_conn = false;
+ if (disable_intr_for_dlfw)
+ rtw89_hci_disable_intr(rtwdev);
+
ret = rtw89_fw_download(rtwdev, fw_type, include_bb);
if (ret) {
rtw89_warn(rtwdev, "download fw failed\n");
return ret;
}
+ if (disable_intr_for_dlfw)
+ rtw89_hci_enable_intr(rtwdev);
+
rtw89_phy_init_rf_reg(rtwdev, true);
ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, rtwsta,
@@ -524,7 +533,6 @@ static int rtw89_wow_swap_fw(struct rtw89_dev *rtwdev, bool wow)
rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, RTW89_PHY_0, 5);
rtw89_mac_hw_mgnt_sec(rtwdev, wow);
- rtw89_hci_enable_intr(rtwdev);
return 0;
}
--
2.25.1
From: Chin-Yen Lee <[email protected]>
Add H2C command and offload template packet to allow firmware send ARP
response in WoWLAN mode. Then, firmware in WoWLAN mode can interactive
with peer that issue ARP request to query MAC address.
Signed-off-by: Chin-Yen Lee <[email protected]>
Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/core.h | 1 +
drivers/net/wireless/realtek/rtw89/fw.c | 108 ++++++++++++++++++
drivers/net/wireless/realtek/rtw89/fw.h | 14 +++
drivers/net/wireless/realtek/rtw89/mac80211.c | 3 +
drivers/net/wireless/realtek/rtw89/wow.c | 8 ++
5 files changed, 134 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 1aa25a3f3659..e03645bb27b7 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -3341,6 +3341,7 @@ struct rtw89_vif {
u8 port;
u8 mac_addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
+ __be32 ip_addr;
u8 phy_idx;
u8 mac_idx;
u8 net_type;
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 444badc3eede..044a5b90c7f4 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -2,6 +2,7 @@
/* Copyright(c) 2019-2020 Realtek Corporation
*/
+#include <linux/if_arp.h>
#include "cam.h"
#include "chan.h"
#include "coex.h"
@@ -26,6 +27,17 @@ struct rtw89_sa_query {
u8 action;
} __packed __aligned(2);
+struct rtw89_arp_rsp {
+ struct ieee80211_hdr_3addr addr;
+ u8 llc_hdr[sizeof(rfc1042_header)];
+ __be16 llc_type;
+ struct arphdr arp_hdr;
+ u8 sender_hw[ETH_ALEN];
+ __be32 sender_ip;
+ u8 target_hw[ETH_ALEN];
+ __be32 target_ip;
+} __packed __aligned(2);
+
static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C};
union rtw89_fw_element_arg {
@@ -2223,6 +2235,48 @@ static struct sk_buff *rtw89_sa_query_get(struct rtw89_dev *rtwdev,
return skb;
}
+static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+ struct rtw89_arp_rsp *arp_skb;
+ struct arphdr *arp_hdr;
+ struct sk_buff *skb;
+ __le16 fc;
+
+ skb = dev_alloc_skb(sizeof(struct rtw89_arp_rsp));
+ if (!skb)
+ return NULL;
+
+ arp_skb = skb_put_zero(skb, sizeof(*arp_skb));
+
+ if (rtw_wow->ptk_alg)
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS |
+ IEEE80211_FCTL_PROTECTED);
+ else
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS);
+
+ arp_skb->addr.frame_control = fc;
+ ether_addr_copy(arp_skb->addr.addr1, rtwvif->bssid);
+ ether_addr_copy(arp_skb->addr.addr2, rtwvif->mac_addr);
+ ether_addr_copy(arp_skb->addr.addr3, rtwvif->bssid);
+
+ memcpy(arp_skb->llc_hdr, rfc1042_header, sizeof(rfc1042_header));
+ arp_skb->llc_type = htons(ETH_P_ARP);
+
+ arp_hdr = &arp_skb->arp_hdr;
+ arp_hdr->ar_hrd = htons(ARPHRD_ETHER);
+ arp_hdr->ar_pro = htons(ETH_P_IP);
+ arp_hdr->ar_hln = ETH_ALEN;
+ arp_hdr->ar_pln = 4;
+ arp_hdr->ar_op = htons(ARPOP_REPLY);
+
+ ether_addr_copy(arp_skb->sender_hw, rtwvif->mac_addr);
+ arp_skb->sender_ip = rtwvif->ip_addr;
+
+ return skb;
+}
+
static int rtw89_fw_h2c_add_general_pkt(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif,
enum rtw89_fw_pkt_ofld_type type,
@@ -2256,6 +2310,9 @@ static int rtw89_fw_h2c_add_general_pkt(struct rtw89_dev *rtwdev,
case RTW89_PKT_OFLD_TYPE_SA_QUERY:
skb = rtw89_sa_query_get(rtwdev, rtwvif);
break;
+ case RTW89_PKT_OFLD_TYPE_ARP_RSP:
+ skb = rtw89_arp_response_get(rtwdev, rtwvif);
+ break;
default:
goto err;
}
@@ -6390,6 +6447,57 @@ int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
return ret;
}
+int rtw89_fw_h2c_arp_offload(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ bool enable)
+{
+ struct rtw89_h2c_arp_offload *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ u8 pkt_id = 0;
+ int ret;
+
+ if (enable) {
+ ret = rtw89_fw_h2c_add_general_pkt(rtwdev, rtwvif,
+ RTW89_PKT_OFLD_TYPE_ARP_RSP,
+ &pkt_id);
+ if (ret)
+ return ret;
+ }
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for arp offload\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_arp_offload *)skb->data;
+
+ h2c->w0 = le32_encode_bits(enable, RTW89_H2C_ARP_OFFLOAD_W0_ENABLE) |
+ le32_encode_bits(0, RTW89_H2C_ARP_OFFLOAD_W0_ACTION) |
+ le32_encode_bits(rtwvif->mac_id, RTW89_H2C_ARP_OFFLOAD_W0_MACID) |
+ le32_encode_bits(pkt_id, RTW89_H2C_ARP_OFFLOAD_W0_PKT_ID);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MAC_WOW,
+ H2C_FUNC_ARP_OFLD, 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;
+}
+
#define H2C_DISCONNECT_DETECT_LEN 8
int rtw89_fw_h2c_disconnect_detect(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif, bool enable)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 5e51267558ad..4151c9d566bd 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2025,6 +2025,17 @@ struct rtw89_h2c_wow_gtk_ofld {
#define RTW89_H2C_WOW_GTK_OFLD_W1_PMF_BIP_SEC_ALGO GENMASK(9, 8)
#define RTW89_H2C_WOW_GTK_OFLD_W1_ALGO_AKM_SUIT GENMASK(17, 10)
+struct rtw89_h2c_arp_offload {
+ __le32 w0;
+ __le32 w1;
+} __packed;
+
+#define RTW89_H2C_ARP_OFFLOAD_W0_ENABLE BIT(0)
+#define RTW89_H2C_ARP_OFFLOAD_W0_ACTION BIT(1)
+#define RTW89_H2C_ARP_OFFLOAD_W0_MACID GENMASK(23, 16)
+#define RTW89_H2C_ARP_OFFLOAD_W0_PKT_ID GENMASK(31, 24)
+#define RTW89_H2C_ARP_OFFLOAD_W1_CONTENT GENMASK(31, 0)
+
enum rtw89_btc_btf_h2c_class {
BTFC_SET = 0x10,
BTFC_GET = 0x11,
@@ -3919,6 +3930,7 @@ enum rtw89_wow_h2c_func {
H2C_FUNC_DISCONNECT_DETECT = 0x1,
H2C_FUNC_WOW_GLOBAL = 0x2,
H2C_FUNC_GTK_OFLD = 0x3,
+ H2C_FUNC_ARP_OFLD = 0x4,
H2C_FUNC_WAKEUP_CTRL = 0x8,
H2C_FUNC_WOW_CAM_UPD = 0xC,
H2C_FUNC_AOAC_REPORT_REQ = 0xD,
@@ -4436,6 +4448,8 @@ int rtw89_fw_h2c_wow_wakeup_ctrl(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,
+ struct rtw89_vif *rtwvif, bool enable);
int rtw89_fw_h2c_disconnect_detect(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif, bool enable);
int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 2fd23f79544d..ca4938eed23c 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -473,6 +473,9 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_PS)
rtw89_recalc_lps(rtwdev);
+ if (changed & BSS_CHANGED_ARP_FILTER)
+ rtwvif->ip_addr = vif->cfg.arp_addr_list[0];
+
mutex_unlock(&rtwdev->mutex);
}
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index 434b29ae8ae9..fa61484c3839 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -1320,6 +1320,10 @@ static int rtw89_wow_fw_start(struct rtw89_dev *rtwdev)
goto out;
}
+ ret = rtw89_fw_h2c_arp_offload(rtwdev, rtwvif, true);
+ if (ret)
+ rtw89_warn(rtwdev, "wow: failed to enable arp offload\n");
+
ret = rtw89_wow_cfg_wake(rtwdev, true);
if (ret) {
rtw89_err(rtwdev, "wow: failed to config wake\n");
@@ -1362,6 +1366,10 @@ static int rtw89_wow_fw_stop(struct rtw89_dev *rtwdev)
goto out;
}
+ 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);
--
2.25.1