qcacld2.0: support RSSI/Mac Addr sniffing for STA

  Extend the STA capability to sniff RSSI & Mac address.
this feature works only when STA is connected. meanwhile,
STA BMPS and roaming is disabled. Frame RSSI & Mac address
is indicated to OS through virtual monitor interface. Set
registry 'gEnableMonOnSta' = 1 in INI to enable this feature.

Ref test steps:
a. create virtual monitor interface and up.
   iw dev wlan0 interface add mon0 type monitor
   ifconfig mon0 up
b. STA connect
   ifconfig wlan0 up
   iw wlan0 connect "ssid"
c. collect sniffing frames
   tcpdump -i mon0 -v -w /data/sniff.pkt

Change-Id: I8f435a7f74b53babae987dbd3e1487cbd518f00d
CRs-Fixed: 2048058
diff --git a/CORE/CLD_TXRX/HTT/htt.h b/CORE/CLD_TXRX/HTT/htt.h
index 351704f..59c5864 100644
--- a/CORE/CLD_TXRX/HTT/htt.h
+++ b/CORE/CLD_TXRX/HTT/htt.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -4068,6 +4068,7 @@
     HTT_T2H_MSG_TYPE_FLOW_POOL_MAP            = 0x18,
     HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP          = 0x19,
     HTT_T2H_MSG_TYPE_SRING_SETUP_DONE         = 0x1a,
+    HTT_T2H_MSG_TYPE_MONITOR_MAC_HEADER_IND   = 0x20,
 
     HTT_T2H_MSG_TYPE_TEST,
     /* keep this last */
@@ -8188,4 +8189,76 @@
         ((_var) |= ((_val) << HTT_SRING_SETUP_DONE_STATUS_S)); \
     } while (0)
 
+/**
+ * @brief target -> host monitor mac header indication message
+ *
+ * @details
+ * The following diagram shows the format of the monitor mac header message
+ * sent from the target to the host, while enable rx filter promiscuous.
+ *
+ *          |31          24|23           16|15            8|7            0|
+ *          |-------------------------------------------------------------|
+ *          |            peer_id           |    reserved0  |    msg_type  |
+ *          |-------------------------------------------------------------|
+ *          |            reserved1         |           num_mpdu           |
+ *          |-------------------------------------------------------------|
+ *          |                       struct hw_rx_desc                     |
+ *          |                      (see wal_rx_desc.h)                    |
+ *          |-------------------------------------------------------------|
+ *          |                   struct ieee80211_frame_addr4              |
+ *          |                      (see ieee80211_defs.h)                 |
+ *          |                            ......                           |
+ *          |-------------------------------------------------------------|
+ *
+ * Header fields:
+ *  - msg_type
+ *    Bits 7:0
+ *    Purpose: Identifies this is a monitor mac header indication
+ *             message.
+ *    Value: 0x20
+ *   - reserved0
+ *     Bits 15:8
+ *     Purpose:
+ *     value:
+ *  - peer_id
+ *    Bits 31:16
+ *    Purpose: Software peer id given by host during association,
+               in this case it should be set to invalid(0xFF)
+ *    Value:
+ *  - num_mpdu
+ *    Bits 15:0
+ *    Purpose: numbers of mpdu mac header (struct ieee80211_frame_addr4) per rx ppdu
+ *             the maximum num_mpdu is limited to 32.
+ *    Value:
+ *   - reserved1
+ *     Bits 31:16
+ *     Purpose:
+ *     value:
+ */
+#define HTT_T2H_MONITOR_MAC_HEADER_IND_HDR_SIZE       8
+
+#define HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_M          0xFFFF0000
+#define HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_S          16
+
+#define HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_M         0x0000FFFF
+#define HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_S         0
+
+#define HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_SET(word, value)             \
+    do {                                                         \
+        HTT_CHECK_SET_VAL(HTT_T2H_MONITOR_MAC_HEADER_PEER_ID, value);   \
+        (word) |= (value)  << HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_S;     \
+    } while (0)
+#define HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_GET(word) \
+    (((word) & HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_M) >> \
+    HTT_T2H_MONITOR_MAC_HEADER_PEER_ID_S)
+
+#define HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_SET(word, value)             \
+    do {                                                         \
+        HTT_CHECK_SET_VAL(HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU, value);   \
+        (word) |= (value)  << HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_S;     \
+    } while (0)
+#define HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_GET(word) \
+    (((word) & HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_M) >> \
+    HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_S)
+
 #endif
diff --git a/CORE/CLD_TXRX/HTT/htt_internal.h b/CORE/CLD_TXRX/HTT/htt_internal.h
index 947444c..667bf4f 100644
--- a/CORE/CLD_TXRX/HTT/htt_internal.h
+++ b/CORE/CLD_TXRX/HTT/htt_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -51,6 +51,21 @@
 #include <rx_desc.h>
 #include <wal_rx_desc.h> /* struct rx_attention, etc */
 
