mwifiex: extend tx_data pause to AP interface as well

This patch adds support to extend TX Data pause for AP intefaces.
Also for station role, support for pausing/unpausing all traffic
when mac address parameter is BSSID is added.

Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 6f98d7e..6e82058 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -1554,6 +1554,8 @@
 void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter);
 void mwifiex_11n_delba(struct mwifiex_private *priv, int tid);
 int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy);
+void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
+				    struct sk_buff *event);
 
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index 72be16e..a2777d1 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -237,58 +237,110 @@
 	return ret;
 }
 
-static void
-mwifiex_process_sta_tx_pause_event(struct mwifiex_private *priv,
-				   struct sk_buff *event_skb)
+static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv,
+					 struct mwifiex_ie_types_header *tlv)
 {
-	struct mwifiex_ie_types_header *tlv;
-	struct mwifiex_tx_pause_tlv *tp_tlv;
+	struct mwifiex_tx_pause_tlv *tp;
 	struct mwifiex_sta_node *sta_ptr;
 	unsigned long flags;
+
+	tp = (void *)tlv;
+	mwifiex_dbg(priv->adapter, EVENT,
+		    "uap tx_pause: %pM pause=%d, pkts=%d\n",
+		    tp->peermac, tp->tx_pause,
+		    tp->pkt_cnt);
+
+	if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) {
+		if (tp->tx_pause)
+			priv->port_open = false;
+		else
+			priv->port_open = true;
+	} else if (is_multicast_ether_addr(tp->peermac)) {
+		mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause);
+	} else {
+		spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+		sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac);
+		spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+		if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) {
+			sta_ptr->tx_pause = tp->tx_pause;
+			mwifiex_update_ralist_tx_pause(priv, tp->peermac,
+						       tp->tx_pause);
+		}
+	}
+}
+
+static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv,
+					 struct mwifiex_ie_types_header *tlv)
+{
+	struct mwifiex_tx_pause_tlv *tp;
+	struct mwifiex_sta_node *sta_ptr;
+	int status;
+	unsigned long flags;
+
+	tp = (void *)tlv;
+	mwifiex_dbg(priv->adapter, EVENT,
+		    "sta tx_pause: %pM pause=%d, pkts=%d\n",
+		    tp->peermac, tp->tx_pause,
+		    tp->pkt_cnt);
+
+	if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) {
+		if (tp->tx_pause)
+			priv->port_open = false;
+		else
+			priv->port_open = true;
+	} else {
+		if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+			return;
+
+		status = mwifiex_get_tdls_link_status(priv, tp->peermac);
+		if (mwifiex_is_tdls_link_setup(status)) {
+			spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+			sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac);
+			spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+			if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) {
+				sta_ptr->tx_pause = tp->tx_pause;
+				mwifiex_update_ralist_tx_pause(priv,
+							       tp->peermac,
+							       tp->tx_pause);
+			}
+		}
+	}
+}
+
+void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
+				    struct sk_buff *event_skb)
+{
+	struct mwifiex_ie_types_header *tlv;
 	u16 tlv_type, tlv_len;
-	int tlv_buf_left, status;
+	int tlv_buf_left;
 
-	if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+	if (!priv->media_connected) {
+		mwifiex_dbg(priv->adapter, ERROR,
+			    "tx_pause event while disconnected; bss_role=%d\n",
+			    priv->bss_role);
 		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) {
+		    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 (mwifiex_is_tdls_link_setup(status)) {
-				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);
-				}
-			}
+			if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
+				mwifiex_process_sta_tx_pause(priv, tlv);
+			else
+				mwifiex_process_uap_tx_pause(priv, tlv);
 		}
 
 		tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) +
@@ -296,6 +348,7 @@
 		tlv = (void *)((u8 *)tlv + tlv_len +
 			       sizeof(struct mwifiex_ie_types_header));
 	}
+
 }
 
 /*
@@ -691,8 +744,8 @@
 		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");
+		mwifiex_process_tx_pause_event(priv, adapter->event_skb);
 		break;
 
 	case EVENT_TX_STATUS_REPORT:
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index a412c3d..a9d34c6 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -300,6 +300,10 @@
 		mwifiex_bt_coex_wlan_param_update_event(priv,
 							adapter->event_skb);
 		break;
+	case EVENT_TX_DATA_PAUSE:
+		mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n");
+		mwifiex_process_tx_pause_event(priv, adapter->event_skb);
+		break;
 	default:
 		mwifiex_dbg(adapter, EVENT,
 			    "event: unknown event id: %#x\n", eventcause);