wil6210: fall back to pseudo-DMS mcast for secure link and PBSS
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Sun, 15 Mar 2015 14:00:24 +0000 (16:00 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Fri, 20 Mar 2015 06:33:24 +0000 (08:33 +0200)
Multicast is not yet properly supported for all connection types;
actually, only non-secure AP is supported. For all other cases,
fall back to old "pseudo-DMS" approach. Namely, for:
- PBSS
- secure connection

When re-routing MCAST Rx->Tx on the AP, do not Tx back to the origin
of the frame

Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/wil6210/txrx.c

index 1fe390f6d7d7f3d340b28ac71b2aef9b3e5ae7ef..e8bd512d81a9b7bbd70b7d56e65e0133e39881b0 100644 (file)
@@ -908,8 +908,20 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
        return NULL;
 }
 
-static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil,
-                                      struct sk_buff *skb)
+/* Use one of 2 strategies:
+ *
+ * 1. New (real broadcast):
+ *    use dedicated broadcast vring
+ * 2. Old (pseudo-DMS):
+ *    Find 1-st vring and return it;
+ *    duplicate skb and send it to other active vrings;
+ *    in all cases override dest address to unicast peer's address
+ * Use old strategy when new is not supported yet:
+ *  - for PBSS
+ *  - for secure link
+ */
+static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil,
+                                        struct sk_buff *skb)
 {
        struct vring *v;
        int i = wil->bcast_vring;
@@ -923,6 +935,93 @@ static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil,
        return v;
 }
 
+static void wil_set_da_for_vring(struct wil6210_priv *wil,
+                                struct sk_buff *skb, int vring_index)
+{
+       struct ethhdr *eth = (void *)skb->data;
+       int cid = wil->vring2cid_tid[vring_index][0];
+
+       ether_addr_copy(eth->h_dest, wil->sta[cid].addr);
+}
+
+static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil,
+                                        struct sk_buff *skb)
+{
+       struct vring *v, *v2;
+       struct sk_buff *skb2;
+       int i;
+       u8 cid;
+       struct ethhdr *eth = (void *)skb->data;
+       char *src = eth->h_source;
+
+       /* find 1-st vring eligible for data */
+       for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+               v = &wil->vring_tx[i];
+               if (!v->va)
+                       continue;
+
+               cid = wil->vring2cid_tid[i][0];
+               if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+                       continue;
+               if (!wil->sta[cid].data_port_open)
+                       continue;
+
+               /* don't Tx back to source when re-routing Rx->Tx at the AP */
+               if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN))
+                       continue;
+
+               goto found;
+       }
+
+       wil_dbg_txrx(wil, "Tx while no vrings active?\n");
+
+       return NULL;
+
+found:
+       wil_dbg_txrx(wil, "BCAST -> ring %d\n", i);
+       wil_set_da_for_vring(wil, skb, i);
+
+       /* find other active vrings and duplicate skb for each */
+       for (i++; i < WIL6210_MAX_TX_RINGS; i++) {
+               v2 = &wil->vring_tx[i];
+               if (!v2->va)
+                       continue;
+               cid = wil->vring2cid_tid[i][0];
+               if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+                       continue;
+               if (!wil->sta[cid].data_port_open)
+                       continue;
+
+               if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN))
+                       continue;
+
+               skb2 = skb_copy(skb, GFP_ATOMIC);
+               if (skb2) {
+                       wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
+                       wil_set_da_for_vring(wil, skb2, i);
+                       wil_tx_vring(wil, v2, skb2);
+               } else {
+                       wil_err(wil, "skb_copy failed\n");
+               }
+       }
+
+       return v;
+}
+
+static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil,
+                                      struct sk_buff *skb)
+{
+       struct wireless_dev *wdev = wil->wdev;
+
+       if (wdev->iftype != NL80211_IFTYPE_AP)
+               return wil_find_tx_bcast_2(wil, skb);
+
+       if (wil->privacy)
+               return wil_find_tx_bcast_2(wil, skb);
+
+       return wil_find_tx_bcast_1(wil, skb);
+}
+
 static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
                           int vring_index)
 {