+/*
+ * Define the HW descriptor information used by host,
+ * which is copied by FW into HTT msg.
+ */
+struct htt_hw_rx_desc_base {
+    struct rx_attention  attention;
+    struct rx_frag_info  frag_info;
+    struct rx_mpdu_start mpdu_start;
+    struct rx_msdu_start msdu_start;
+    struct rx_msdu_end   msdu_end;
+    struct rx_mpdu_end   mpdu_end;
+    struct rx_ppdu_start ppdu_start;
+    struct rx_ppdu_end   ppdu_end;
+};
+
 struct htt_host_fw_desc_base {
     union {
         struct fw_rx_desc_base val;
diff --git a/CORE/CLD_TXRX/HTT/htt_rx.c b/CORE/CLD_TXRX/HTT/htt_rx.c
index 57762e0..53c82ad 100644
--- a/CORE/CLD_TXRX/HTT/htt_rx.c
+++ b/CORE/CLD_TXRX/HTT/htt_rx.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1922,6 +1922,100 @@
 }
 
 int
+htt_rx_mac_header_mon_process(
+		htt_pdev_handle pdev,
+		adf_nbuf_t rx_ind_msg,
+		adf_nbuf_t *head_msdu,
+		adf_nbuf_t *tail_msdu)
+{
+	struct htt_hw_rx_desc_base *hw_desc;
+	struct ieee80211_frame_addr4 *mac_array;
+	uint8_t rtap_buf[sizeof(struct ieee80211_radiotap_header) + 100] = {0};
+	uint16_t rtap_len;
+	uint32_t *msg_word;
+	uint8_t *rx_ind_data;
+	adf_nbuf_t msdu = NULL;
+
+	/* num of mac header in rx_ind_msg */
+	int num_elems;
+	int elem;
+	uint32_t tsf;
+	uint32_t rssi_comb;
+
+	rx_ind_data = adf_nbuf_data(rx_ind_msg);
+	msg_word = (uint32_t *)rx_ind_data;
+	msg_word++;
+	num_elems = HTT_T2H_MONITOR_MAC_HEADER_NUM_MPDU_GET(*msg_word);
+
+	/* what's the num_elem max value? */
+	if (num_elems <= 0)
+		return 0;
+
+	/* get htt_hw_rx_desc_base_rx_desc pointer */
+	hw_desc = (struct htt_hw_rx_desc_base *)
+			(rx_ind_data + HTT_T2H_MONITOR_MAC_HEADER_IND_HDR_SIZE);
+
+	rssi_comb = hw_desc->ppdu_start.rssi_comb;
+	tsf = hw_desc->ppdu_end.tsf_timestamp;
+
+	/* construct one radiotap header */
+	rtap_len = adf_nbuf_construct_radiotap(
+					rtap_buf,
+					tsf,
+					rssi_comb);
+
+	/* get ieee80211_frame_addr4 array pointer*/
+	mac_array = (struct ieee80211_frame_addr4 *)
+			(rx_ind_data + HTT_T2H_MONITOR_MAC_HEADER_IND_HDR_SIZE +
+			 sizeof(struct htt_hw_rx_desc_base));
+
+	for (elem = 0; elem < num_elems; elem++) {
+		uint8_t *dest = NULL;
+		/*
+		 * copy each mac header +
+		 * radiotap header into single msdu buff
+		 */
+		msdu = adf_nbuf_alloc(
+			pdev->osdev,
+			rtap_len + sizeof(struct ieee80211_frame_addr4),
+			0, 4, TRUE);
+		if (!msdu)
+			return A_NO_MEMORY;
+
+		dest = adf_nbuf_put_tail(msdu, rtap_len);
+		if (!dest) {
+			adf_os_print("%s: No buffer to save radiotap len %d\n",
+				     __func__, rtap_len);
+			return	A_NO_MEMORY;
+		}
+		adf_os_mem_copy(dest, rtap_buf, rtap_len);
+
+		dest = adf_nbuf_put_tail(msdu,
+					 sizeof(struct ieee80211_frame_addr4));
+		if (!dest) {
+			adf_os_print("%s: No buffer for mac header %u\n",
+				     __func__,
+				     (unsigned int)
+				     sizeof(struct ieee80211_frame_addr4));
+			return	A_NO_MEMORY;
+		}
+		adf_os_mem_copy(dest, &mac_array[elem],
+				sizeof(struct ieee80211_frame_addr4));
+
+		adf_nbuf_set_next(msdu, NULL);
+		if (*head_msdu == NULL) {
+			*head_msdu = msdu;
+			*tail_msdu = msdu;
+		} else {
+			adf_nbuf_set_next(*tail_msdu, msdu);
+			*tail_msdu = msdu;
+		}
+	}
+
+	return 0;
+}
+
+int
 htt_rx_offload_msdu_cnt_hl(
     htt_pdev_handle pdev)
 {
diff --git a/CORE/CLD_TXRX/HTT/htt_t2h.c b/CORE/CLD_TXRX/HTT/htt_t2h.c
index 0653d37..78c7c4e 100644
--- a/CORE/CLD_TXRX/HTT/htt_t2h.c
+++ b/CORE/CLD_TXRX/HTT/htt_t2h.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -864,6 +864,11 @@
                                                peer_id, tid, offload_ind);
             break;
      }
+    case HTT_T2H_MSG_TYPE_MONITOR_MAC_HEADER_IND:
+        {
+            ol_rx_mon_mac_header_handler(pdev->txrx_pdev, htt_t2h_msg);
+            break;
+        }
 
     default:
         htt_t2h_lp_msg_handler(context, htt_t2h_msg);
diff --git a/CORE/CLD_TXRX/TLSHIM/tl_shim.c b/CORE/CLD_TXRX/TLSHIM/tl_shim.c
index 6388f3a..0f77c98 100644
--- a/CORE/CLD_TXRX/TLSHIM/tl_shim.c
+++ b/CORE/CLD_TXRX/TLSHIM/tl_shim.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1186,6 +1186,29 @@
 	}
 }
 
+/*
+ * TLSHIM virtual monitor mode RX callback,
+ * registered for OL data indication.
+ */
+
+static void tl_shim_vir_mon_rx(adf_nbuf_t rx_buf_list)
+{
+	struct txrx_tl_shim_ctx *tl_shim;
+	void *vos_ctx = vos_get_global_context(VOS_MODULE_ID_TL, NULL);
+
+	tl_shim = vos_get_context(VOS_MODULE_ID_TL, vos_ctx);
+
+	if (!tl_shim) {
+		TLSHIM_LOGE("%s: Failed to get TLSHIM context", __func__);
+		return;
+	}
+
+	if (tl_shim->rx_monitor_cb)
+		tl_shim->rx_monitor_cb(vos_ctx, rx_buf_list, 0);
+	else
+		TLSHIM_LOGE("%s: tl_shim->rx_monitor_cb is NULL", __func__);
+}
+
 /*************************/
 /*	TL APIs		 */
 /*************************/
@@ -1939,6 +1962,57 @@
 	return VOS_STATUS_SUCCESS;
 }
 
