wil6210: implement broadcast/multicast data
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Sun, 15 Mar 2015 14:00:23 +0000 (16:00 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Fri, 20 Mar 2015 06:33:23 +0000 (08:33 +0200)
Use dedicated vring for multicast frames; this vring allocated for
AP and PBSS (both P2P GO and client) configurations

For short frames, use MCS0; for long - MCS1

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

index c9f362c..8f7596f 100644 (file)
@@ -783,8 +783,17 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
                           channel->hw_value);
        if (rc)
-               netif_carrier_off(ndev);
+               goto err_pcp_start;
 
+       rc = wil_bcast_init(wil);
+       if (rc)
+               goto err_bcast;
+
+       goto out; /* success */
+err_bcast:
+       wmi_pcp_stop(wil);
+err_pcp_start:
+       netif_carrier_off(ndev);
 out:
        mutex_unlock(&wil->mutex);
        return rc;
index a42cb89..bbc22d8 100644 (file)
@@ -121,12 +121,18 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
 
                        snprintf(name, sizeof(name), "tx_%2d", i);
 
-                       seq_printf(s,
-                               "\n%pM CID %d TID %d BACK([%d] %d TU A%s) [%3d|%3d] idle %s\n",
-                               wil->sta[cid].addr, cid, tid,
-                               txdata->agg_wsize, txdata->agg_timeout,
-                               txdata->agg_amsdu ? "+" : "-",
-                               used, avail, sidle);
+                       if (cid < WIL6210_MAX_CID)
+                               seq_printf(s,
+                                          "\n%pM CID %d TID %d BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n",
+                                          wil->sta[cid].addr, cid, tid,
+                                          txdata->agg_wsize,
+                                          txdata->agg_timeout,
+                                          txdata->agg_amsdu ? "+" : "-",
+                                          used, avail, sidle);
+                       else
+                               seq_printf(s,
+                                          "\nBroadcast [%3d|%3d] idle %s\n",
+                                          used, avail, sidle);
 
                        wil_print_vring(s, wil, name, vring, '_', 'H');
                }
index a5fd605..c2a2384 100644 (file)
@@ -68,6 +68,7 @@ MODULE_PARM_DESC(mtu_max, " Max MTU value.");
 
 static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
 static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
 
 static int ring_order_set(const char *val, const struct kernel_param *kp)
 {
@@ -216,6 +217,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
+               wil_bcast_fini(wil);
                netif_tx_stop_all_queues(ndev);
                netif_carrier_off(ndev);
 
@@ -360,6 +362,35 @@ static int wil_find_free_vring(struct wil6210_priv *wil)
        return -EINVAL;
 }
 
+int wil_bcast_init(struct wil6210_priv *wil)
+{
+       int ri = wil->bcast_vring, rc;
+
+       if ((ri >= 0) && wil->vring_tx[ri].va)
+               return 0;
+
+       ri = wil_find_free_vring(wil);
+       if (ri < 0)
+               return ri;
+
+       rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
+       if (rc == 0)
+               wil->bcast_vring = ri;
+
+       return rc;
+}
+
+void wil_bcast_fini(struct wil6210_priv *wil)
+{
+       int ri = wil->bcast_vring;
+
+       if (ri < 0)
+               return;
+
+       wil->bcast_vring = -1;
+       wil_vring_fini_tx(wil, ri);
+}
+
 static void wil_connect_worker(struct work_struct *work)
 {
        int rc;
@@ -407,6 +438,7 @@ int wil_priv_init(struct wil6210_priv *wil)
        init_completion(&wil->wmi_call);
 
        wil->pending_connect_cid = -1;
+       wil->bcast_vring = -1;
        setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
        setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
 
@@ -656,6 +688,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
+       wil_bcast_fini(wil);
 
        /* prevent NAPI from being scheduled */
        bitmap_zero(wil->status, wil_status_last);
index 2453470..1fe390f 100644 (file)
@@ -523,7 +523,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
        struct wireless_dev *wdev = wil_to_wdev(wil);
        unsigned int len = skb->len;
        struct vring_rx_desc *d = wil_skb_rxdesc(skb);
-       int cid = wil_rxdesc_cid(d);
+       int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */
        struct ethhdr *eth = (void *)skb->data;
        /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication
         * is not suitable, need to look at data
@@ -749,6 +749,72 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
        return rc;
 }
 
