diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 7d6f14420855..5a3aff131472 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2579,6 +2579,21 @@ wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, return rc; } +static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy, + struct net_device *dev, + const bool enabled) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + if (wil->multicast_to_unicast == enabled) + return 0; + + wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled); + wil->multicast_to_unicast = enabled; + + return 0; +} + static const struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, @@ -2615,6 +2630,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .sched_scan_start = wil_cfg80211_sched_scan_start, .sched_scan_stop = wil_cfg80211_sched_scan_stop, .update_ft_ies = wil_cfg80211_update_ft_ies, + .set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast, }; static void wil_wiphy_init(struct wiphy *wiphy) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 8ebc6d59aa74..17118d643d7e 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -1529,6 +1530,35 @@ static struct wil_ring *wil_find_tx_bcast_1(struct wil6210_priv *wil, return v; } +/* apply multicast to unicast only for ARP and IP packets + * (see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info) + */ +static bool wil_check_multicast_to_unicast(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + const struct ethhdr *eth = (void *)skb->data; + const struct vlan_ethhdr *ethvlan = (void *)skb->data; + __be16 ethertype; + + if (!wil->multicast_to_unicast) + return false; + + /* multicast to unicast conversion only for some payload */ + ethertype = eth->h_proto; + if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN) + ethertype = ethvlan->h_vlan_encapsulated_proto; + switch (ethertype) { + case htons(ETH_P_ARP): + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + break; + default: + return false; + } + + return true; +} + static void wil_set_da_for_vring(struct wil6210_priv *wil, struct sk_buff *skb, int vring_index) { @@ -2336,7 +2366,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* in STA mode (ESS), all to same VRING (to AP) */ ring = wil_find_tx_ring_sta(wil, vif, skb); } else if (bcast) { - if (vif->pbss) + if (vif->pbss || wil_check_multicast_to_unicast(wil, skb)) /* in pbss, no bcast VRING - duplicate skb in * all stations VRINGs */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index bf00f0693a3d..21399ced2a5d 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1059,6 +1059,7 @@ struct wil6210_priv { u32 max_agg_wsize; u32 max_ampdu_size; + u8 multicast_to_unicast; }; #define wil_to_wiphy(i) (i->wiphy)