+VOS_STATUS tl_register_vir_mon_cb(void *vos_ctx,
+				  WLANTL_STARxCBType rxcb)
+{
+	struct txrx_tl_shim_ctx *tl_shim;
+	ol_txrx_pdev_handle pdev;
+
+	tl_shim = vos_get_context(VOS_MODULE_ID_TL, vos_ctx);
+	if (!tl_shim) {
+		TLSHIM_LOGE("tl_shim is NULL");
+		return VOS_STATUS_E_FAULT;
+	}
+
+	pdev = vos_get_context(VOS_MODULE_ID_TXRX, vos_ctx);
+	if (!pdev) {
+		TLSHIM_LOGE("%s: Failed to find pdev", __func__);
+		return VOS_STATUS_E_FAULT;
+	}
+
+	tl_shim->rx_monitor_cb = rxcb;
+
+	/* register TLSHIM RX montior callback to OL */
+	ol_txrx_osif_pdev_mon_register_cbk(pdev, tl_shim_vir_mon_rx);
+
+	return VOS_STATUS_SUCCESS;
+}
+
+VOS_STATUS tl_deregister_vir_mon_cb(void *vos_ctx)
+{
+	struct txrx_tl_shim_ctx *tl_shim;
+	ol_txrx_pdev_handle pdev;
+
+	tl_shim = vos_get_context(VOS_MODULE_ID_TL, vos_ctx);
+	if (!tl_shim) {
+		TLSHIM_LOGE("tl_shim is NULL");
+		return VOS_STATUS_E_FAULT;
+	}
+
+	pdev = vos_get_context(VOS_MODULE_ID_TXRX, vos_ctx);
+	if (!pdev) {
+		TLSHIM_LOGE("%s: Failed to find pdev", __func__);
+		return VOS_STATUS_E_FAULT;
+	}
+
+	tl_shim->rx_monitor_cb = NULL;
+
+	/* register TLSHIM RX montior callback to OL */
+	ol_txrx_osif_pdev_mon_register_cbk(pdev, NULL);
+
+	return VOS_STATUS_SUCCESS;
+}
+
 VOS_STATUS WLANTL_Stop(void *vos_ctx)
 {
 	/* Nothing to do really */
diff --git a/CORE/CLD_TXRX/TLSHIM/tl_shim.h b/CORE/CLD_TXRX/TLSHIM/tl_shim.h
index 770684c..57ef2e8 100644
--- a/CORE/CLD_TXRX/TLSHIM/tl_shim.h
+++ b/CORE/CLD_TXRX/TLSHIM/tl_shim.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014,2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014,2016-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -112,6 +112,7 @@
 	ipa_uc_fw_op_cb fw_op_cb;
 	void *usr_ctxt;
 #endif /* IPA_UC_OFFLOAD */
+	WLANTL_STARxCBType rx_monitor_cb;
 };
 
 /*
@@ -201,4 +202,19 @@
 }
 #endif
 
+static inline void *tlshim_get_rxmon_cbk(void)
+{
+	void *vos_ctx = vos_get_global_context(VOS_MODULE_ID_TL, NULL);
+	struct txrx_tl_shim_ctx *tlshim;
+
+	if (!vos_ctx)
+		return NULL;
+
+	tlshim = vos_get_context(VOS_MODULE_ID_TL, vos_ctx);
+	if (tlshim)
+		return (void *)tlshim->rx_monitor_cb;
+
+	return NULL;
+}
+
 #endif
diff --git a/CORE/CLD_TXRX/TXRX/ol_rx.c b/CORE/CLD_TXRX/TXRX/ol_rx.c
index 150c109..a44a4b0 100644
--- a/CORE/CLD_TXRX/TXRX/ol_rx.c
+++ b/CORE/CLD_TXRX/TXRX/ol_rx.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -299,6 +299,45 @@
 		ol_txrx_peer_unref_delete(peer);
 }
 
+/*
+ * ol_rx_mon_mac_header_handler() - htt rx mac header msg handler
+ * @pdev: pointer to struct ol_txrx_pdev_handle
+ * @rx_ind_msg: htt rx indication message
+ */
+void
+ol_rx_mon_mac_header_handler(
+	ol_txrx_pdev_handle pdev,
+	adf_nbuf_t rx_ind_msg)
+{
+	adf_nbuf_t head_msdu = NULL;
+	adf_nbuf_t tail_msdu = NULL;
+	adf_nbuf_t next;
+	htt_pdev_handle htt_pdev;
+
+	htt_pdev = pdev->htt_pdev;
+
+	/* only if the rx callback is ready */
+	if (NULL == pdev->osif_rx_mon_cb)
+	    return;
+
+	htt_rx_mac_header_mon_process(htt_pdev,
+				      rx_ind_msg,
+				      &head_msdu,
+				      &tail_msdu);
+
+	if (head_msdu) {
+	   if (pdev->osif_rx_mon_cb) {
+	       pdev->osif_rx_mon_cb(head_msdu);
+	   } else {
+	      while (head_msdu) {
+		next = adf_nbuf_next(head_msdu);
+		adf_nbuf_free(head_msdu);
+		head_msdu = next;
+		}
+	   }
+	}
+}
+
 void
 ol_rx_indication_handler(
     ol_txrx_pdev_handle pdev,
diff --git a/CORE/CLD_TXRX/TXRX/ol_txrx.c b/CORE/CLD_TXRX/TXRX/ol_txrx.c
index 00d90e9..b137cdf 100644
--- a/CORE/CLD_TXRX/TXRX/ol_txrx.c
+++ b/CORE/CLD_TXRX/TXRX/ol_txrx.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1301,6 +1301,25 @@
     #endif /* QCA_LL_TX_FLOW_CT */
 }
 
+/**
+ * ol_txrx_osif_pdev_mon_register_cbk() - register monitor rx callback
+ * @txrx_pdev: pdev handle
+ * @cbk: monitor rx callback function
+ *
+ * Return: none
+ */
+void ol_txrx_osif_pdev_mon_register_cbk(
+	ol_txrx_pdev_handle txrx_pdev,
+	ol_txrx_vir_mon_rx_fp cbk)
+{
+	TXRX_ASSERT2(txrx_pdev);
+
+	if (NULL == txrx_pdev)
+	    return;
+
+	txrx_pdev->osif_rx_mon_cb = cbk;
+}
+
 void
 ol_txrx_set_curchan(
     ol_txrx_pdev_handle pdev,
diff --git a/CORE/CLD_TXRX/TXRX/ol_txrx_types.h b/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
index 6cd70c1..cb3404d 100644
--- a/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
+++ b/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -616,6 +616,9 @@
 	tp_ol_packetdump_cb ol_tx_packetdump_cb;
 	tp_ol_packetdump_cb ol_rx_packetdump_cb;
 
+	/* virtual montior callback functions */
+	ol_txrx_vir_mon_rx_fp osif_rx_mon_cb;
+
 #ifdef WLAN_FEATURE_TSF_PLUS
 	tp_ol_timestamp_cb ol_tx_timestamp_cb;
 #endif
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index f039e46..3991428 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -4908,6 +4908,26 @@
 #define CFG_CCA_THRESHOLD_5G_MAX     (127)
 #define CFG_CCA_THRESHOLD_5G_DEFAULT (28)
 
+/*
+ * <ini>
+ * gEnableMonOnSta - extend the monitor capability for STA
+ * @Min: 0
+ * @Max: 1
+ * @Default: 0
+ *
+ * this is used to control monitor feature for STA.
+ *
+ * Related: none
+ *
+ * Usage: External
+ *
+ * </ini>
+ */
+#define CFG_ENABLE_MONITOR_ON_STA	  "gEnableMonOnSta"
+#define CFG_ENABLE_MONITOR_ON_STA_MIN     (0)
+#define CFG_ENABLE_MONITOR_ON_STA_MAX     (1)
+#define CFG_ENABLE_MONITOR_ON_STA_DEFAULT (0)
+
 /*---------------------------------------------------------------------------
   Type declarations
   -------------------------------------------------------------------------*/
@@ -5792,6 +5812,7 @@
    bool      cca_threshold_enable;
    uint32_t  cca_threshold_2g;
    uint32_t  cca_threshold_5g;
+   uint8_t                     mon_on_sta_enable;
 };
 
 typedef struct hdd_config hdd_config_t;
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 8d3b230..bad420e 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -2120,6 +2120,8 @@
     /* the context that is capturing tsf */
     hdd_adapter_t *cap_tsf_context;
 #endif
+    /* flag to show whether moniotr mode is enabled */
+    bool is_mon_enable;
 };
 
 /*---------------------------------------------------------------------------
@@ -2658,4 +2660,13 @@
  */
 int hdd_drv_cmd_validate(tANI_U8 *command, int len);
 
+/**
+ * wlan_hdd_monitor_mode_enable() - function to enable/disable monitor mode
+ * @hdd_ctx: pointer to HDD context
+ * @enable: 0 - disable, 1 - enable
+ *
+ * Return: 0 for success and error number for failure
+ */
+int wlan_hdd_monitor_mode_enable(hdd_context_t *hdd_ctx, bool enable);
+
 #endif    // end #if !defined( WLAN_HDD_MAIN_H )
diff --git a/CORE/HDD/inc/wlan_hdd_tx_rx.h b/CORE/HDD/inc/wlan_hdd_tx_rx.h
index 7906433..2fd820c 100644
--- a/CORE/HDD/inc/wlan_hdd_tx_rx.h
+++ b/CORE/HDD/inc/wlan_hdd_tx_rx.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -268,6 +268,20 @@
 VOS_STATUS hdd_mon_rx_packet_cbk(v_VOID_t *vos_ctx, adf_nbuf_t rx_buf,
 				 uint8_t sta_id);
 