+int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
+{
+       int rc;
+       struct wmi_bcast_vring_cfg_cmd cmd = {
+               .action = cpu_to_le32(WMI_VRING_CMD_ADD),
+               .vring_cfg = {
+                       .tx_sw_ring = {
+                               .max_mpdu_size =
+                                       cpu_to_le16(wil_mtu2macbuf(mtu_max)),
+                               .ring_size = cpu_to_le16(size),
+                       },
+                       .ringid = id,
+                       .encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
+               },
+       };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_vring_cfg_done_event cmd;
+       } __packed reply;
+       struct vring *vring = &wil->vring_tx[id];
+       struct vring_tx_data *txdata = &wil->vring_tx_data[id];
+
+       wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
+                    cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+
+       if (vring->va) {
+               wil_err(wil, "Tx ring [%d] already allocated\n", id);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       memset(txdata, 0, sizeof(*txdata));
+       spin_lock_init(&txdata->lock);
+       vring->size = size;
+       rc = wil_vring_alloc(wil, vring);
+       if (rc)
+               goto out;
+
+       wil->vring2cid_tid[id][0] = WIL6210_MAX_CID; /* CID */
+       wil->vring2cid_tid[id][1] = 0; /* TID */
+
+       cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
+
+       rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd),
+                     WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
+       if (rc)
+               goto out_free;
+
+       if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) {
+               wil_err(wil, "Tx config failed, status 0x%02x\n",
+                       reply.cmd.status);
+               rc = -EINVAL;
+               goto out_free;
+       }
+       vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+
+       txdata->enabled = 1;
+
+       return 0;
+ out_free:
+       wil_vring_free(wil, vring, 1);
+ out:
+
+       return rc;
+}
+
 void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
 {
        struct vring *vring = &wil->vring_tx[id];
@@ -772,7 +838,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
        memset(txdata, 0, sizeof(*txdata));
 }
 
-static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
+static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil,
                                       struct sk_buff *skb)
 {
        int i;
@@ -805,15 +871,6 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
        return NULL;
 }
 
-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];
-
-       memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
-}
-
 static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                        struct sk_buff *skb);
 
@@ -834,6 +891,9 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
                        continue;
 
                cid = wil->vring2cid_tid[i][0];
+               if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+                       continue;
+
                if (!wil->sta[cid].data_port_open &&
                    (skb->protocol != cpu_to_be16(ETH_P_PAE)))
                        break;
@@ -848,57 +908,17 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
        return NULL;
 }
 
