diff options
| author | Manjunathappa Prakash <prakashp@qca.qualcomm.com> | 2015-12-26 10:41:47 -0800 |
|---|---|---|
| committer | Anjaneedevi Kapparapu <akappa@codeaurora.org> | 2016-02-03 17:27:59 +0530 |
| commit | 4ff9447f4211f2bfe1e2d3594c0d294f0097702b (patch) | |
| tree | 81c065fc2ce9bf26e9b80d826305521333275366 | |
| parent | 250a98255f0239a9cc771140895b98027fe0ac1f (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
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. |