+/**
+ * hdd_vir_mon_rx_cbk() - Monitor callback registered to TLSHIM.
+ * @vosContext: [in] pointer to VOS context
+ * @rxBuf:      [in] pointer to rx adf_nbuf
+ * @staId:      [in] Station Id
+ *
+ * TL will call this to notify the HDD when one or more packets were
+ * received for a registered HDD adapter.
+ * Return: VOS_STATUS_E_FAILURE if any errors encountered, VOS_STATUS_SUCCESS
+ * otherwise
+ */
+VOS_STATUS hdd_vir_mon_rx_cbk(v_VOID_t *vos_ctx, adf_nbuf_t rx_buf,
+			      uint8_t sta_id);
+
 void wlan_display_tx_timeout_stats(hdd_adapter_t *adapter);
 
 const char *hdd_reason_type_to_string(enum netif_reason_type reason);
diff --git a/CORE/HDD/src/wlan_hdd_assoc.c b/CORE/HDD/src/wlan_hdd_assoc.c
index 34b3e3e..d2166d0 100644
--- a/CORE/HDD/src/wlan_hdd_assoc.c
+++ b/CORE/HDD/src/wlan_hdd_assoc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1187,6 +1187,7 @@
     int type = -1;
     v_MACADDR_t peerMacAddr;
 
+    hdd_adapter_t *mon_adapter = NULL;
 #if defined (WLAN_FEATURE_VOWIFI_11R)
     // Added to find the auth type on the fly at run time
     // rather than with cfg to see if FT is enabled
@@ -1303,6 +1304,16 @@
         spin_unlock_bh(&pHddCtx->bus_bw_lock);
         hdd_start_bus_bw_compute_timer(pAdapter);
 #endif
+        if (pHddCtx->cfg_ini->mon_on_sta_enable &&
+            (pAdapter->device_mode == WLAN_HDD_INFRA_STATION)) {
+            /* monitor mode interface should be ready */
+            mon_adapter = hdd_get_adapter(pHddCtx, WLAN_HDD_MONITOR);
+
+            if (mon_adapter && (WLAN_HDD_ADAPTER_MAGIC == mon_adapter->magic) &&
+                tlshim_get_rxmon_cbk()) {
+               wlan_hdd_monitor_mode_enable(pHddCtx, true);
+           }
+        }
     }
     else if (eConnectionState_IbssConnected == pHddStaCtx->conn_info.connState) // IBss Associated
     {
@@ -1358,6 +1369,11 @@
         spin_unlock_bh(&pHddCtx->bus_bw_lock);
         hdd_stop_bus_bw_compute_timer(pAdapter);
 #endif
+        if (pHddCtx->cfg_ini->mon_on_sta_enable &&
+            (pAdapter->device_mode == WLAN_HDD_INFRA_STATION) &&
+            (true == pHddCtx->is_mon_enable)) {
+            wlan_hdd_monitor_mode_enable(pHddCtx, false);
+        }
     }
     hdd_dump_concurrency_info(pHddCtx);
 
@@ -2158,6 +2174,7 @@
 			hdd_is_roam_sync_in_progress(roaminfo));
 	hdd_connSetAuthenticated(adapter, VOS_TRUE);
 	if (hddctx->cfg_ini->enablePowersaveOffload &&
+		(false == hddctx->is_mon_enable) &&
 		((WLAN_HDD_INFRA_STATION == adapter->device_mode) ||
 		(WLAN_HDD_P2P_CLIENT == adapter->device_mode))) {
 		sme_PsOffloadEnableDeferredPowerSave(
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 46fccb1..5518cfe 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -5195,6 +5195,13 @@
 		CFG_CCA_THRESHOLD_5G_DEFAULT,
 		CFG_CCA_THRESHOLD_5G_MIN,
 		CFG_CCA_THRESHOLD_5G_MAX),
+
+	REG_VARIABLE(CFG_ENABLE_MONITOR_ON_STA, WLAN_PARAM_Integer,
+		     hdd_config_t, mon_on_sta_enable,
+		     VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+		     CFG_ENABLE_MONITOR_ON_STA_DEFAULT,
+		     CFG_ENABLE_MONITOR_ON_STA_MIN,
+		     CFG_ENABLE_MONITOR_ON_STA_MAX),
 };
 
 
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index d671e9d..8ed1096 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -569,6 +569,14 @@
    }
 };
 
+static const struct ieee80211_iface_limit
+   wlan_hdd_mon_iface_limit[] = {
+   {
+       .max = 3, /* Monitor interface */
+       .types = BIT(NL80211_IFTYPE_MONITOR),
+   },
+};
+
 static struct ieee80211_iface_combination
 wlan_hdd_iface_combination[] = {
    /* STA */
@@ -636,6 +644,13 @@
       .n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_p2pgo_iface_limit),
       .beacon_int_infra_match = true,
    },
+   /* Monitor */
+   {
+      .limits = wlan_hdd_mon_iface_limit,
+      .max_interfaces = 3,
+      .num_different_channels = 2,
+      .n_limits = ARRAY_SIZE(wlan_hdd_mon_iface_limit),
+   },
 };
 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) ||
          defined(WITH_BACKPORTS) */
@@ -15524,7 +15539,8 @@
                              | BIT(NL80211_IFTYPE_ADHOC)
                              | BIT(NL80211_IFTYPE_P2P_CLIENT)
                              | BIT(NL80211_IFTYPE_P2P_GO)
-                             | BIT(NL80211_IFTYPE_AP);
+                             | BIT(NL80211_IFTYPE_AP)
+                             | BIT(NL80211_IFTYPE_MONITOR);
 
     if( pCfg->advertiseConcurrentOperation )
     {
@@ -29969,6 +29985,11 @@
         return -EINVAL;
     }
 
