summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManjunathappa Prakash <prakashp@qca.qualcomm.com>2015-12-26 10:41:47 -0800
committerAnjaneedevi Kapparapu <akappa@codeaurora.org>2016-02-03 17:27:59 +0530
commit4ff9447f4211f2bfe1e2d3594c0d294f0097702b (patch)
tree81c065fc2ce9bf26e9b80d826305521333275366
parent250a98255f0239a9cc771140895b98027fe0ac1f (diff)
qcacld-2.0: Add monitor mode support
Add monitor mode support. Configure target to deliver 802.11 packets in raw mode. Below is the procedure to start the monitor mode: insmod /system/lib/modules/wlan.ko con_mode=4 ifconfig wlan0 up iwpriv wlan0 setMonChan 36 2 tcpdump -i wlan0 -w <tcpdump.pcap> Change-Id: I211ece0a66e2d43bc111e523714942e1557e36f4 CRs-Fixed: 963060
-rw-r--r--CORE/CLD_TXRX/HTT/htt_h2t.c16
-rw-r--r--CORE/CLD_TXRX/HTT/htt_internal.h3
-rw-r--r--CORE/CLD_TXRX/HTT/htt_rx.c383
-rw-r--r--CORE/CLD_TXRX/TXRX/ol_rx.c4
-rw-r--r--CORE/CLD_TXRX/TXRX/ol_txrx.c10
-rw-r--r--CORE/CLD_TXRX/TXRX/ol_txrx_types.h3
-rw-r--r--CORE/HDD/inc/wlan_hdd_main.h16
-rw-r--r--CORE/HDD/inc/wlan_hdd_tx_rx.h16
-rw-r--r--CORE/HDD/src/wlan_hdd_cfg.c50
-rw-r--r--CORE/HDD/src/wlan_hdd_cfg80211.c21
-rw-r--r--CORE/HDD/src/wlan_hdd_main.c70
-rw-r--r--CORE/HDD/src/wlan_hdd_tx_rx.c82
-rw-r--r--CORE/HDD/src/wlan_hdd_wext.c44
-rw-r--r--CORE/MAC/inc/sirApi.h14
-rw-r--r--CORE/MAC/inc/wniApi.h1
-rw-r--r--CORE/MAC/src/pe/include/limApi.h9
-rw-r--r--CORE/MAC/src/pe/lim/limApi.c24
-rw-r--r--CORE/MAC/src/pe/lim/limProcessMessageQueue.c6
-rw-r--r--CORE/MAC/src/pe/lim/limSession.c6
-rw-r--r--CORE/SERVICES/COMMON/adf/adf_nbuf.c68
-rw-r--r--CORE/SERVICES/COMMON/adf/adf_nbuf.h38
-rw-r--r--CORE/SERVICES/COMMON/wlan_tgt_def_config.h4
-rw-r--r--CORE/SERVICES/WMA/wma.c161
-rw-r--r--CORE/SME/inc/sme_Api.h10
-rw-r--r--CORE/SME/src/sme_common/sme_Api.c29
25 files changed, 1043 insertions, 45 deletions
diff --git a/CORE/CLD_TXRX/HTT/htt_h2t.c b/CORE/CLD_TXRX/HTT/htt_h2t.c
index 362164895616..7de9e27dde23 100644
--- a/CORE/CLD_TXRX/HTT/htt_h2t.c
+++ b/CORE/CLD_TXRX/HTT/htt_h2t.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -52,6 +52,7 @@
#include <htt_internal.h>
+#include <vos_getBin.h>
#define HTT_MSG_BUF_SIZE(msg_bytes) \
((msg_bytes) + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING)
@@ -309,6 +310,19 @@ htt_h2t_rx_ring_cfg_msg_ll(struct htt_pdev_t *pdev)
enable_ppdu_start= 0;
enable_ppdu_end = 0;
#endif
+ if (VOS_MONITOR_MODE == vos_get_conparam()) {
+ enable_ctrl_data = 1;
+ enable_mgmt_data = 1;
+ enable_null_data = 1;
+ enable_phy_data = 1;
+ enable_hdr = 1;
+ enable_ppdu_start= 1;
+ enable_ppdu_end = 1;
+ /* Disable ASPM for monitor mode */
+ adf_os_print("Monitor mode is enabled\n");
+ htt_htc_disable_aspm();
+ }
+
HTT_RX_RING_CFG_ENABLED_802_11_HDR_SET(*msg_word, enable_hdr);
HTT_RX_RING_CFG_ENABLED_MSDU_PAYLD_SET(*msg_word, 1);
HTT_RX_RING_CFG_ENABLED_PPDU_START_SET(*msg_word, enable_ppdu_start);
diff --git a/CORE/CLD_TXRX/HTT/htt_internal.h b/CORE/CLD_TXRX/HTT/htt_internal.h
index 5be1655bda81..aa16980c369e 100644
--- a/CORE/CLD_TXRX/HTT/htt_internal.h
+++ b/CORE/CLD_TXRX/HTT/htt_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -259,6 +259,7 @@ htt_print_rx_desc(struct htt_host_rx_desc_base *rx_desc)
* rounded up to a cache line size.
*/
#define HTT_RX_BUF_SIZE 1920
+#define MAX_RX_PAYLOAD_SZ (HTT_RX_BUF_SIZE - RX_STD_DESC_SIZE)
/*
* DMA_MAP expects the buffer to be an integral number of cache lines.
* Rather than checking the actual cache line size, this code makes a
diff --git a/CORE/CLD_TXRX/HTT/htt_rx.c b/CORE/CLD_TXRX/HTT/htt_rx.c
index cac5afcbf260..469a11c61364 100644
--- a/CORE/CLD_TXRX/HTT/htt_rx.c
+++ b/CORE/CLD_TXRX/HTT/htt_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -68,6 +68,12 @@ extern int process_wma_set_command(int sessid, int paramid,
int sval, int vpdev);
#endif
+#ifdef PKT_DUMP
+#define HTT_PKT_DUMP(x) x
+#else
+#define HTT_PKT_DUMP(x) /* no-op */
+#endif
+
/* AR9888v1 WORKAROUND for EV#112367 */
/* FIX THIS - remove this WAR when the bug is fixed */
#define PEREGRINE_1_0_ZERO_LEN_PHY_ERR_WAR
@@ -1377,6 +1383,371 @@ htt_rx_offload_paddr_msdu_pop_ll(
return 0;
}
+/**
+ * htt_handle_amsdu_packet() - Handle consecutive fragments of amsdu
+ * @msdu: pointer to first msdu of amsdu
+ * @pdev: Handle to htt_pdev_handle
+ * @msg_word: Input and output variable, so pointer to HTT msg pointer
+ * @amsdu_len: remaining length of all N-1 msdu msdu's
+ *
+ * This function handles the (N-1) msdu's of amsdu, N'th msdu is already
+ * handled by calling function. N-1 msdu's are tied using frags_list.
+ * msdu_info field updated by FW indicates if this is last msdu. All the
+ * msdu's before last msdu will be of MAX payload.
+ *
+ * Return: 1 on success and 0 on failure.
+ */
+int
+htt_handle_amsdu_packet(adf_nbuf_t msdu,
+ htt_pdev_handle pdev,
+ uint32_t **msg_word,
+ uint32_t amsdu_len)
+{
+ adf_nbuf_t frag_nbuf;
+ adf_nbuf_t prev_frag_nbuf;
+ uint32_t len;
+ uint32_t last_frag;
+
+ *msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS;
+ frag_nbuf = htt_rx_in_order_netbuf_pop(pdev,
+ HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(**msg_word));
+ if (adf_os_unlikely(NULL == frag_nbuf)) {
+ adf_os_print("%s: netbuf pop failed!\n", __func__);
+ return 0;
+ }
+ last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu32_t *)*msg_word)->
+ msdu_info;
+ adf_nbuf_append_ext_list(msdu, frag_nbuf, amsdu_len);
+ adf_nbuf_set_pktlen(frag_nbuf, HTT_RX_BUF_SIZE);
+ adf_nbuf_unmap(pdev->osdev, frag_nbuf, ADF_OS_DMA_FROM_DEVICE);
+ /* For msdu's other than parent will not have htt_host_rx_desc_base */
+ len = MIN(amsdu_len, HTT_RX_BUF_SIZE);
+ amsdu_len -= len;
+ adf_nbuf_trim_tail(frag_nbuf, HTT_RX_BUF_SIZE - len);
+
+ HTT_PKT_DUMP(vos_trace_hex_dump(VOS_MODULE_ID_TXRX,
+ VOS_TRACE_LEVEL_FATAL,
+ adf_nbuf_data(frag_nbuf),
+ adf_nbuf_len(frag_nbuf)));
+ prev_frag_nbuf = frag_nbuf;
+ while (!last_frag) {
+ *msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS;
+ frag_nbuf = htt_rx_in_order_netbuf_pop(pdev,
+ HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(**msg_word));
+ last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu32_t *)
+ *msg_word)->msdu_info;
+
+ if (adf_os_unlikely(NULL == frag_nbuf)) {
+ adf_os_print("%s: netbuf pop failed!\n", __func__);
+ prev_frag_nbuf->next = NULL;
+ return 0;
+ }
+ adf_nbuf_set_pktlen(frag_nbuf, HTT_RX_BUF_SIZE);
+ adf_nbuf_unmap(pdev->osdev, frag_nbuf, ADF_OS_DMA_FROM_DEVICE);
+
+ len = MIN(amsdu_len, HTT_RX_BUF_SIZE);
+ amsdu_len -= len;
+ adf_nbuf_trim_tail(frag_nbuf, HTT_RX_BUF_SIZE - len);
+ HTT_PKT_DUMP(vos_trace_hex_dump(VOS_MODULE_ID_TXRX,
+ VOS_TRACE_LEVEL_FATAL,
+ adf_nbuf_data(frag_nbuf),
+ adf_nbuf_len(frag_nbuf)));
+
+ adf_nbuf_set_next(prev_frag_nbuf, frag_nbuf);
+ prev_frag_nbuf = frag_nbuf;
+ }
+ adf_nbuf_set_next(prev_frag_nbuf, NULL);
+ return 1;
+}
+#ifdef RX_HASH_DEBUG
+#define HTT_RX_CHECK_MSDU_COUNT(msdu_count) HTT_ASSERT_ALWAYS(msdu_count)
+#else
+#define HTT_RX_CHECK_MSDU_COUNT(msdu_count) /* no-op */
+#endif
+
+/**
+ * get_rate() - Get rate interms of 500Kbps extracted from htt_rx_desc
+ * @l_sig_rate_select: OFDM or CCK rate
+ * @l_sig_rate:
+ *
+ * If l_sig_rate_select is 0:
+ * 0x8: OFDM 48 Mbps
+ * 0x9: OFDM 24 Mbps
+ * 0xA: OFDM 12 Mbps
+ * 0xB: OFDM 6 Mbps
+ * 0xC: OFDM 54 Mbps
+ * 0xD: OFDM 36 Mbps
+ * 0xE: OFDM 18 Mbps
+ * 0xF: OFDM 9 Mbps
+ * If l_sig_rate_select is 1:
+ * 0x8: CCK 11 Mbps long preamble
+ * 0x9: CCK 5.5 Mbps long preamble
+ * 0xA: CCK 2 Mbps long preamble
+ * 0xB: CCK 1 Mbps long preamble
+ * 0xC: CCK 11 Mbps short preamble
+ * 0xD: CCK 5.5 Mbps short preamble
+ * 0xE: CCK 2 Mbps short preamble
+ *
+ * Return: rate interms of 500Kbps.
+ */
+static unsigned char get_rate(uint32_t l_sig_rate_select, uint32_t l_sig_rate)
+{
+ char ret = 0x0;
+
+ if (l_sig_rate_select == 0) {
+ switch (l_sig_rate) {
+ case 0x8:
+ ret = 0x60;
+ break;
+ case 0x9:
+ ret = 0x30;
+ break;
+ case 0xA:
+ ret = 0x18;
+ break;
+ case 0xB:
+ ret = 0x0c;
+ break;
+ case 0xC:
+ ret = 0x6c;
+ break;
+ case 0xD:
+ ret = 0x48;
+ break;
+ case 0xE:
+ ret = 0x24;
+ break;
+ case 0xF:
+ ret = 0x12;
+ break;
+ default:
+ break;
+ }
+ } else if (l_sig_rate_select == 1) {
+ switch (l_sig_rate) {
+ case 0x8:
+ ret = 0x16;
+ break;
+ case 0x9:
+ ret = 0x0B;
+ break;
+ case 0xA:
+ ret = 0x04;
+ break;
+ case 0xB:
+ ret = 0x02;
+ break;
+ case 0xC:
+ ret = 0x16;
+ break;
+ case 0xD:
+ ret = 0x0B;
+ break;
+ case 0xE:
+ ret = 0x04;
+ break;
+ default:
+ break;
+ }
+ } else {
+ adf_os_print("Invalid rate info\n");
+ }
+ return ret;
+}
+
+/**
+ * get_nr_antenna() - get number of antenna
+ * @rx_desc: pointer to htt_host_rx_desc_base.
+ *
+ * Return: number of antenna.
+ */
+unsigned char get_nr_antenna(struct htt_host_rx_desc_base *rx_desc)
+{
+ uint8_t preamble_type =
+ (uint8_t)rx_desc->ppdu_start.preamble_type;
+ uint8_t mcs, nss = 1;
+
+ switch (preamble_type) {
+ case 8:
+ case 9:
+ mcs = (uint8_t)(rx_desc->ppdu_start.ht_sig_vht_sig_a_1 & 0x7f);
+ nss = mcs>>3;
+ break;
+ case 0x0c: /* VHT w/o TxBF */
+ case 0x0d: /* VHT w/ TxBF */
+ mcs = (uint8_t)((rx_desc->ppdu_start.ht_sig_vht_sig_a_2
+ >> 4) & 0xf);
+ nss = (uint8_t)((rx_desc->ppdu_start.ht_sig_vht_sig_a_1
+ >> 10) & 0x7);
+ break;
+ default:
+ break;
+ }
+ return nss;
+}
+
+/**
+ * htt_get_radiotap_rx_status() - Update information about the rx status, which
+ * is used later for radiotap updation.
+ * @rx_desc: Pointer to struct htt_host_rx_desc_base
+ * @rx_status: Return variable updated with rx_status
+ *
+ * Return: None
+ */
+void htt_get_radiotap_rx_status(struct htt_host_rx_desc_base *rx_desc, struct
+ mon_rx_status *rx_status)
+{
+ uint16_t channel_flags = 0;
+
+ rx_status->tsft = (u_int64_t)rx_desc->ppdu_end.tsf_timestamp;
+ /* IEEE80211_RADIOTAP_F_FCS */
+ rx_status->flags |= 0x10;
+ rx_status->rate = get_rate(rx_desc->ppdu_start.l_sig_rate_select,
+ rx_desc->ppdu_start.l_sig_rate);
+ channel_flags |= rx_desc->ppdu_start.l_sig_rate_select?
+ IEEE80211_CHAN_CCK : IEEE80211_CHAN_OFDM;
+ rx_status->chan_flags = channel_flags;
+ rx_status->ant_signal_db = rx_desc->ppdu_start.rssi_comb;
+ rx_status->nr_ant = get_nr_antenna(rx_desc);
+}
+
+/**
+ * htt_rx_mon_amsdu_rx_in_order_pop_ll() - Monitor mode HTT Rx in order pop
+ * function
+ * @pdev: Handle to htt_pdev_handle
+ * @rx_ind_msg: In order indication message.
+ * @head_msdu: Return variable pointing to head msdu.
+ * @tail_msdu: Return variable pointing to tail msdu.
+ *
+ * This function pops the msdu based on paddr:length of inorder indication
+ * message.
+ *
+ * Return: 1 for sucess, 0 on failure.
+ */
+int
+htt_rx_mon_amsdu_rx_in_order_pop_ll(htt_pdev_handle pdev, adf_nbuf_t rx_ind_msg,
+ adf_nbuf_t *head_msdu,
+ adf_nbuf_t *tail_msdu)
+{
+ adf_nbuf_t msdu, next, prev = NULL;
+ uint8_t *rx_ind_data;
+ uint32_t *msg_word;
+ uint32_t msdu_count = 0;
+ struct htt_host_rx_desc_base *rx_desc;
+ struct mon_rx_status rx_status = {0};
+ uint32_t amsdu_len;
+ uint32_t len;
+ uint32_t last_frag;
+
+ HTT_ASSERT1(htt_rx_in_order_ring_elems(pdev) != 0);
+
+ rx_ind_data = adf_nbuf_data(rx_ind_msg);
+ msg_word = (uint32_t *)rx_ind_data;
+
+ HTT_PKT_DUMP(vos_trace_hex_dump(VOS_MODULE_ID_TXRX,
+ VOS_TRACE_LEVEL_FATAL,
+ (void *)rx_ind_data,
+ (int)adf_nbuf_len(rx_ind_msg)));
+
+ /* Get the total number of MSDUs */
+ msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1));
+ HTT_RX_CHECK_MSDU_COUNT(msdu_count);
+
+ msg_word = (uint32_t *)(rx_ind_data +
+ HTT_RX_IN_ORD_PADDR_IND_HDR_BYTES);
+
+ (*head_msdu) = msdu = htt_rx_in_order_netbuf_pop(pdev,
+ HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*msg_word));
+
+ if (adf_os_unlikely(NULL == msdu)) {
+ adf_os_print("%s: netbuf pop failed!\n", __func__);
+ *tail_msdu = NULL;
+ return 0;
+ }
+ while (msdu_count > 0) {
+
+ msdu_count--;
+ /*
+ * Set the netbuf length to be the entire buffer length
+ * initially, so the unmap will unmap the entire buffer.
+ */
+ adf_nbuf_set_pktlen(msdu, HTT_RX_BUF_SIZE);
+ adf_nbuf_unmap(pdev->osdev, msdu, ADF_OS_DMA_FROM_DEVICE);
+
+ /*
+ * cache consistency has been taken care of by the
+ * adf_nbuf_unmap
+ */
+ rx_desc = htt_rx_desc(msdu);
+ HTT_PKT_DUMP(htt_print_rx_desc(rx_desc));
+ /*
+ * Make the netbuf's data pointer point to the payload rather
+ * than the descriptor.
+ */
+ htt_get_radiotap_rx_status(rx_desc, &rx_status);
+ /*
+ * 250 bytes of RX_STD_DESC size should be sufficient for
+ * radiotap.
+ */
+ adf_nbuf_update_radiotap(&rx_status, msdu,
+ HTT_RX_STD_DESC_RESERVATION);
+ amsdu_len = HTT_RX_IN_ORD_PADDR_IND_MSDU_LEN_GET(*(msg_word
+ + 1));
+ /*
+ * MAX_RX_PAYLOAD_SZ when we have AMSDU packet. amsdu_len in
+ * which case is the total length of sum of all AMSDU's
+ */
+ len = MIN(amsdu_len, MAX_RX_PAYLOAD_SZ);
+ amsdu_len -= len;
+ adf_nbuf_trim_tail(msdu,
+ HTT_RX_BUF_SIZE -
+ (RX_STD_DESC_SIZE + len));
+
+
+ HTT_PKT_DUMP(vos_trace_hex_dump(VOS_MODULE_ID_TXRX,
+ VOS_TRACE_LEVEL_FATAL,
+ adf_nbuf_data(msdu),
+ adf_nbuf_len(msdu)));
+ last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu32_t *)
+ msg_word)->msdu_info;
+
+ /* Handle amsdu packet */
+ if (!last_frag) {
+ /*
+ * For AMSDU packet msdu->len is sum of all the msdu's
+ * length, msdu->data_len is sum of length's of
+ * remaining msdu's other than parent.
+ */
+ if (!htt_handle_amsdu_packet(msdu, pdev, &msg_word,
+ amsdu_len)) {
+ adf_os_print("%s: failed to handle amsdu packet\n",
+ __func__);
+ return 0;
+ }
+ }
+ /* check if this is the last msdu */
+ if (msdu_count) {
+ msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS;
+ next = htt_rx_in_order_netbuf_pop(pdev,
+ HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*msg_word));
+ if (adf_os_unlikely(NULL == next)) {
+ adf_os_print("%s: netbuf pop failed!\n",
+ __func__);
+ *tail_msdu = NULL;
+ return 0;
+ }
+ adf_nbuf_set_next(msdu, next);
+ prev = msdu;
+ msdu = next;
+ } else {
+ *tail_msdu = msdu;
+ adf_nbuf_set_next(msdu, NULL);
+ }
+ }
+
+ return 1;
+}
+/* Return values: 1 - success, 0 - failure */
int
htt_rx_offload_msdu_pop_hl(
htt_pdev_handle pdev,
@@ -1459,12 +1830,6 @@ htt_rx_offload_msdu_pop_hl(
return ret;
}
-#ifdef RX_HASH_DEBUG
-#define HTT_RX_CHECK_MSDU_COUNT(msdu_count) HTT_ASSERT_ALWAYS(msdu_count)
-#else
-#define HTT_RX_CHECK_MSDU_COUNT(msdu_count) /* no-op */
-#endif
-
/* Return values: 1 - success, 0 - failure */
int
htt_rx_amsdu_rx_in_order_pop_ll(
@@ -2843,6 +3208,10 @@ htt_rx_attach(struct htt_pdev_t *pdev)
htt_rx_frag_pop = htt_rx_amsdu_pop_ll;
htt_rx_mpdu_desc_list_next = htt_rx_mpdu_desc_list_next_ll;
}
+
+ if (VOS_MONITOR_MODE == vos_get_conparam())
+ htt_rx_amsdu_pop = htt_rx_mon_amsdu_rx_in_order_pop_ll;
+
htt_rx_offload_msdu_pop = htt_rx_offload_msdu_pop_ll;
htt_rx_mpdu_desc_retry = htt_rx_mpdu_desc_retry_ll;
htt_rx_mpdu_desc_seq_num = htt_rx_mpdu_desc_seq_num_ll;
diff --git a/CORE/CLD_TXRX/TXRX/ol_rx.c b/CORE/CLD_TXRX/TXRX/ol_rx.c
index 317e87e25729..64111791b269 100644
--- a/CORE/CLD_TXRX/TXRX/ol_rx.c
+++ b/CORE/CLD_TXRX/TXRX/ol_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1156,6 +1156,8 @@ ol_rx_in_order_indication_handler(
if (pdev) {
peer = ol_txrx_peer_find_by_id(pdev, peer_id);
+ if (VOS_MONITOR_MODE == vos_get_conparam())
+ peer = pdev->self_peer;
htt_pdev = pdev->htt_pdev;
} else {
TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
diff --git a/CORE/CLD_TXRX/TXRX/ol_txrx.c b/CORE/CLD_TXRX/TXRX/ol_txrx.c
index 48c328ff111d..f7892e6b7396 100644
--- a/CORE/CLD_TXRX/TXRX/ol_txrx.c
+++ b/CORE/CLD_TXRX/TXRX/ol_txrx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1394,6 +1394,14 @@ ol_txrx_peer_attach(
#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI
peer->rssi_dbm = HTT_RSSI_INVALID;
#endif
+ if ((VOS_MONITOR_MODE == vos_get_conparam()) && !pdev->self_peer) {
+ pdev->self_peer = peer;
+ /*
+ * No Tx in monitor mode, otherwise results in target assert.
+ * Setting disable_intrabss_fwd to true
+ */
+ ol_vdev_rx_set_intrabss_fwd(vdev, true);
+ }
OL_TXRX_LOCAL_PEER_ID_ALLOC(pdev, peer);
diff --git a/CORE/CLD_TXRX/TXRX/ol_txrx_types.h b/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
index 685dabb5a1bb..1cc772360204 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-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -834,6 +834,7 @@ struct ol_txrx_pdev_t {
unsigned int num_desc_pages;
unsigned int num_descs_per_page;
void **desc_pages;
+ struct ol_txrx_peer_t *self_peer;
};
struct ol_txrx_ocb_chan_info {
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 1d5940f92e99..f70919eed0c4 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -680,6 +680,20 @@ typedef enum{
HDD_SSR_DISABLED,
}e_hdd_ssr_required;
+/**
+ * struct hdd_mon_set_ch_info - Holds monitor mode channel switch params
+ * @channel: Channel number.
+ * @cb_mode: Channel bonding
+ * @channel_width: Channel width 0/1/2 for 20/40/80MHz respectively.
+ * @phy_mode: PHY mode
+ */
+struct hdd_mon_set_ch_info {
+ uint8_t channel;
+ uint8_t cb_mode;
+ uint32_t channel_width;
+ eCsrPhyMode phy_mode;
+};
+
struct hdd_station_ctx
{
/** Handle to the Wireless Extension State */
@@ -716,6 +730,8 @@ struct hdd_station_ctx
/* STA ctx debug variables */
int staDebugState;
+
+ struct hdd_mon_set_ch_info ch_info;
};
#define BSS_STOP 0
diff --git a/CORE/HDD/inc/wlan_hdd_tx_rx.h b/CORE/HDD/inc/wlan_hdd_tx_rx.h
index 43258edaf95a..0ec10eaf4944 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-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -270,4 +270,18 @@ static inline void wlan_hdd_log_eapol(struct sk_buff *skb,
}
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
+/**
+ * hdd_mon_rx_packet_cbk() - Receive callback registered with TL.
+ * @vosContext: [in] pointer to VOS context
+ * @staId: [in] Station Id
+ * @rxBuf: [in] pointer to rx adf_nbuf
+ *
+ * TL will call this to notify the HDD when one or more packets were
+ * received for a registered STA.
+ *
+ * Return: VOS_STATUS_E_FAILURE if any errors encountered, VOS_STATUS_SUCCESS
+ * otherwise
+ */
+VOS_STATUS hdd_mon_rx_packet_cbk(v_VOID_t *vos_ctx, adf_nbuf_t rx_buf,
+ uint8_t sta_id);
#endif // end #if !defined( WLAN_HDD_TX_RX_H )
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index a6ca5193377e..9232abb60c22 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -4419,6 +4419,53 @@ void dump_cfg_ini (tCfgIniEntry* iniTable, unsigned long entries)
}
#endif
+#ifdef FEATURE_RUNTIME_PM
+static void disable_runtime_pm(hdd_config_t *cfg_ini)
+{
+ cfg_ini->runtime_pm = 0;
+}
+#else
+static void disable_runtime_pm(hdd_config_t *cfg_ini)
+{
+}
+#endif
+
+#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
+static void disable_auto_shutdown(hdd_config_t *cfg_ini)
+{
+ cfg_ini->WlanAutoShutdown = 0;
+}
+#else
+static void disable_auto_shutdown(hdd_config_t *cfg_ini)
+{
+}
+#endif
+
+/**
+ * hdd_override_all_ps() - overrides to disables all the powersave features.
+ * @hdd_ctx: Pointer to HDD context.
+ *
+ * Overrides below powersave ini configurations.
+ * gEnableImps=0
+ * gEnableBmps=0
+ * gRuntimePM=0
+ * gWlanAutoShutdown = 0
+ * gEnableSuspend=0
+ * gEnablePowerSaveOffload=0
+ * gEnableWoW=0
+ */
+static void hdd_override_all_ps(hdd_context_t *hdd_ctx)
+{
+ hdd_config_t *cfg_ini = hdd_ctx->cfg_ini;
+
+ cfg_ini->fIsImpsEnabled = 0;
+ cfg_ini->fIsBmpsEnabled = 0;
+ disable_runtime_pm(cfg_ini);
+ disable_auto_shutdown(cfg_ini);
+ cfg_ini->enablePowersaveOffload = 0;
+ cfg_ini->wowEnable = 0;
+}
+
/*
* This function reads the qcom_cfg.ini file and
* parses each 'Name=Value' pair in the ini file
@@ -4513,6 +4560,9 @@ VOS_STATUS hdd_parse_config_ini(hdd_context_t* pHddCtx)
//Loop through the registry table and apply all these configs
vos_status = hdd_apply_cfg_ini(pHddCtx, cfgIniTable, i);
+ if (VOS_MONITOR_MODE == hdd_get_conparam())
+ hdd_override_all_ps(pHddCtx);
+
config_exit:
release_firmware(fw);
vos_mem_free(pTemp);
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 6e7891723545..da1d03e0c072 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -16643,10 +16643,16 @@ void hdd_select_cbmode(hdd_adapter_t *pAdapter, v_U8_t operationChannel,
v_U8_t iniDot11Mode =
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->dot11Mode;
eHddDot11Mode hddDot11Mode = iniDot11Mode;
+ hdd_station_ctx_t *station_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+ uint32_t cb_mode;
+ struct hdd_mon_set_ch_info *ch_info = &station_ctx->ch_info;
+
hddLog(LOG1, FL("Channel Bonding Mode Selected is %u"),
iniDot11Mode);
- *vht_channel_width =
- (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->vhtChannelWidth;
+ if (VOS_MONITOR_MODE != hdd_get_conparam())
+ *vht_channel_width =
+ (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->vhtChannelWidth;
+
switch ( iniDot11Mode )
{
case eHDD_DOT11_MODE_AUTO:
@@ -16670,11 +16676,20 @@ void hdd_select_cbmode(hdd_adapter_t *pAdapter, v_U8_t operationChannel,
break;
}
/* This call decides required channel bonding mode */
- sme_SelectCBMode((WLAN_HDD_GET_CTX(pAdapter)->hHal),
+ cb_mode = sme_SelectCBMode((WLAN_HDD_GET_CTX(pAdapter)->hHal),
hdd_cfg_xlate_to_csr_phy_mode(hddDot11Mode),
operationChannel, 0,
vht_channel_width,
*vht_channel_width);
+
+ if (VOS_MONITOR_MODE == hdd_get_conparam()) {
+ ch_info->channel_width = *vht_channel_width;
+ ch_info->phy_mode = hdd_cfg_xlate_to_csr_phy_mode(hddDot11Mode);
+ ch_info->channel = operationChannel;
+ ch_info->cb_mode = cb_mode;
+ hddLog(LOG1, FL("ch_info width %d, phymode %d channel %d"),
+ ch_info->channel_width, ch_info->phy_mode, ch_info->channel);
+ }
}
/**
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 4fab753540c7..f97a0991433c 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -8965,8 +8965,42 @@ static int hdd_open(struct net_device *dev)
*/
static int __hdd_mon_open(struct net_device *dev)
{
- netif_start_queue(dev);
- return 0;
+ hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ int ret;
+ VOS_STATUS vos_status;
+ eHalStatus hal_status;
+ WLAN_STADescType sta_desc = {0};
+
+ 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;
+
+ /* peer is created wma_vdev_attach->wma_create_peer */
+ vos_status = WLANTL_RegisterSTAClient(hdd_ctx->pvosContext,
+ hdd_mon_rx_packet_cbk,
+ &sta_desc, 0);
+ if (VOS_STATUS_SUCCESS != vos_status) {
+ hddLog(LOGE,
+ FL("WLANTL_RegisterSTAClient() failed to register. Status= %d [0x%08X]"),
+ vos_status, vos_status);
+ goto exit;
+ }
+
+ hal_status = sme_create_mon_session(hdd_ctx->hHal,
+ adapter->macAddressCurrent.bytes);
+ if (eHAL_STATUS_SUCCESS != hal_status) {
+ hddLog(LOGE,
+ FL("sme_create_mon_session() failed to register. Status= %d [0x%08X]"),
+ hal_status, hal_status);
+ goto exit;
+ }
+ return ret;
+exit:
+ return -EIO;
}
/**
@@ -9814,18 +9848,30 @@ static struct net_device_ops wlan_drv_ops = {
static struct net_device_ops wlan_mon_drv_ops = {
.ndo_open = hdd_mon_open,
.ndo_stop = hdd_stop,
- .ndo_uninit = hdd_uninit,
- .ndo_start_xmit = hdd_mon_hard_start_xmit,
- .ndo_tx_timeout = hdd_tx_timeout,
.ndo_get_stats = hdd_stats,
- .ndo_do_ioctl = hdd_ioctl,
- .ndo_set_mac_address = hdd_set_mac_address,
};
void hdd_set_station_ops( struct net_device *pWlanDev )
{
- pWlanDev->netdev_ops = &wlan_drv_ops;
+ if (VOS_MONITOR_MODE == hdd_get_conparam())
+ pWlanDev->netdev_ops = &wlan_mon_drv_ops;
+ else
+ pWlanDev->netdev_ops = &wlan_drv_ops;
+}
+
+static void mon_mode_ether_setup(struct net_device *dev)
+{
+ dev->header_ops = NULL;
+ dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = ETH_DATA_LEN;
+ dev->addr_len = ETH_ALEN;
+ dev->tx_queue_len = 1000; /* Ethernet wants good queues */
+ dev->flags = IFF_BROADCAST|IFF_MULTICAST;
+ dev->priv_flags |= IFF_TX_SKB_SHARING;
+
+ memset(dev->broadcast, 0xFF, ETH_ALEN);
}
#ifdef FEATURE_RUNTIME_PM
@@ -9921,7 +9967,8 @@ static hdd_adapter_t* hdd_alloc_station_adapter( hdd_context_t *pHddCtx, tSirMac
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)) || defined(WITH_BACKPORTS)
NET_NAME_UNKNOWN,
#endif
- ether_setup,
+ (VOS_MONITOR_MODE == vos_get_conparam()?
+ mon_mode_ether_setup : ether_setup),
NUM_TX_QUEUES);
if(pWlanDev != NULL)
@@ -10798,6 +10845,11 @@ hdd_adapter_t* hdd_open_adapter( hdd_context_t *pHddCtx, tANI_U8 session_type,
pAdapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
pAdapter->device_mode = session_type;
+
+ status = hdd_init_station_mode( pAdapter );
+ if (VOS_STATUS_SUCCESS != status)
+ goto err_free_netdev;
+
status = hdd_register_interface( pAdapter, rtnl_held );
pAdapter->dev->netdev_ops = &wlan_mon_drv_ops;
hdd_init_tx_rx( pAdapter );
diff --git a/CORE/HDD/src/wlan_hdd_tx_rx.c b/CORE/HDD/src/wlan_hdd_tx_rx.c
index 6e93add9f8ff..eee773d62cf9 100644
--- a/CORE/HDD/src/wlan_hdd_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_tx_rx.c
@@ -1375,6 +1375,88 @@ bool drop_ip6_mcast(struct sk_buff *skb)
#define drop_ip6_mcast(_a) 0
#endif
+/**
+ * hdd_mon_rx_packet_cbk() - Receive callback registered with TLSHIM.
+ * @vosContext: [in] pointer to VOS context
+ * @staId: [in] Station Id
+ * @rxBuf: [in] pointer to rx adf_nbuf
+ *
+ * TL will call this to notify the HDD when one or more packets were
+ * received for a registered STA.
+ * Return: VOS_STATUS_E_FAILURE if any errors encountered, VOS_STATUS_SUCCESS
+ * otherwise
+ */
+VOS_STATUS hdd_mon_rx_packet_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;
+ unsigned int cpu_index;
+ hdd_adapter_list_node_t *adapter_node = NULL;
+
+ /* 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;
+ }
+
+ hdd_get_front_adapter(hdd_ctx, &adapter_node);
+ adapter = adapter_node->pAdapter;
+ 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;
+ }
+
+ cpu_index = wlan_hdd_get_cpu();
+
+ /* walk the chain until all are processed */
+ skb = (struct sk_buff *) rx_buf;
+ while (NULL != skb) {
+ skb_next = skb->next;
+ skb->dev = adapter->dev;
+
+ ++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index];
+ ++adapter->stats.rx_packets;
+ adapter->stats.rx_bytes += skb->len;
+ /*
+ * 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);
+ }
+
+ if (NET_RX_SUCCESS == rxstat)
+ ++adapter->
+ hdd_stats.hddTxRxStats.rxDelivered[cpu_index];
+ else
+ ++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index];
+
+ skb = skb_next;
+ }
+
+ adapter->dev->last_rx = jiffies;
+
+ return VOS_STATUS_SUCCESS;
+}
/**============================================================================
@brief hdd_rx_packet_cbk() - Receive callback registered with TL.
TL will call this to notify the HDD when one or more packets were
diff --git a/CORE/HDD/src/wlan_hdd_wext.c b/CORE/HDD/src/wlan_hdd_wext.c
index 1e22425dde67..bf2c4edf2168 100644
--- a/CORE/HDD/src/wlan_hdd_wext.c
+++ b/CORE/HDD/src/wlan_hdd_wext.c
@@ -455,6 +455,7 @@ static const hdd_freq_chan_map_t freq_chan_map[] = { {2412, 1}, {2417, 2},
#ifdef DEBUG
#define WE_SET_FW_CRASH_INJECT 2
#endif
+#define WE_SET_MON_MODE_CHAN 3
#define WLAN_STATS_INVALID 0
#define WLAN_STATS_RETRY_CNT 1
@@ -11242,6 +11243,46 @@ static int __iw_set_two_ints_getnone(struct net_device *dev,
value[1], value[2], GEN_CMD);
break;
#endif
+ case WE_SET_MON_MODE_CHAN:
+ /*
+ * TODO: Remove this private implementation use standard
+ * interface wlan_hdd_cfg80211_ops.set_monitor_channel
+ */
+ if (VOS_MONITOR_MODE == hdd_get_conparam()) {
+ uint16_t vht_channel_width = value[2];
+ hdd_station_ctx_t *sta_ctx =
+ WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+ struct hdd_mon_set_ch_info *ch_info = &sta_ctx->ch_info;
+ eHalStatus hal_status;
+
+ tHalHandle hal_hdl = hdd_ctx->hHal;
+ tCsrBssid bssid = {0};
+ tCsrRoamProfile roam_profile;
+
+ hddLog(LOG1, "Set monitor mode Channel %d", value[1]);
+ hdd_select_cbmode(pAdapter, value[1], &vht_channel_width);
+ roam_profile.ChannelInfo.ChannelList = &ch_info->channel;
+ roam_profile.ChannelInfo.numOfChannels = 1;
+ roam_profile.vht_channel_width = ch_info->channel_width;
+ roam_profile.phyMode = ch_info->phy_mode;
+
+ vos_mem_copy(bssid, pAdapter->macAddressCurrent.bytes,
+ VOS_MAC_ADDR_SIZE);
+
+ hal_status = sme_RoamChannelChangeReq(hal_hdl,
+ bssid,
+ ch_info->cb_mode, &roam_profile);
+ if (!HAL_STATUS_SUCCESS(hal_status)) {
+ hddLog(LOGE,
+ "Failed to set sme_RoamChannel for monitor mode");
+ ret = -EINVAL;
+ }
+ } else {
+ hddLog(LOGE, "Not supported, device is not in monitor mode");
+ ret = -EINVAL;
+ }
+ break;
+
default:
hddLog(LOGE, "%s: Invalid IOCTL command %d", __func__, sub_cmd);
break;
@@ -12432,6 +12473,9 @@ static const struct iw_priv_args we_private_args[] = {
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
0, "crash_inject" },
#endif
+ { WE_SET_MON_MODE_CHAN,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
+ 0, "setMonChan" },
};
diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h
index abb128253a2f..35196bd34358 100644
--- a/CORE/MAC/inc/sirApi.h
+++ b/CORE/MAC/inc/sirApi.h
@@ -538,6 +538,7 @@ typedef enum eSirBssType
eSIR_BTAMP_STA_MODE, //Added for BT-AMP support
eSIR_BTAMP_AP_MODE, //Added for BT-AMP support
eSIR_AUTO_MODE,
+ eSIR_MONITOR_MODE,
eSIR_DONOT_USE_BSS_TYPE = SIR_MAX_ENUM_SIZE
} tSirBssType;
@@ -3297,6 +3298,19 @@ typedef struct sSirUpdateParams
tANI_U8 ssidHidden; // Hide SSID
} tSirUpdateParams, *tpSirUpdateParams;
+/**
+ * struct sir_create_session - Used for creating session in monitor mode
+ * @type: SME host message type.
+ * @msg_len: Length of the message.
+ * @bss_id: bss_id for creating the session.
+ */
+struct sir_create_session
+{
+ uint16_t type;
+ uint16_t msg_len;
+ tSirMacAddr bss_id;
+};
+
//Beacon Interval
typedef struct sSirChangeBIParams
{
diff --git a/CORE/MAC/inc/wniApi.h b/CORE/MAC/inc/wniApi.h
index 71b3b55f3ffe..051d6c09c489 100644
--- a/CORE/MAC/inc/wniApi.h
+++ b/CORE/MAC/inc/wniApi.h
@@ -402,6 +402,7 @@ enum eWniMsgTypes
eWNI_SME_ROAM_RESTART_REQ,
eWNI_SME_SMPS_FORCE_MODE_IND,
eWNI_SME_REGISTER_MGMT_FRAME_CB,
+ eWNI_SME_MON_INIT_SESSION,
eWNI_SME_MSG_TYPES_END
};
diff --git a/CORE/MAC/src/pe/include/limApi.h b/CORE/MAC/src/pe/include/limApi.h
index 45a6e1cb3e39..3728b732fae5 100644
--- a/CORE/MAC/src/pe/include/limApi.h
+++ b/CORE/MAC/src/pe/include/limApi.h
@@ -184,6 +184,15 @@ void limMicFailureInd(tpAniSirGlobal pMac, tpSirMsgQ pMsg);
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
void limRoamOffloadSynchInd(tpAniSirGlobal pMac, tpSirMsgQ pMsg);
#endif
+/**
+ * lim_mon_init_session() - create PE session for monitor mode operation
+ * @mac_ptr: mac pointer
+ * @msg: Pointer to struct sir_create_session type.
+ *
+ * Return: NONE
+ */
+void lim_mon_init_session(tpAniSirGlobal mac_ptr,
+ struct sir_create_session *msg);
void lim_update_lost_link_info(tpAniSirGlobal mac, tpPESession session,
int8_t rssi);
/* ----------------------------------------------------------------------- */
diff --git a/CORE/MAC/src/pe/lim/limApi.c b/CORE/MAC/src/pe/lim/limApi.c
index 51cd158052c6..4911fa433f7c 100644
--- a/CORE/MAC/src/pe/lim/limApi.c
+++ b/CORE/MAC/src/pe/lim/limApi.c
@@ -2378,6 +2378,30 @@ eHalStatus limRoamFillBssDescr(tpAniSirGlobal pMac,
return eHAL_STATUS_SUCCESS;
}
+/**
+ * lim_mon_init_session() - create PE session for monitor mode operation
+ * @mac_ptr: mac pointer
+ * @msg: Pointer to struct sir_create_session type.
+ *
+ * Return: NONE
+ */
+void lim_mon_init_session(tpAniSirGlobal mac_ptr,
+ struct sir_create_session *msg)
+{
+ tpPESession psession_entry;
+ uint8_t session_id;
+
+ if((psession_entry = peCreateSession(mac_ptr, msg->bss_id,
+ &session_id, mac_ptr->lim.maxStation,
+ eSIR_MONITOR_MODE)) == NULL) {
+ limLog(mac_ptr, LOGE,
+ FL("Monitor mode: Session Can not be created"));
+ limPrintMacAddr(mac_ptr, msg->bss_id, LOGE);
+ return;
+ }
+ psession_entry->vhtCapability = 1;
+}
+
/** -----------------------------------------------------------------
* brief limRoamOffloadSynchInd() - Handles Roam Synch Indication
* param pMac - global mac structure
diff --git a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
index cd6eb84e29e2..fba9c123134f 100644
--- a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
+++ b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
@@ -1619,6 +1619,12 @@ limProcessMessages(tpAniSirGlobal pMac, tpSirMsgQ limMsg)
limMsg->bodyptr = NULL;
break;
+ case eWNI_SME_MON_INIT_SESSION:
+ lim_mon_init_session(pMac, limMsg->bodyptr);
+ vos_mem_free(limMsg->bodyptr);
+ limMsg->bodyptr = NULL;
+ break;
+
//Power Save Related Messages From HAL
case WDA_ENTER_BMPS_RSP:
case WDA_EXIT_BMPS_RSP:
diff --git a/CORE/MAC/src/pe/lim/limSession.c b/CORE/MAC/src/pe/lim/limSession.c
index 312e795cfe5e..4b0792ff8bf0 100644
--- a/CORE/MAC/src/pe/lim/limSession.c
+++ b/CORE/MAC/src/pe/lim/limSession.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -296,6 +296,8 @@ tpPESession peCreateSession(tpAniSirGlobal pMac,
/* Copy the BSSID to the session table */
sirCopyMacAddr(pMac->lim.gpSession[i].bssId, bssid);
+ if (bssType == eSIR_MONITOR_MODE)
+ sirCopyMacAddr(pMac->lim.gpSession[i].selfMacAddr, bssid);
pMac->lim.gpSession[i].valid = TRUE;
/* Initialize the SME and MLM states to IDLE */
@@ -388,6 +390,8 @@ tpPESession peCreateSession(tpAniSirGlobal pMac,
limFTOpen(pMac, &pMac->lim.gpSession[i]);
}
#endif
+ if (eSIR_MONITOR_MODE == bssType)
+ limFTOpen(pMac, &pMac->lim.gpSession[i]);
if (eSIR_INFRA_AP_MODE == bssType) {
pMac->lim.gpSession[i].old_protection_state = 0;
diff --git a/CORE/SERVICES/COMMON/adf/adf_nbuf.c b/CORE/SERVICES/COMMON/adf/adf_nbuf.c
index 8a97c4c1daaf..b40a6b1a3ba5 100644
--- a/CORE/SERVICES/COMMON/adf/adf_nbuf.c
+++ b/CORE/SERVICES/COMMON/adf/adf_nbuf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -33,6 +33,7 @@
#include <adf_os_types.h>
#include <adf_nbuf.h>
#include <adf_os_io.h>
+#include <net/ieee80211_radiotap.h>
#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
#include <net/cnss_prealloc.h>
@@ -541,6 +542,71 @@ __adf_nbuf_trace_update(struct sk_buff *buf, char *event_string)
}
#endif /* QCA_PKT_PROTO_TRACE */
+/**
+ * adf_nbuf_update_radiotap() - Update radiotap header from rx_status
+ *
+ * @rx_status: Pointer to rx_status.
+ * @nbuf: nbuf pointe to which radiotap has to be updated
+ * @headroom_sz: Available headroom size.
+ *
+ * Return: length of rtap_len updated.
+ */
+int adf_nbuf_update_radiotap(struct mon_rx_status *rx_status, adf_nbuf_t nbuf,
+ u_int32_t headroom_sz)
+{
+ uint8_t rtap_buf[sizeof(struct ieee80211_radiotap_header) + 100] = {0};
+ 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(rx_status->tsft,
+ (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] = rx_status->flags;
+ rtap_len += 1;
+
+ /* IEEE80211_RADIOTAP_RATE u8 500kb/s*/
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
+ rtap_buf[rtap_len] = rx_status->rate;
+ rtap_len += 1;
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
+ /* IEEE80211_RADIOTAP_CHANNEL, Channel frequency in Mhz */
+ put_unaligned_le16(rx_status->chan, (void *)&rtap_buf[rtap_len]);
+ rtap_len += 2;
+ /* Channel flags. */
+
+ put_unaligned_le16(rx_status->chan_flags, (void *)&rtap_buf[rtap_len]);
+ rtap_len += 2;
+
+ /* 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] = rx_status->ant_signal_db +
+ NORMALIZED_TO_NOISE_FLOOR;
+ rtap_len += 1;
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_ANTENNA);
+ rtap_buf[rtap_len] = rx_status->nr_ant;
+ rtap_len += 1;
+
+ rthdr->it_len = cpu_to_le16(rtap_len);
+
+ adf_nbuf_pull_head(nbuf, headroom_sz - rtap_len);
+ adf_os_mem_copy(adf_nbuf_data(nbuf), rthdr, rtap_hdr_len);
+ adf_os_mem_copy(adf_nbuf_data(nbuf) + rtap_hdr_len, rtap_buf +
+ rtap_hdr_len, rtap_len - rtap_hdr_len);
+ return rtap_len;
+}
+
EXPORT_SYMBOL(__adf_nbuf_alloc);
#ifdef QCA_ARP_SPOOFING_WAR
EXPORT_SYMBOL(__adf_rx_nbuf_alloc);
diff --git a/CORE/SERVICES/COMMON/adf/adf_nbuf.h b/CORE/SERVICES/COMMON/adf/adf_nbuf.h
index 7b49f0d8b0d5..8d30142acf62 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 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -62,6 +62,29 @@
#define ADF_NBUF_TRAC_EAPOL_ETH_TYPE 0x888E
/**
+ * struct mon_rx_status - This will have monitor mode rx_status extracted from
+ * htt_rx_desc used later to update radiotap information.
+ * @tsft: Time Synchronization Function timer
+ * @chan: Channel frequency
+ * @chan_flags: Bitmap of Channel flags, IEEE80211_CHAN_TURBO,
+ * IEEE80211_CHAN_CCK...
+ * @flags: For IEEE80211_RADIOTAP_FLAGS
+ * @rate: Rate in terms 500Kbps
+ * @ant_signal_db: Rx packet RSSI
+ * @nr_ant: Number of Antennas used for streaming
+ */
+
+struct mon_rx_status {
+ uint64_t tsft;
+ uint16_t chan;
+ uint16_t chan_flags;
+ uint8_t flags;
+ uint8_t rate;
+ uint8_t ant_signal_db;
+ uint8_t nr_ant;
+};
+
+/**
* @brief Platform indepedent packet abstraction
*/
typedef __adf_nbuf_t adf_nbuf_t;
@@ -1191,4 +1214,17 @@ adf_nbuf_is_eapol_pkt(adf_nbuf_t buf)
{
return (__adf_nbuf_is_eapol_pkt(buf));
}
+
+/**
+ * adf_nbuf_update_radiotap() - update radiotap at head of nbuf.
+ * @rx_status: rx_status containing required info to update radiotap
+ * @nbuf: Pointer to nbuf
+ * @headroom_sz: Available headroom size
+ *
+ * Return: radiotap length.
+ */
+int adf_nbuf_update_radiotap(struct mon_rx_status *rx_status, adf_nbuf_t nbuf,
+ u_int32_t headroom_sz);
+
+
#endif
diff --git a/CORE/SERVICES/COMMON/wlan_tgt_def_config.h b/CORE/SERVICES/COMMON/wlan_tgt_def_config.h
index 8989f03207e7..5a05705820a7 100644
--- a/CORE/SERVICES/COMMON/wlan_tgt_def_config.h
+++ b/CORE/SERVICES/COMMON/wlan_tgt_def_config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -125,6 +125,8 @@
#define CFG_TGT_RX_DECAP_MODE (0x2)
/* Decap to native Wifi header */
#define CFG_TGT_RX_DECAP_MODE_NWIFI (0x1)
+/* Decap to raw mode header */
+#define CFG_TGT_RX_DECAP_MODE_RAW (0x0)
/* maximum number of pending scan requests */
#define CFG_TGT_DEFAULT_SCAN_MAX_REQS 0x4
diff --git a/CORE/SERVICES/WMA/wma.c b/CORE/SERVICES/WMA/wma.c
index 56ec1c3fd8d4..800c4d8f668c 100644
--- a/CORE/SERVICES/WMA/wma.c
+++ b/CORE/SERVICES/WMA/wma.c
@@ -1324,8 +1324,10 @@ static int wma_vdev_start_rsp_ind(tp_wma_handle wma, u_int8_t *buf)
params->chainMask = resp_event->chain_mask;
params->smpsMode = host_map_smps_mode(resp_event->smps_mode);
params->status = resp_event->status;
- if (resp_event->resp_type == WMI_VDEV_RESTART_RESP_EVENT &&
- (iface->type == WMI_VDEV_TYPE_STA)) {
+ if (((resp_event->resp_type == WMI_VDEV_RESTART_RESP_EVENT) &&
+ (iface->type == WMI_VDEV_TYPE_STA)) ||
+ ((resp_event->resp_type == WMI_VDEV_START_RESP_EVENT) &&
+ (iface->type == WMI_VDEV_TYPE_MONITOR))) {
err = wma_set_peer_param(wma, iface->bssid,
WMI_PEER_PHYMODE, iface->chanmode,
@@ -1555,6 +1557,9 @@ static v_VOID_t wma_set_default_tgt_config(tp_wma_handle wma_handle)
tgt_cfg.rx_decap_mode = CFG_TGT_RX_DECAP_MODE_NWIFI;
}
#endif
+ if (VOS_MONITOR_MODE == vos_get_conparam())
+ tgt_cfg.rx_decap_mode = CFG_TGT_RX_DECAP_MODE_RAW;
+
wma_handle->wlan_resource_config = tgt_cfg;
}
@@ -7234,6 +7239,8 @@ enum wlan_op_mode wma_get_txrx_vdev_type(u_int32_t type)
vdev_type = wlan_op_mode_ocb;
break;
case WMI_VDEV_TYPE_MONITOR:
+ vdev_type = wlan_op_mode_monitor;
+ break;
default:
WMA_LOGE("Invalid vdev type %u", type);
vdev_type = wlan_op_mode_unknown;
@@ -8034,6 +8041,9 @@ static ol_txrx_vdev_handle wma_vdev_attach(tp_wma_handle wma_handle,
goto end;
}
+ if (VOS_MONITOR_MODE == vos_get_conparam())
+ self_sta_req->type = WMI_VDEV_TYPE_MONITOR;
+
/* Create a vdev in target */
if (wma_unified_vdev_create_send(wma_handle->wmi_handle,
self_sta_req->sessionId,
@@ -8132,7 +8142,8 @@ static ol_txrx_vdev_handle wma_vdev_attach(tp_wma_handle wma_handle,
if (((self_sta_req->type == WMI_VDEV_TYPE_AP) &&
(self_sta_req->subType ==
WMI_UNIFIED_VDEV_SUBTYPE_P2P_DEVICE))
- || (self_sta_req->type == WMI_VDEV_TYPE_OCB)) {
+ || (self_sta_req->type == WMI_VDEV_TYPE_OCB) ||
+ (self_sta_req->type == WMI_VDEV_TYPE_MONITOR)) {
WMA_LOGA("P2P Device: creating self peer %pM, vdev_id %hu",
self_sta_req->selfMacAddr,
self_sta_req->sessionId);
@@ -11577,7 +11588,113 @@ tANI_U8 wma_getCenterChannel(tANI_U8 chan, tANI_U8 chan_offset)
return band_center_chan;
}
+/**
+ * wma_switch_channel() - WMA api to switch channel dynamically
+ * @wma: Pointer of WMA context
+ * @req: Pointer vdev_start having channel switch info.
+ *
+ * Return: 0 for success, otherwise appropriate error code
+ */
+VOS_STATUS wma_switch_channel(tp_wma_handle wma, struct wma_vdev_start_req *req)
+{
+
+ wmi_buf_t buf;
+ wmi_channel *cmd;
+ int32_t len, ret;
+ WLAN_PHY_MODE chanmode;
+ struct wma_txrx_node *intr = wma->interfaces;
+ tpAniSirGlobal pmac;
+ uint8_t *buf_ptr;
+
+ pmac = vos_get_context(VOS_MODULE_ID_PE, wma->vos_context);
+
+ if (pmac == NULL) {
+ WMA_LOGE("%s: vdev start failed as pmac is NULL", __func__);
+ return VOS_STATUS_E_FAILURE;
+ }
+ len = sizeof(*cmd);
+ buf = wmi_buf_alloc(wma->wmi_handle, len);
+ if (!buf) {
+ WMA_LOGE("%s : wmi_buf_alloc failed", __func__);
+ return VOS_STATUS_E_NOMEM;
+ }
+ buf_ptr = (u_int8_t *) wmi_buf_data(buf);
+ cmd = (wmi_channel *) buf_ptr;
+ WMITLV_SET_HDR(&cmd->tlv_header,
+ WMITLV_TAG_STRUC_wmi_channel,
+ WMITLV_GET_STRUCT_TLVLEN(wmi_channel));
+
+ /* Fill channel info */
+ cmd->mhz = vos_chan_to_freq(req->chan);
+ chanmode = wma_chan_to_mode(req->chan, req->chan_offset,
+ req->vht_capable, req->dot11_mode);
+
+ intr[req->vdev_id].chanmode = chanmode; /* save channel mode */
+ intr[req->vdev_id].ht_capable = req->ht_capable;
+ intr[req->vdev_id].vht_capable = req->vht_capable;
+ intr[req->vdev_id].config.gtx_info.gtxRTMask[0] =
+ CFG_TGT_DEFAULT_GTX_HT_MASK;
+ intr[req->vdev_id].config.gtx_info.gtxRTMask[1] =
+ CFG_TGT_DEFAULT_GTX_VHT_MASK;
+ intr[req->vdev_id].config.gtx_info.gtxUsrcfg =
+ CFG_TGT_DEFAULT_GTX_USR_CFG;
+ intr[req->vdev_id].config.gtx_info.gtxPERThreshold =
+ CFG_TGT_DEFAULT_GTX_PER_THRESHOLD;
+ intr[req->vdev_id].config.gtx_info.gtxPERMargin =
+ CFG_TGT_DEFAULT_GTX_PER_MARGIN;
+ intr[req->vdev_id].config.gtx_info.gtxTPCstep =
+ CFG_TGT_DEFAULT_GTX_TPC_STEP;
+ intr[req->vdev_id].config.gtx_info.gtxTPCMin =
+ CFG_TGT_DEFAULT_GTX_TPC_MIN;
+ intr[req->vdev_id].config.gtx_info.gtxBWMask =
+ CFG_TGT_DEFAULT_GTX_BW_MASK;
+ intr[req->vdev_id].mhz = cmd->mhz;
+
+ WMI_SET_CHANNEL_MODE(cmd, chanmode);
+ cmd->band_center_freq1 = cmd->mhz;
+
+ if (chanmode == MODE_11AC_VHT80)
+ cmd->band_center_freq1 = vos_chan_to_freq(wma_getCenterChannel
+ (req->chan, req->chan_offset));
+
+ if ((chanmode == MODE_11NA_HT40) || (chanmode == MODE_11NG_HT40) ||
+ (chanmode == MODE_11AC_VHT40)) {
+ if (req->chan_offset == PHY_DOUBLE_CHANNEL_LOW_PRIMARY)
+ cmd->band_center_freq1 += 10;
+ else
+ cmd->band_center_freq1 -= 10;
+ }
+ cmd->band_center_freq2 = 0;
+
+ /* Set half or quarter rate WMI flags */
+ if (req->is_half_rate)
+ WMI_SET_CHANNEL_FLAG(cmd, WMI_CHAN_FLAG_HALF_RATE);
+ else if (req->is_quarter_rate)
+ WMI_SET_CHANNEL_FLAG(cmd, WMI_CHAN_FLAG_QUARTER_RATE);
+
+ /* Find out min, max and regulatory power levels */
+ WMI_SET_CHANNEL_REG_POWER(cmd, req->max_txpow);
+ WMI_SET_CHANNEL_MAX_TX_POWER(cmd, req->max_txpow);
+
+
+ WMA_LOGD("%s: freq %d channel %d chanmode %d center_chan %d center_freq2 %d reg_info_1: 0x%x reg_info_2: 0x%x, req->max_txpow: 0x%x",
+ __func__, cmd->mhz, req->chan, chanmode,
+ cmd->band_center_freq1, cmd->band_center_freq2,
+ cmd->reg_info_1, cmd->reg_info_2, req->max_txpow);
+
+
+ ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len,
+ WMI_PDEV_SET_CHANNEL_CMDID);
+
+ if (ret < 0) {
+ WMA_LOGP("%s: Failed to send vdev start command", __func__);
+ adf_nbuf_free(buf);
+ return VOS_STATUS_E_FAILURE;
+ }
+
+ return VOS_STATUS_SUCCESS;
+}
VOS_STATUS wma_vdev_start(tp_wma_handle wma,
struct wma_vdev_start_req *req, v_BOOL_t isRestart)
{
@@ -11677,7 +11794,7 @@ VOS_STATUS wma_vdev_start(tp_wma_handle wma,
* If the Channel is DFS,
* set the WMI_CHAN_FLAG_DFS flag
*/
- if (req->is_dfs) {
+ if ((VOS_MONITOR_MODE != vos_get_conparam()) && req->is_dfs) {
WMI_SET_CHANNEL_FLAG(chan, WMI_CHAN_FLAG_DFS);
cmd->disable_hw_ack = VOS_TRUE;
@@ -12366,17 +12483,29 @@ static void wma_set_channel(tp_wma_handle wma, tpSwitchChannelParams params)
params->restart_on_chan_switch == VOS_TRUE)
wma->interfaces[req.vdev_id].is_channel_switch = VOS_TRUE;
- status = wma_vdev_start(wma, &req,
- wma->interfaces[req.vdev_id].is_channel_switch);
- if (status != VOS_STATUS_SUCCESS) {
- wma_remove_vdev_req(wma, req.vdev_id, WMA_TARGET_REQ_TYPE_VDEV_START);
- WMA_LOGP("%s: vdev start failed status = %d", __func__, status);
- goto send_resp;
- }
+ if ((VOS_MONITOR_MODE == vos_get_conparam()) && wma_is_vdev_up(0)) {
+ status = wma_switch_channel(wma, &req);
+ if (status != VOS_STATUS_SUCCESS)
+ WMA_LOGE("%s: wma_switch_channel failed %d\n", __func__,
+ status);
+ return;
+ } else {
- if (wma->interfaces[req.vdev_id].is_channel_switch)
- wma->interfaces[req.vdev_id].is_channel_switch = VOS_FALSE;
- return;
+ status = wma_vdev_start(wma, &req,
+ wma->interfaces[req.vdev_id].is_channel_switch);
+ if (status != VOS_STATUS_SUCCESS) {
+ wma_remove_vdev_req(wma, req.vdev_id,
+ WMA_TARGET_REQ_TYPE_VDEV_START);
+ WMA_LOGP("%s: vdev start failed status = %d", __func__,
+ status);
+ goto send_resp;
+ }
+
+ if (wma->interfaces[req.vdev_id].is_channel_switch)
+ wma->interfaces[req.vdev_id].is_channel_switch =
+ VOS_FALSE;
+ return;
+ }
send_resp:
WMA_LOGD("%s: channel %d offset %d txpower %d status %d", __func__,
params->channelNumber, params->secondaryChannelOffset,
@@ -12387,8 +12516,8 @@ send_resp:
#endif
status);
params->status = status;
- WMA_LOGI("%s: sending WDA_SWITCH_CHANNEL_RSP, status = 0x%x",
- __func__, status);
+ WMA_LOGI("%s: sending WDA_SWITCH_CHANNEL_RSP, status = 0x%x",
+ __func__, status);
wma_send_msg(wma, WDA_SWITCH_CHANNEL_RSP, (void *)params, 0);
}
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index 1998a2d83f6a..f5eec1186660 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -4510,4 +4510,14 @@ bool sme_is_sta_smps_allowed(tHalHandle hHal, uint8_t session_id);
eHalStatus sme_get_bpf_offload_capabilities(tHalHandle hal);
eHalStatus sme_set_bpf_instructions(tHalHandle hal,
struct sir_bpf_set_offload *);
+
+/**
+ * sme_create_mon_session() - post message to create PE session for monitormode
+ * operation
+ * @hal_handle: Handle to the HAL
+ * @bssid: pointer to bssid
+ *
+ * Return: eHAL_STATUS_SUCCESS on success, non-zero error code on failure.
+ */
+eHalStatus sme_create_mon_session(tHalHandle hal_handle, uint8_t *bssid);
#endif //#if !defined( __SME_API_H )
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index e61b86c26ef9..db68bb60bed6 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -7210,6 +7210,35 @@ eHalStatus sme_OemDataReq(tHalHandle hHal,
#endif /*FEATURE_OEM_DATA_SUPPORT*/
+/**
+ * sme_create_mon_session() - post message to create PE session for monitormode
+ * operation
+ * @hal_handle: Handle to the HAL
+ * @bssid: pointer to bssid
+ *
+ * Return: eHAL_STATUS_SUCCESS on success, non-zero error code on failure.
+ */
+eHalStatus sme_create_mon_session(tHalHandle hal_handle, tSirMacAddr bss_id)
+{
+ eHalStatus status = eHAL_STATUS_SUCCESS;
+ struct sir_create_session *msg;
+ tpAniSirGlobal mac_ptr = PMAC_STRUCT(hal_handle);
+ uint16_t msg_len;
+
+ msg_len = (tANI_U16)(sizeof(struct sir_create_session));
+ msg = vos_mem_malloc(msg_len);
+ if ( NULL != msg )
+ {
+ vos_mem_set(msg, msg_len, 0);
+ msg->type =
+ pal_cpu_to_be16((tANI_U16)eWNI_SME_MON_INIT_SESSION);
+ msg->msg_len = pal_cpu_to_be16(msg_len);
+ vos_mem_copy(msg->bss_id, bss_id, sizeof(tSirMacAddr));
+ status = palSendMBMessage(mac_ptr->hHdd, msg);
+ }
+ return status;
+}
+
/*--------------------------------------------------------------------------
\brief sme_OpenSession() - Open a session for scan/roam operation.