mwifiex: add tx data pause support
authorAvinash Patil <patila@marvell.com>
Mon, 22 Jun 2015 13:36:07 +0000 (19:06 +0530)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 21 Jul 2015 13:40:12 +0000 (16:40 +0300)
This patch adds support to enable TX data pause feature for mwifiex.
Whenever FW TX buffers reach threshold, FW would send TX pause event
to driver. Driver in turn would block data traffic to that particular
receiver address.

Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Xinming Hu <huxm@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwifiex/wmm.h

index cd09051710e6cee82c624e6960f4c8accd452338..8ab0d81e6437cfb42a05c498ae8ecff8ea0eb839 100644 (file)
@@ -169,6 +169,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_UAP_PS_AO_TIMER    (PROPRIETARY_TLV_BASE_ID + 123)
 #define TLV_TYPE_PWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 145)
 #define TLV_TYPE_GWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 146)
+#define TLV_TYPE_TX_PAUSE           (PROPRIETARY_TLV_BASE_ID + 148)
 #define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
 #define TLV_TYPE_KEY_PARAM_V2       (PROPRIETARY_TLV_BASE_ID + 156)
 #define TLV_TYPE_TDLS_IDLE_TIMEOUT  (PROPRIETARY_TLV_BASE_ID + 194)
@@ -509,6 +510,7 @@ enum P2P_MODES {
 #define EVENT_TDLS_GENERIC_EVENT        0x00000052
 #define EVENT_RADAR_DETECTED           0x00000053
 #define EVENT_CHANNEL_REPORT_RDY        0x00000054
+#define EVENT_TX_DATA_PAUSE             0x00000055
 #define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 #define EVENT_TX_STATUS_REPORT         0x00000074
@@ -1131,6 +1133,13 @@ struct host_cmd_ds_tx_rate_query {
        u8 ht_info;
 } __packed;
 
+struct mwifiex_tx_pause_tlv {
+       struct mwifiex_ie_types_header header;
+       u8 peermac[ETH_ALEN];
+       u8 tx_pause;
+       u8 pkt_cnt;
+} __packed;
+
 enum Host_Sleep_Action {
        HS_CONFIGURE = 0x0001,
        HS_ACTIVATE  = 0x0002,
index ae98b5b83b1f0133cc216c2942095081ded5b52d..2106a5c246920af65634cff5387cc4dec1954f0c 100644 (file)
@@ -281,6 +281,7 @@ struct mwifiex_ra_list_tbl {
        u8 amsdu_in_ampdu;
        u16 total_pkt_count;
        bool tdls_link;
+       bool tx_paused;
 };
 
 struct mwifiex_tid_tbl {
@@ -294,6 +295,7 @@ struct mwifiex_tid_tbl {
 struct mwifiex_wmm_desc {
        struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
        u32 packets_out[MAX_NUM_TID];
+       u32 pkts_paused[MAX_NUM_TID];
        /* spin lock to protect ra_list */
        spinlock_t ra_list_spinlock;
        struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS];
@@ -768,6 +770,7 @@ struct mwifiex_sta_node {
        u8 tdls_status;
        struct mwifiex_tdls_capab tdls_cap;
        struct mwifiex_station_stats stats;
+       u8 tx_pause;
 };
 
 struct mwifiex_auto_tdls_peer {
index 848de2621958cfaacbcd500ae7317fddf6d92085..0a80814ddbb0ebd9368ea8bf5ac04bbf8981ab1b 100644 (file)
@@ -182,6 +182,67 @@ static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
        return ret;
 }
 
+static void
+mwifiex_process_sta_tx_pause_event(struct mwifiex_private *priv,
+                                  struct sk_buff *event_skb)
+{
+       struct mwifiex_ie_types_header *tlv;
+       struct mwifiex_tx_pause_tlv *tp_tlv;
+       struct mwifiex_sta_node *sta_ptr;
+       unsigned long flags;
+       u16 tlv_type, tlv_len;
+       int tlv_buf_left, status;
+
+       if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+               return;
+
+       if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+               return;
+
+       tlv_buf_left = event_skb->len - sizeof(u32);
+       tlv = (void *)event_skb->data + sizeof(u32);
+       while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) {
+               tlv_type = le16_to_cpu(tlv->type);
+               tlv_len  = le16_to_cpu(tlv->len);
+               if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) >
+                  tlv_buf_left) {
+                       mwifiex_dbg(priv->adapter, ERROR,
+                                   "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n",
+                                   tlv_len, tlv_buf_left);
+                       break;
+               }
+               if (tlv_type == TLV_TYPE_TX_PAUSE) {
+                       tp_tlv = (void *)tlv;
+                       mwifiex_dbg(priv->adapter, ERROR,
+                                   "TxPause: %pM pause=%d, pkts=%d\n",
+                                   tp_tlv->peermac, tp_tlv->tx_pause,
+                                   tp_tlv->pkt_cnt);
+                       status = mwifiex_get_tdls_link_status
+                                       (priv,  tp_tlv->peermac);
+                       if (status == TDLS_SETUP_COMPLETE) {
+                               spin_lock_irqsave(&priv->sta_list_spinlock,
+                                                 flags);
+                               sta_ptr = mwifiex_get_sta_entry
+                                               (priv, tp_tlv->peermac);
+                               spin_unlock_irqrestore(&priv->sta_list_spinlock,
+                                                      flags);
+                               if (sta_ptr && sta_ptr->tx_pause !=
+                                                            tp_tlv->tx_pause) {
+                                       sta_ptr->tx_pause = tp_tlv->tx_pause;
+                                       mwifiex_update_ralist_tx_pause
+                                               (priv, tp_tlv->peermac,
+                                                tp_tlv->tx_pause);
+                               }
+                       }
+               }
+
+               tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) +
+                               tlv_len;
+               tlv = (void *)((u8 *)tlv + tlv_len +
+                              sizeof(struct mwifiex_ie_types_header));
+       }
+}
+
 /*
 * This function handles coex events generated by firmware
 */