+    if (true == vos_is_mon_enable()) {
+        hddLog(LOGE, FL("command not allowed in FTM mode"));
+        return -EINVAL;
+    }
+
     /* If RADAR detection is in progress (HDD), prevent suspend. The flag
      * "dfs_cac_block_tx" is set to TRUE when RADAR is found and stay TRUE until
      * CAC is done for a SoftAP which is in started state.
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 5db5a05..d82c784 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -9504,6 +9504,206 @@
 	return ret;
 }
 
+int wlan_hdd_monitor_mode_enable(hdd_context_t *hdd_ctx, bool enable)
+{
+	int ret = 0;
+
+	ret = process_wma_set_command(
+		0,
+		GEN_PDEV_MONITOR_MODE,
+		enable,
+		GEN_CMD);
+
+	if (ret) {
+		hddLog(LOGE,
+		       FL("send monitor enable cmd fail, ret %d"),
+		       ret);
+		return ret;
+	}
+
+	hdd_ctx->is_mon_enable = enable;
+
+	return ret;
+}
+
+/**
+ * __hdd_vir_mon_open() - HDD monitor open
+ * @dev: pointer to net_device
+ *
+ * Return: 0 for success and error number for failure
+ */
+static int __hdd_vir_mon_open(struct net_device *dev)
+{
+	hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	hdd_context_t *hdd_ctx =  WLAN_HDD_GET_CTX(adapter);
+	hdd_adapter_t *sta_adapter = NULL;
+	int ret;
+	VOS_STATUS vos_status;
+
+	if (!hdd_ctx->cfg_ini->mon_on_sta_enable) {
+		hddLog(LOGE,
+		       FL("monitor feature for STA is not enabled"));
+		goto exit;
+	}
+
+	MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
+			 adapter->sessionId, adapter->device_mode));
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (0 != ret)
+		return ret;
+
+	/* register monitor RX callback to TLSHIM and OL */
+	vos_status = tl_register_vir_mon_cb(hdd_ctx->pvosContext,
+					    hdd_vir_mon_rx_cbk);
+
+	if (VOS_STATUS_SUCCESS != vos_status) {
+		hddLog(LOGE,
+		       FL("failed to register monitor cbk"));
+		goto exit;
+	}
+
+	/* get STA adapter to check if STA is connected */
+	sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
+
+	if ((NULL == sta_adapter) ||
+	    (WLAN_HDD_ADAPTER_MAGIC != sta_adapter->magic)) {
+		hddLog(LOGE,
+		       FL("STA adapter is not existed."));
+		return ret;
+	}
+
+	if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter)) &&
+	    (false == hdd_ctx->is_mon_enable)) {
+		/* send WMI cmd to enable monitor function */
+		ret = wlan_hdd_monitor_mode_enable(hdd_ctx, true);
+
+		/* disable Sta BMPS */
+		if (hdd_ctx->cfg_ini->enablePowersaveOffload)
+			sme_PsOffloadDisablePowerSave(
+				WLAN_HDD_GET_HAL_CTX(sta_adapter),
+				NULL,
+				NULL,
+				sta_adapter->sessionId);
+
+#if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
+		if (hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled &&
+		    hdd_ctx->cfg_ini->isRoamOffloadScanEnabled)
+			sme_stopRoaming(WLAN_HDD_GET_HAL_CTX(sta_adapter),
+					sta_adapter->sessionId, 0);
+#endif
+	}
+
+	return ret;
+exit:
+	return -EIO;
+}
+
+/**
+ * __hdd_vir_mon_stop() - HDD monitor stop
+ * @dev: pointer to net_device
+ *
+ * Return: 0 for success and error number for failure
+ */
+static int __hdd_vir_mon_stop(struct net_device *dev)
+{
+	hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	hdd_context_t *hdd_ctx =  WLAN_HDD_GET_CTX(adapter);
+	hdd_adapter_t *sta_adapter = NULL;
+	int ret;
+	VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
+
+	if (!hdd_ctx->cfg_ini->mon_on_sta_enable) {
+		hddLog(LOGE,
+		       FL("monitor feature for STA is not enabled"));
+		goto exit;
+	}
+
+	MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
+			 adapter->sessionId, adapter->device_mode));
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (0 != ret)
+		return ret;
+
+	/* if monitor is enabled already,
+	 * send WMI cmd to disable target monitor.
+	 */
+	if (true == hdd_ctx->is_mon_enable) {
+		ret = wlan_hdd_monitor_mode_enable(hdd_ctx, false);
+
+		/* get STA adapter to check if STA is connected */
+		sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
+
+	    if (sta_adapter &&
+		(WLAN_HDD_ADAPTER_MAGIC == sta_adapter->magic) &&
+		hdd_connIsConnected(
+				WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter))) {
+		/* enable BMPS power save */
+		if (hdd_ctx->cfg_ini->enablePowersaveOffload)
+			sme_PsOffloadEnablePowerSave(
+					WLAN_HDD_GET_HAL_CTX(sta_adapter),
+					sta_adapter->sessionId);
+
+#if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
+		/* enable roaming */
+		if (hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled &&
+		    hdd_ctx->cfg_ini->isRoamOffloadScanEnabled)
+			sme_startRoaming(WLAN_HDD_GET_HAL_CTX(sta_adapter),
+					 sta_adapter->sessionId,
+					 REASON_CONNECT);
+#endif
+		}
+	}
+
+	/* Deregister monitor RX callback to TLSHIM and OL */
+	vos_status = tl_deregister_vir_mon_cb(hdd_ctx->pvosContext);
+
+	if (VOS_STATUS_SUCCESS != vos_status) {
+		hddLog(LOGE,
+		       FL("failed to deregister monitor cbk"));
+		goto exit;
+	}
+
+	return ret;
+exit:
+	return -EIO;
+}
+
+/**
+ * hdd_vir_mon_open()
+ * @dev: pointer to net_device
+ *
+ * Return: 0 for success and error number for failure
+ */
+static int hdd_vir_mon_open(struct net_device *dev)
+{
+	int ret = 0;
+
+	vos_ssr_protect(__func__);
+	__hdd_vir_mon_open(dev);
+	vos_ssr_unprotect(__func__);
+
+	return ret;
+}
+
+/**
+ * hdd_vir_mon_stop()
+ * @dev: pointer to net_device
+ *
+ * Return: 0 for success and error number for failure
+ */
+static int hdd_vir_mon_stop(struct net_device *dev)
+{
+	int ret = 0;
+
+	vos_ssr_protect(__func__);
+	__hdd_vir_mon_stop(dev);
+	vos_ssr_unprotect(__func__);
+
+	return ret;
+}
+
 #ifdef MODULE
 /**
  * wlan_hdd_stop_enter_lowpower() - Enter low power mode
@@ -10403,6 +10603,10 @@
       .ndo_get_stats = hdd_stats,
 };
 
+static struct net_device_ops wlan_mon_dev_ops = {
+	.ndo_open = hdd_vir_mon_open,
+	.ndo_stop = hdd_vir_mon_stop,
+};
 
 void hdd_set_station_ops( struct net_device *pWlanDev )
 {
@@ -10412,6 +10616,11 @@
 		pWlanDev->netdev_ops = &wlan_drv_ops;
 }
 
+void hdd_set_monitor_ops(struct net_device *pwlan_dev)
+{
+	pwlan_dev->netdev_ops = &wlan_mon_dev_ops;
+}
+
 static void mon_mode_ether_setup(struct net_device *dev)
 {
 	dev->header_ops         = NULL;
@@ -10597,6 +10806,79 @@
    return pAdapter;
 }
 
+static hdd_adapter_t *hdd_alloc_monitor_adapter(hdd_context_t *pHddCtx,
+						tSirMacAddr macAddr,
+						unsigned char name_assign_type,
+						const char *name)
+{
+	struct net_device *pwlan_dev = NULL;
+	hdd_adapter_t *pAdapter = NULL;
+	/*
+	 * cfg80211 initialization and registration....
+	 */
+	pwlan_dev = alloc_netdev_mq(sizeof(hdd_adapter_t),
+				   name,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS)
+				   name_assign_type,
+#endif
+				   mon_mode_ether_setup,
+				   NUM_TX_QUEUES);
+
+	if (pwlan_dev != NULL) {
+	   /* Save the pointer to the net_device in the HDD adapter */
+	   pAdapter = (hdd_adapter_t *)netdev_priv(pwlan_dev);
+
+	   vos_mem_zero(pAdapter, sizeof(hdd_adapter_t));
+
+	   pAdapter->dev = pwlan_dev;
+	   pAdapter->pHddCtx = pHddCtx;
+	   pAdapter->magic = WLAN_HDD_ADAPTER_MAGIC;
+
+	   vos_event_init(&pAdapter->scan_info.scan_finished_event);
+	   pAdapter->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP;
+
+	   pAdapter->offloads_configured = FALSE;
+	   pAdapter->isLinkUpSvcNeeded = FALSE;
+	   pAdapter->higherDtimTransition = eANI_BOOLEAN_TRUE;
+	   /* Init the net_device structure */
+	   strlcpy(pwlan_dev->name, name, IFNAMSIZ);
+
+	   vos_mem_copy(pwlan_dev->dev_addr,
+			(void *)macAddr, sizeof(tSirMacAddr));
+	   vos_mem_copy(pAdapter->macAddressCurrent.bytes,
+			macAddr, sizeof(tSirMacAddr));
+	   pwlan_dev->watchdog_timeo = HDD_TX_TIMEOUT;
+	   /*
+	    * kernel will consume ethernet header length buffer for hard_header,
+	    * so just reserve it
+	    */
+	   hdd_set_needed_headroom(pwlan_dev, pwlan_dev->hard_header_len);
+
+	   if (pHddCtx->cfg_ini->enableIPChecksumOffload)
+		pwlan_dev->features |= NETIF_F_HW_CSUM;
+	   else if (pHddCtx->cfg_ini->enableTCPChkSumOffld)
+		pwlan_dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+	   pwlan_dev->features |= NETIF_F_RXCSUM;
+	   hdd_set_monitor_ops(pAdapter->dev);
+
+	   pwlan_dev->destructor = free_netdev;
+	   pwlan_dev->ieee80211_ptr = &pAdapter->wdev;
+	   pwlan_dev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN;
+	   pAdapter->wdev.wiphy = pHddCtx->wiphy;
+	   pAdapter->wdev.netdev =  pwlan_dev;
+	   /* set pwlan_dev's parent to underlying device */
+	   SET_NETDEV_DEV(pwlan_dev, pHddCtx->parent_dev);
+	   hdd_wmm_init(pAdapter);
+	   hdd_adapter_runtime_suspend_init(pAdapter);
+	   spin_lock_init(&pAdapter->pause_map_lock);
+	   pAdapter->last_tx_jiffies = jiffies;
+	   pAdapter->bug_report_count = 0;
+	   pAdapter->start_time = pAdapter->last_time = vos_system_ticks();
+	}
+
+	return pAdapter;
+}
+
 VOS_STATUS hdd_register_interface( hdd_adapter_t *pAdapter, tANI_U8 rtnl_lock_held )
 {
    struct net_device *pWlanDev = pAdapter->dev;
@@ -11539,6 +11821,37 @@
 		break;
 	}
 
