wil6210: support set_multicast_to_unicast cfg80211 operation
authorAhmad Masri <amasri@codeaurora.org>
Wed, 18 Dec 2019 18:10:21 +0000 (20:10 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 19 Dec 2019 16:16:42 +0000 (18:16 +0200)
Wil6210 AP has a separate ring for transmitting multicast packets,
multicast packets are transmitted without an ack from the receiver side.
Therefore, 802.11 spec defines some low MCS rates for multicat packets.
However, there is no guarantee that these packets were really received
and handled on the client side.

Some applications that rely on multicast packets, may prefer to
transmit these packets as a unicast to ensure reliability, and also
to ensure better performance with high MCS rates.
multicast to unicast is done by duplicating multicast packets to all
clients and changing the DA (multicast) to the MAC address of the
client.
see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info.

Signed-off-by: Ahmad Masri <amasri@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h

index 7d6f14420855ecc968a9c91d0c2e4921235e037f..5a3aff13147239298dfb796e7da9b747ed596c0a 100644 (file)
@@ -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)
index 8ebc6d59aa74338178e4696cad5321e1a317bf4e..17118d643d7e180d268633b969ee72cfa8c3b61c 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/moduleparam.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
+#include <linux/if_vlan.h>
 #include <net/ipv6.h>
 #include <linux/prefetch.h>
 
@@ -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
                         */
index bf00f0693a3d7e77b531e6b030446524e746ca89..21399ced2a5d5ea9a3e0c2842c68b10599a168af 100644 (file)
@@ -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)