@@ -573,6 +634,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
                break;
 
+       case EVENT_TX_DATA_PAUSE:
+               mwifiex_process_sta_tx_pause_event(priv, adapter->event_skb);
+               mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n");
+               break;
+
        case EVENT_TX_STATUS_REPORT:
                mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n");
                mwifiex_parse_tx_status_event(priv, adapter->event_body);
index a8ea21c3340c73537c8f597ad6dc2d176b45f588..bc920a5a947cfd040854a5e536216ea8d0496a5d 100644 (file)
@@ -160,6 +160,7 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
                ra_list->tdls_link = false;
                ra_list->ba_status = BA_SETUP_NONE;
                ra_list->amsdu_in_ampdu = false;
+               ra_list->tx_paused = false;
                if (!mwifiex_queuing_ra_based(priv)) {
                        if (mwifiex_get_tdls_link_status(priv, ra) ==
                            TDLS_SETUP_COMPLETE) {
@@ -603,6 +604,43 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
        return NULL;
 }
 
+void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac,
+                                   u8 tx_pause)
+{
+       struct mwifiex_ra_list_tbl *ra_list;
+       u32 pkt_cnt = 0, tx_pkts_queued;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+       for (i = 0; i < MAX_NUM_TID; ++i) {
+               ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac);
+               if (ra_list && ra_list->tx_paused != tx_pause) {
+                       pkt_cnt += ra_list->total_pkt_count;
+                       ra_list->tx_paused = tx_pause;
+                       if (tx_pause)
+                               priv->wmm.pkts_paused[i] +=
+                                       ra_list->total_pkt_count;
+                       else
+                               priv->wmm.pkts_paused[i] -=
+                                       ra_list->total_pkt_count;
+               }
+       }
+
+       if (pkt_cnt) {
+               tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued);
+               if (tx_pause)
+                       tx_pkts_queued -= pkt_cnt;
+               else
+                       tx_pkts_queued += pkt_cnt;
+
+               atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued);
+               atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
+       }
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
 /*
  * This function retrieves an RA list node for a given TID and
  * RA address pair.
index 48ece0b355919d3c3a4278dfc1727fc391f5848d..90edb8fc337d59ea63e3569edbf39ee15ea7e09f 100644 (file)
@@ -126,6 +126,8 @@ struct mwifiex_ra_list_tbl *
 mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
                            const u8 *ra_addr);
 u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
+void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac,
+                                   u8 tx_pause);
 
 struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private
                                        *priv, u8 tid, const u8 *ra_addr);