+	case WLAN_HDD_MONITOR:
+	{
+		adapter = hdd_alloc_monitor_adapter(hdd_ctx,
+						    mac_addr,
+						    name_assign_type,
+						    iface_name);
+
+		if (NULL == adapter) {
+			hddLog(VOS_TRACE_LEVEL_FATAL,
+			       FL("failed to allocate adapter for session %d"),
+			       session_type);
+			return NULL;
+		}
+
+		adapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
+		adapter->device_mode = session_type;
+		status = hdd_register_interface(adapter, rtnl_held);
+		if (VOS_STATUS_SUCCESS != status)
+			goto err_register_interface;
+
+		hdd_initialize_adapter_common(adapter);
+
+		/* Stop the Interface TX queue */
+		hddLog(LOG1, FL("Disabling queues"));
+		wlan_hdd_netif_queue_control(adapter,
+					     WLAN_NETIF_TX_DISABLE_N_CARRIER,
+					     WLAN_CONTROL_PATH);
+
+		break;
+	}
+
 	default:
 	{
 		hddLog(VOS_TRACE_LEVEL_FATAL, "%s Invalid session type %d",
@@ -12184,6 +12497,10 @@
             WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.staId[0]);
          break;
 
+      case WLAN_HDD_MONITOR:
+         hdd_vir_mon_stop(pAdapter->dev);
+         break;
+
       default:
          break;
    }
diff --git a/CORE/HDD/src/wlan_hdd_tx_rx.c b/CORE/HDD/src/wlan_hdd_tx_rx.c
index 27baaca..d484159 100644
--- a/CORE/HDD/src/wlan_hdd_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_tx_rx.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1254,6 +1254,64 @@
 	return VOS_STATUS_SUCCESS;
 }
 
+VOS_STATUS hdd_vir_mon_rx_cbk(v_VOID_t *vos_ctx, adf_nbuf_t rx_buf,
+			      uint8_t sta_id)
+{
+	hdd_adapter_t *adapter = NULL;
+	hdd_context_t *hdd_ctx = NULL;
+	int rxstat;
+	struct sk_buff *skb = NULL;
+	struct sk_buff *skb_next;
+
+	/* Sanity check on inputs */
+	if ((NULL == vos_ctx) || (NULL == rx_buf)) {
+		VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
+			  "%s: Null params being passed", __func__);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx);
+	if (NULL == hdd_ctx) {
+		VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
+			  "%s: HDD adapter context is Null", __func__);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_MONITOR);
+	if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
+		VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
+			  "invalid adapter %p for sta Id %d", adapter, sta_id);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	/* walk the chain until all are processed */
+	skb = (struct sk_buff *)rx_buf;
+	while (NULL != skb) {
+		skb_next = skb->next;
+		skb->dev = adapter->dev;
+
+		/*
+		 * If this is not a last packet on the chain
+		 * Just put packet into backlog queue, not scheduling RX sirq
+		 */
+		if (skb->next) {
+			rxstat = netif_rx(skb);
+		} else {
+			/*
+			 * This is the last packet on the chain
+			 * Scheduling rx sirq
+			 */
+			rxstat = netif_rx_ni(skb);
+		}
+
+		skb = skb_next;
+	}
+
+	adapter->dev->last_rx = jiffies;
+
+	return VOS_STATUS_SUCCESS;
+}
+
 /**
  * hdd_is_arp_local() - check if local or non local arp
  * @skb: pointer to sk_buff
diff --git a/CORE/SERVICES/COMMON/adf/adf_nbuf.c b/CORE/SERVICES/COMMON/adf/adf_nbuf.c
index 1f9cd2c..58a68c5 100644
--- a/CORE/SERVICES/COMMON/adf/adf_nbuf.c
+++ b/CORE/SERVICES/COMMON/adf/adf_nbuf.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1860,6 +1860,57 @@
 }
 
 /**
+ * adf_nbuf_construct_radiotap() - fill in the info into radiotap buf
+ *
+ * @rtap_buf: pointer to radiotap buffer
+ * @tsf: timestamp of packet
+ * @rssi_comb: rssi of packet
+ *
+ * Return: length of rtap_len updated.
+ */
+uint16_t adf_nbuf_construct_radiotap(
+		uint8_t *rtap_buf,
+		uint32_t tsf,
+		uint32_t rssi_comb)
+{
+	struct ieee80211_radiotap_header *rthdr =
+		(struct ieee80211_radiotap_header *)rtap_buf;
+	uint32_t rtap_hdr_len = sizeof(struct ieee80211_radiotap_header);
+	uint32_t rtap_len = rtap_hdr_len;
+
+	/* IEEE80211_RADIOTAP_TSFT              __le64       microseconds*/
+	rthdr->it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+	put_unaligned_le64((uint64_t)tsf,
+			   (void *)&rtap_buf[rtap_len]);
+	rtap_len += 8;
+
+	/* IEEE80211_RADIOTAP_FLAGS u8*/
+/*	rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_FLAGS);
+	rtap_buf[rtap_len] = 0x10;
+	rtap_len += 1; */
+
+	/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
+	rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+#define NORMALIZED_TO_NOISE_FLOOR (-96)
+	/*
+	 * rssi_comb is int dB, need to convert it to dBm.
+	 * normalize value to noise floor of -96 dBm
+	 */
+	rtap_buf[rtap_len] = rssi_comb +
+		NORMALIZED_TO_NOISE_FLOOR;
+	rtap_len += 1;
+
+	/* IEEE80211_RADIOTAP_DBM_ANTNOISE */
+	rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE);
+	rtap_buf[rtap_len] = NORMALIZED_TO_NOISE_FLOOR;
+	rtap_len += 1;
+
+	rthdr->it_len = cpu_to_le16(rtap_len);
+
+	return rthdr->it_len;
+}
+
+/**
  * __adf_nbuf_validate_skb_cb() - validate skb CB
  *
  * SKB control block size limit is 48 byte, add compile time
diff --git a/CORE/SERVICES/COMMON/adf/adf_nbuf.h b/CORE/SERVICES/COMMON/adf/adf_nbuf.h
index fefe556..0bf5a00 100644
--- a/CORE/SERVICES/COMMON/adf/adf_nbuf.h
+++ b/CORE/SERVICES/COMMON/adf/adf_nbuf.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -2091,6 +2091,20 @@
 			     u_int32_t headroom_sz);
 
 /**
+ * adf_nbuf_construct_radiotap() - fill in the info into radiotap buf
+ *
+ * @rtap_buf: pointer to radiotap buffer
+ * @tsf: timestamp of packet
+ * @rssi_comb: rssi of packet
+ *
+ * Return: length of rtap_len updated.
+ */
+uint16_t adf_nbuf_construct_radiotap(
+		uint8_t *rtap_buf,
+		uint32_t tsf,
+		uint32_t rssi_comb);
+
+/**
  * adf_nbuf_update_skb_mark() - update skb->mark.
  * @skb: Pointer to nbuf
  * @mask: the mask to set in skb->mark
diff --git a/CORE/SERVICES/COMMON/ol_htt_rx_api.h b/CORE/SERVICES/COMMON/ol_htt_rx_api.h
index 6073731..8cf7a45 100644
--- a/CORE/SERVICES/COMMON/ol_htt_rx_api.h
+++ b/CORE/SERVICES/COMMON/ol_htt_rx_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2015, 2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2015, 2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -892,4 +892,19 @@
     u_int8_t *fw_desc,
     adf_nbuf_t *head_buf,
     adf_nbuf_t *tail_buf);
+
+/**
+ * @brief parse the htt msg to get necessary info
+ * @param pdev - the HTT instance the rx data was received on
+ * @param rx_ind_msg - the nbuf pointer containing the htt msg content
+ * @param head_buf - reference to the head buffer
+ * @param tail_buf - reference to the tail buffer
+ */
+int
+htt_rx_mac_header_mon_process(
+	htt_pdev_handle pdev,
+	adf_nbuf_t rx_ind_msg,
+	adf_nbuf_t *head_msdu,
+	adf_nbuf_t *tail_msdu);
+
 #endif /* _OL_HTT_RX_API__H_ */