-/*
- * Find 1-st vring and return it; set dest address for this vring in skb
- * duplicate skb and send it to other active vrings
- */
-static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
-                                 struct sk_buff *skb)
+static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil,
+                                      struct sk_buff *skb)
 {
-       struct vring *v, *v2;
-       struct sk_buff *skb2;
-       int i;
-       u8 cid;
-
-       /* 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 (!wil->sta[cid].data_port_open)
-                       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 (!wil->sta[cid].data_port_open)
-                       continue;
+       struct vring *v;
+       int i = wil->bcast_vring;
 
-               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");
-               }
-       }
+       if (i < 0)
+               return NULL;
+       v = &wil->vring_tx[i];
+       if (!v->va)
+               return NULL;
 
        return v;
 }
@@ -995,6 +1015,8 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        uint i = swhead;
        dma_addr_t pa;
        int used;
+       bool mcast = (vring_index == wil->bcast_vring);
+       uint len = skb_headlen(skb);
 
        wil_dbg_txrx(wil, "%s()\n", __func__);
 
@@ -1020,7 +1042,17 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                return -EINVAL;
        vring->ctx[i].mapped_as = wil_mapped_as_single;
        /* 1-st segment */
-       wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+       wil_tx_desc_map(d, pa, len, vring_index);
+       if (unlikely(mcast)) {
+               d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */
+               if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) {
+                       /* set MCS 1 */
+                       d->mac.d[0] |= (1 << MAC_CFG_DESC_TX_0_MCS_INDEX_POS);
+                       /* packet mode 2 */
+                       d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS) |
+                                      (2 << MAC_CFG_DESC_TX_1_PKT_MODE_POS);
+               }
+       }
        /* Process TCP/UDP checksum offloading */
        if (unlikely(wil_tx_desc_offload_cksum_set(wil, d, skb))) {
                wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n",
@@ -1126,6 +1158,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct wil6210_priv *wil = ndev_to_wil(ndev);
        struct ethhdr *eth = (void *)skb->data;
+       bool bcast = is_multicast_ether_addr(eth->h_dest);
        struct vring *vring;
        static bool pr_once_fw;
        int rc;
@@ -1153,10 +1186,8 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                /* in STA mode (ESS), all to same VRING */
                vring = wil_find_tx_vring_sta(wil, skb);
        } else { /* direct communication, find matching VRING */
-               if (is_unicast_ether_addr(eth->h_dest))
-                       vring = wil_find_tx_vring(wil, skb);
-               else
-                       vring = wil_tx_bcast(wil, skb);
+               vring = bcast ? wil_find_tx_bcast(wil, skb) :
+                               wil_find_tx_ucast(wil, skb);
        }
        if (unlikely(!vring)) {
                wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
@@ -1219,7 +1250,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
        struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
        int done = 0;
        int cid = wil->vring2cid_tid[ringid][0];
-       struct wil_net_stats *stats = &wil->sta[cid].stats;
+       struct wil_net_stats *stats = NULL;
        volatile struct vring_tx_desc *_d;
        int used_before_complete;
        int used_new;
@@ -1238,6 +1269,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
 
        used_before_complete = wil_vring_used_tx(vring);
 
+       if (cid < WIL6210_MAX_CID)
+               stats = &wil->sta[cid].stats;
+
        while (!wil_vring_is_empty(vring)) {
                int new_swtail;
                struct wil_ctx *ctx = &vring->ctx[vring->swtail];
@@ -1279,12 +1313,15 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                        if (skb) {
                                if (likely(d->dma.error == 0)) {
                                        ndev->stats.tx_packets++;
-                                       stats->tx_packets++;
                                        ndev->stats.tx_bytes += skb->len;
-                                       stats->tx_bytes += skb->len;
+                                       if (stats) {
+                                               stats->tx_packets++;
+                                               stats->tx_bytes += skb->len;
+                                       }
                                } else {
                                        ndev->stats.tx_errors++;
-                                       stats->tx_errors++;
+                                       if (stats)
+                                               stats->tx_errors++;
                                }
                                wil_consume_skb(skb, d->dma.error == 0);
                        }
index a6b096e..4310972 100644 (file)
@@ -50,6 +50,8 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 #define WIL_TX_Q_LEN_DEFAULT           (4000)
 #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
 #define WIL_TX_RING_SIZE_ORDER_DEFAULT (10)
+#define WIL_BCAST_RING_SIZE_ORDER_DEFAULT      (7)
+#define WIL_BCAST_MCS0_LIMIT           (1024) /* limit for MCS0 frame size */
 /* limit ring size in range [32..32k] */
 #define WIL_RING_SIZE_ORDER_MIN        (5)
 #define WIL_RING_SIZE_ORDER_MAX        (15)
@@ -595,6 +597,7 @@ struct wil6210_priv {
        struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
        u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
        struct wil_sta_info sta[WIL6210_MAX_CID];
+       int bcast_vring;
        /* scan */
        struct cfg80211_scan_request *scan_request;
 
@@ -757,6 +760,9 @@ void wil_rx_fini(struct wil6210_priv *wil);
 int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
                      int cid, int tid);
 void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
+int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size);
+int wil_bcast_init(struct wil6210_priv *wil);
+void wil_bcast_fini(struct wil6210_priv *wil);
 
 netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
 int wil_tx_complete(struct wil6210_priv *wil, int ringid);