mt76: mt7921: add ipv6 NS offload support
Add ipv6 NS offload for WoWLAN state. Tested in this way: 1. Put device-A into WoW state. 2. ping6 from device-B to device-A. 3. In sniffer, see Neighbour advertisement from device-A. Reviewed-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: Deren Wu <deren.wu@mediatek.com> Signed-off-by: Ming Yen Hsieh <mingyen.hsieh@mediatek.com> Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
@@ -264,6 +264,10 @@ int mt7921_register_device(struct mt7921_dev *dev)
|
||||
INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7921_mac_work);
|
||||
INIT_DELAYED_WORK(&dev->phy.scan_work, mt7921_scan_work);
|
||||
INIT_DELAYED_WORK(&dev->coredump.work, mt7921_coredump_work);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
INIT_WORK(&dev->ipv6_ns_work, mt7921_set_ipv6_ns_work);
|
||||
skb_queue_head_init(&dev->ipv6_ns_list);
|
||||
#endif
|
||||
skb_queue_head_init(&dev->phy.scan_event_list);
|
||||
skb_queue_head_init(&dev->coredump.msg_list);
|
||||
INIT_LIST_HEAD(&dev->sta_poll_list);
|
||||
|
||||
@@ -1726,3 +1726,29 @@ bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update)
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_status_data);
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
void mt7921_set_ipv6_ns_work(struct work_struct *work)
|
||||
{
|
||||
struct mt7921_dev *dev = container_of(work, struct mt7921_dev,
|
||||
ipv6_ns_work);
|
||||
struct sk_buff *skb;
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
skb = skb_dequeue(&dev->ipv6_ns_list);
|
||||
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
mt7921_mutex_acquire(dev);
|
||||
ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
|
||||
MCU_UNI_CMD(OFFLOAD), true);
|
||||
mt7921_mutex_release(dev);
|
||||
|
||||
} while (!ret);
|
||||
|
||||
if (ret)
|
||||
skb_queue_purge(&dev->ipv6_ns_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/ipv6.h>
|
||||
#include "mt7921.h"
|
||||
#include "mcu.h"
|
||||
|
||||
@@ -1332,7 +1333,7 @@ static int mt7921_suspend(struct ieee80211_hw *hw,
|
||||
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
|
||||
ieee80211_iterate_active_interfaces(hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
mt76_connac_mcu_set_suspend_iter,
|
||||
mt7921_mcu_set_suspend_iter,
|
||||
&dev->mphy);
|
||||
|
||||
mt7921_mutex_release(dev);
|
||||
@@ -1407,6 +1408,67 @@ static void mt7921_sta_set_decap_offload(struct ieee80211_hw *hw,
|
||||
MCU_UNI_CMD(STA_REC_UPDATE));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static void mt7921_ipv6_addr_change(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct inet6_dev *idev)
|
||||
{
|
||||
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
||||
struct mt7921_dev *dev = mvif->phy->dev;
|
||||
struct inet6_ifaddr *ifa;
|
||||
struct in6_addr ns_addrs[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
|
||||
struct sk_buff *skb;
|
||||
u8 i, idx = 0;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} __packed hdr;
|
||||
struct mt76_connac_arpns_tlv arpns;
|
||||
} req_hdr = {
|
||||
.hdr = {
|
||||
.bss_idx = mvif->mt76.idx,
|
||||
},
|
||||
.arpns = {
|
||||
.tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND),
|
||||
.mode = 2, /* update */
|
||||
.option = 1, /* update only */
|
||||
},
|
||||
};
|
||||
|
||||
read_lock_bh(&idev->lock);
|
||||
list_for_each_entry(ifa, &idev->addr_list, if_list) {
|
||||
if (ifa->flags & IFA_F_TENTATIVE)
|
||||
continue;
|
||||
ns_addrs[idx] = ifa->addr;
|
||||
if (++idx >= IEEE80211_BSS_ARP_ADDR_LIST_LEN)
|
||||
break;
|
||||
}
|
||||
read_unlock_bh(&idev->lock);
|
||||
|
||||
if (!idx)
|
||||
return;
|
||||
|
||||
skb = __mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req_hdr) +
|
||||
idx * sizeof(struct in6_addr), GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
req_hdr.arpns.ips_num = idx;
|
||||
req_hdr.arpns.len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)
|
||||
+ idx * sizeof(struct in6_addr));
|
||||
skb_put_data(skb, &req_hdr, sizeof(req_hdr));
|
||||
|
||||
for (i = 0; i < idx; i++)
|
||||
skb_put_data(skb, &ns_addrs[i].in6_u, sizeof(struct in6_addr));
|
||||
|
||||
skb_queue_tail(&dev->ipv6_ns_list, skb);
|
||||
|
||||
ieee80211_queue_work(dev->mt76.hw, &dev->ipv6_ns_work);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mt7921_set_sar_specs(struct ieee80211_hw *hw,
|
||||
const struct cfg80211_sar_specs *sar)
|
||||
{
|
||||
@@ -1452,6 +1514,9 @@ const struct ieee80211_ops mt7921_ops = {
|
||||
.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
|
||||
.set_key = mt7921_set_key,
|
||||
.sta_set_decap_offload = mt7921_sta_set_decap_offload,
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
.ipv6_addr_change = mt7921_ipv6_addr_change,
|
||||
#endif /* CONFIG_IPV6 */
|
||||
.ampdu_action = mt7921_ampdu_action,
|
||||
.set_rts_threshold = mt7921_set_rts_threshold,
|
||||
.wake_tx_queue = mt76_wake_tx_queue,
|
||||
|
||||
@@ -224,6 +224,49 @@ exit:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mt7921_mcu_fill_message);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int
|
||||
mt7921_mcu_set_ipv6_ns_filter(struct mt76_dev *dev,
|
||||
struct ieee80211_vif *vif, bool suspend)
|
||||
{
|
||||
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
||||
struct {
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} __packed hdr;
|
||||
struct mt76_connac_arpns_tlv arpns;
|
||||
} req = {
|
||||
.hdr = {
|
||||
.bss_idx = mvif->mt76.idx,
|
||||
},
|
||||
.arpns = {
|
||||
.tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND),
|
||||
.len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)),
|
||||
.mode = suspend,
|
||||
},
|
||||
};
|
||||
|
||||
return mt76_mcu_send_msg(dev, MCU_UNI_CMD_OFFLOAD, &req, sizeof(req),
|
||||
true);
|
||||
}
|
||||
|
||||
void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_IPV6)) {
|
||||
struct mt76_phy *phy = priv;
|
||||
|
||||
mt7921_mcu_set_ipv6_ns_filter(phy->dev, vif,
|
||||
!test_bit(MT76_STATE_RUNNING,
|
||||
&phy->state));
|
||||
}
|
||||
|
||||
mt76_connac_mcu_set_suspend_iter(priv, mac, vif);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static void
|
||||
mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
|
||||
@@ -211,6 +211,10 @@ struct mt7921_dev {
|
||||
struct mt76_connac_pm pm;
|
||||
struct mt76_connac_coredump coredump;
|
||||
const struct mt7921_hif_ops *hif_ops;
|
||||
|
||||
struct work_struct ipv6_ns_work;
|
||||
/* IPv6 addresses for WoWLAN */
|
||||
struct sk_buff_head ipv6_ns_list;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -449,6 +453,10 @@ int mt7921s_mcu_drv_pmctrl(struct mt7921_dev *dev);
|
||||
int mt7921s_mcu_fw_pmctrl(struct mt7921_dev *dev);
|
||||
void mt7921_mac_add_txs(struct mt7921_dev *dev, void *data);
|
||||
void mt7921_set_runtime_pm(struct mt7921_dev *dev);
|
||||
void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac,
|
||||
struct ieee80211_vif *vif);
|
||||
void mt7921_set_ipv6_ns_work(struct work_struct *work);
|
||||
|
||||
int mt7921_mcu_set_sniffer(struct mt7921_dev *dev, struct ieee80211_vif *vif,
|
||||
bool enable);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user