diff --git a/CORE/SERVICES/COMMON/ol_txrx_htt_api.h b/CORE/SERVICES/COMMON/ol_txrx_htt_api.h
index 516b387..941a692 100644
--- a/CORE/SERVICES/COMMON/ol_txrx_htt_api.h
+++ b/CORE/SERVICES/COMMON/ol_txrx_htt_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, 2016-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -834,4 +834,19 @@
 {
 }
 #endif
+
+/**
+ * @brief Process an rx indication message sent by the target.
+ * @details
+ *  The target send a rx indication message containing thw HW descriptor
+ *  info and a list of Mac header. HTT layer parse this message, get
+ *  each Mac header, radiotap header info to fill in monitor nbuf,
+ *  then deliver these nbuf to upper layer.
+ *
+ * @param pdev - the data physical device that received the frames
+ *      (registered with HTT as a context pointer during attach time)
+ * @param rx_ind_msg - the network buffer holding the rx indication message
+ */
+void
+ol_rx_mon_mac_header_handler(ol_txrx_pdev_handle pdev, adf_nbuf_t rx_ind_msg);
 #endif /* _OL_TXRX_HTT_API__H_ */
diff --git a/CORE/SERVICES/COMMON/ol_txrx_osif_api.h b/CORE/SERVICES/COMMON/ol_txrx_osif_api.h
index 3dc06aa..b770729 100644
--- a/CORE/SERVICES/COMMON/ol_txrx_osif_api.h
+++ b/CORE/SERVICES/COMMON/ol_txrx_osif_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, 2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012, 2014, 2017-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -98,6 +98,12 @@
  */
 typedef void (*ol_txrx_tx_flow_control_fp)(void *osif_dev,
                                            u_int8_t vdev_id, a_bool_t tx_resume);
+/**
+ * @typedef ol_txrx_vir_mon_rx_fp
+ * @brief receive function to hand montior frames from txrx to OS shim
+ * @param msdus - rx monitor frame(s), in a null-terminated list
+ */
+typedef void (*ol_txrx_vir_mon_rx_fp)(adf_nbuf_t msdus);
 
 /**
  * @typedef ol_txrx_rx_fp
@@ -208,6 +214,22 @@
     int max_seg_payload_bytes,
     adf_nbuf_t jumbo_tcp_frame);
 
+/**
+ * @brief register the TLSHIM montior callback to OL pdev context
+ * @details
+ * For the virtual monitor interface which real VDEV is not created case,
+ * just store the TLSHIM RX montior callback into TXRX_PDEV context.
+ * later when receive RX montior packets from HTT layer, call the callback
+ * stored in TXRX_PDEV to deliver packets to TLSHIM.
+ *
+ * @param txrx_pdev - TXRX physical device context
+ * @param cbk - TLSHIM montior callback to be registered
+ */
+void
+ol_txrx_osif_pdev_mon_register_cbk(
+	ol_txrx_pdev_handle txrx_pdev,
+	ol_txrx_vir_mon_rx_fp cbk);
+
 #ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK
 void
 ol_tx_vdev_set_driver_del_ack_enable(uint8_t vdev_id, unsigned long tx_packets,
diff --git a/CORE/SERVICES/COMMON/wma_api.h b/CORE/SERVICES/COMMON/wma_api.h
index 62fbbff..dc4b95b 100644
--- a/CORE/SERVICES/COMMON/wma_api.h
+++ b/CORE/SERVICES/COMMON/wma_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -79,6 +79,7 @@
     GEN_PARAM_MODULATED_DTIM,
     GEN_PARAM_CAPTURE_TSF,
     GEN_PARAM_RESET_TSF_GPIO,
+    GEN_PDEV_MONITOR_MODE,
 } GEN_PARAM;
 
 #define VDEV_CMD 1
diff --git a/CORE/SERVICES/WMA/wma.c b/CORE/SERVICES/WMA/wma.c
index 805a581..525ad40 100644
--- a/CORE/SERVICES/WMA/wma.c
+++ b/CORE/SERVICES/WMA/wma.c
@@ -16977,6 +16977,44 @@
 	}
 }
 
+static int32_t wma_send_pdev_monitor_mode_cmd(
+				tp_wma_handle wma,
+				wda_cli_set_cmd_t *privcmd)
+{
+	wmi_pdev_set_rx_filter_promiscuous_cmd_fixed_param *cmd;
+	wmi_buf_t buf;
+	int32_t len = sizeof(*cmd);
+
+	WMA_LOGD("Set pdev monitor mode value %d", privcmd->param_value);
+
+	buf = wmi_buf_alloc(wma->wmi_handle, len);
+	if (!buf) {
+		WMA_LOGP("%s: pdev monitor mode Mem Alloc Failed", __func__);
+		return -ENOMEM;
+	}
+
+	cmd = (wmi_pdev_set_rx_filter_promiscuous_cmd_fixed_param *)
+		wmi_buf_data(buf);
+	WMITLV_SET_HDR(&cmd->tlv_header,
+		       WMITLV_TAG_STRUC_wmi_pdev_set_rx_filter_promiscuous_cmd_fixed_param,
+		       WMITLV_GET_STRUCT_TLVLEN(
+			wmi_pdev_set_rx_filter_promiscuous_cmd_fixed_param));
+
+	cmd->pdev_id = 0; /* default 0, pdev id */
+	cmd->rx_filter_promiscuous_enable = privcmd->param_value;
+
+	if (wmi_unified_cmd_send(wma->wmi_handle, buf, len,
+				 WMI_PDEV_SET_RX_FILTER_PROMISCUOUS_CMDID)) {
+		WMA_LOGE("set pdev monitor mode failed, val %d",
+			 privcmd->param_value);
+
+		wmi_buf_free(buf);
+		return -EIO;
+	}
+
+	return 0;
+}
+
 static void wma_process_cli_set_cmd(tp_wma_handle wma,
 				    wda_cli_set_cmd_t *privcmd)
 {
@@ -17103,6 +17141,9 @@
 		case GEN_PARAM_MODULATED_DTIM:
 			wma_set_modulated_dtim(wma, privcmd);
 			break;
+		case GEN_PDEV_MONITOR_MODE:
+			wma_send_pdev_monitor_mode_cmd(wma, privcmd);
+			break;
 		default:
 			WMA_LOGE("Invalid param id 0x%x", privcmd->param_id);
 			break;
diff --git a/CORE/SME/src/csr/csrApiRoam.c b/CORE/SME/src/csr/csrApiRoam.c
index 0e0f619..779b112 100644
--- a/CORE/SME/src/csr/csrApiRoam.c
+++ b/CORE/SME/src/csr/csrApiRoam.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -18182,6 +18182,13 @@
       smsLog(pMac, LOGE, FL("Supplicant disabled driver roaming"));
       return eHAL_STATUS_FAILURE;
    }
+
+   if (vos_is_mon_enable() && (ROAM_SCAN_OFFLOAD_STOP != command)) {
+       VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+                 "%s: monitor is enabled, disable roaming", __func__);
+       return eHAL_STATUS_FAILURE;
+   }
+
 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
    if (pSession->roamOffloadSynchParams.bRoamSynchInProgress
        && (ROAM_SCAN_OFFLOAD_STOP == command))
diff --git a/CORE/SME/src/pmc/pmc.c b/CORE/SME/src/pmc/pmc.c
index dbd4df0..838f9e6 100644
--- a/CORE/SME/src/pmc/pmc.c
+++ b/CORE/SME/src/pmc/pmc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -3326,9 +3326,14 @@
      /* Call Full Power Req Cbs */
      pmcOffloadDoFullPowerCallbacks(pMac, sessionId, eHAL_STATUS_SUCCESS);
 
-     if (pmc->configStaPsEnabled || pmc->configDefStaPsEnabled)
+     if (pmc->configStaPsEnabled || pmc->configDefStaPsEnabled) {
+        if (true == vos_is_mon_enable()) {
+           smsLog(pMac, LOGE, FL("Montior is enabled, skip start StaPsTimer"));
+           return eHAL_STATUS_SUCCESS;
+        }
         pmcOffloadStartAutoStaPsTimer(pMac, sessionId,
                                       pmc->autoPsEntryTimerPeriod);
+        }
      else
         smsLog(pMac, LOGE, FL("Master Sta Ps Disabled"));
      return eHAL_STATUS_SUCCESS;
diff --git a/CORE/SME/src/pmc/pmcApi.c b/CORE/SME/src/pmc/pmcApi.c
index ee72e89..0bf93a9 100644
--- a/CORE/SME/src/pmc/pmcApi.c
+++ b/CORE/SME/src/pmc/pmcApi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -3626,7 +3626,8 @@
     tpPmcOffloadReqFullPowerEntry power_entry;
     tListElem *pEntry;
 
-    if (pmc->configStaPsEnabled) {
+    if (pmc->configStaPsEnabled ||
+        vos_is_mon_enable()) {
         if (callback_routine) {
             /* Allocate entry for Full Power Cb list. */
             power_entry = vos_mem_malloc(sizeof(*power_entry));
diff --git a/CORE/TL/inc/wlan_qct_tl.h b/CORE/TL/inc/wlan_qct_tl.h
index 4956f47..2665324 100644
--- a/CORE/TL/inc/wlan_qct_tl.h
+++ b/CORE/TL/inc/wlan_qct_tl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, 2016-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -2947,6 +2947,25 @@
 VOS_STATUS WLANTL_RegisterOCBPeer(void *vos_ctx, uint8_t *mac_addr,
     uint8_t *peer_id);
 
+/**
+ * tl_register_vir_mon_cb() - register the HDD monitor callback to TL.
+ * @vos_ctx: pointer to vos context
+ * @rxcb: HDD rx callback function
+ *
+ * Return:VOS_STATUS_SUCCESS on success, or others failure.
+ */
+VOS_STATUS
+tl_register_vir_mon_cb(void *vos_ctx, WLANTL_STARxCBType rxcb);
+
+/**
+ * tl_deregister_vir_mon_cb() - deregister the HDD monitor callback to TL.
+ * @vos_ctx: pointer to vos context
+ *
+ * Return:VOS_STATUS_SUCCESS on success, or others failure.
+ */
+VOS_STATUS
+tl_deregister_vir_mon_cb(void *vos_ctx);
+
 void WLANTL_display_datapath_stats(void *vos_ctx, uint16_t bitmap);
 void WLANTL_clear_datapath_stats(void *vos_ctx, uint16_t bitmap);
 
diff --git a/CORE/VOSS/inc/vos_api.h b/CORE/VOSS/inc/vos_api.h
index 2f6f9d9..dc7d90f 100644
--- a/CORE/VOSS/inc/vos_api.h
+++ b/CORE/VOSS/inc/vos_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -430,5 +430,12 @@
 VOS_STATUS vos_force_fw_dump(void);
 
 bool vos_is_probe_rsp_offload_enabled(void);
+/**
+ * vos_is_mon_enable - API to check if moniotr mode is on now.
+ *
+ * return - false: monitor mode is off
+ *          true: monitor mode is on
+ */
+bool vos_is_mon_enable(void);
 
 #endif // if !defined __VOS_API_H
diff --git a/CORE/VOSS/src/vos_api.c b/CORE/VOSS/src/vos_api.c
index b6622da..360237d 100644
--- a/CORE/VOSS/src/vos_api.c
+++ b/CORE/VOSS/src/vos_api.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -3299,3 +3299,24 @@
 
 	return pHddCtx->cfg_ini->sap_probe_resp_offload;
 }
+
+bool vos_is_mon_enable(void)
+{
+	hdd_context_t *phdd_ctx = NULL;
+
+	if (gpVosContext == NULL) {
+		VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
+			  "%s: global voss context is NULL", __func__);
+		return false;
+	}
+
+	phdd_ctx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD,
+						   gpVosContext);
+	if (!phdd_ctx) {
+		VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+			  "%s: HDD context is Null", __func__);
+		return false;
+	}
+
+	return phdd_ctx->is_mon_enable;
+}