/*
 * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

#include <qdf_nbuf.h>               /* qdf_nbuf_t, etc. */
#include <qdf_util.h>               /* qdf_cpu_to_le64 */
#include <qdf_types.h>              /* bool */
#include <cds_ieee80211_common.h>   /* ieee80211_frame */

/* external API header files */
#include <ol_ctrl_txrx_api.h>   /* ol_rx_notify */
#include <ol_txrx_api.h>        /* ol_txrx_pdev_handle */
#include <ol_txrx_htt_api.h>    /* ol_rx_indication_handler */
#include <ol_htt_rx_api.h>      /* htt_rx_peer_id, etc. */

/* internal API header files */
#include <ol_txrx_peer_find.h>  /* ol_txrx_peer_find_by_id */
#include <ol_rx_reorder.h>      /* ol_rx_reorder_store, etc. */
#include <ol_rx_reorder_timeout.h>      /* OL_RX_REORDER_TIMEOUT_UPDATE */
#include <ol_rx_defrag.h>       /* ol_rx_defrag_waitlist_flush */
#include <ol_txrx_internal.h>
#include <wdi_event.h>
#ifdef QCA_SUPPORT_SW_TXRX_ENCAP
#include <ol_txrx_encap.h>      /* ol_rx_decap_info_t, etc */
#endif
#include <ol_rx.h>

/* FIX THIS: txrx should not include private header files of other modules */
#include <htt_types.h>
#include <ol_if_athvar.h>
#include <enet.h>               /* ethernet + SNAP/LLC header defs and
				 * ethertype values
				 */
#include <ip_prot.h>            /* IP protocol values */
#include <ipv4.h>               /* IPv4 header defs */
#include <ipv6_defs.h>          /* IPv6 header defs */
#include <ol_vowext_dbg_defs.h>
#include <wma.h>
#include <wlan_policy_mgr_api.h>
#include "pktlog_ac_fmt.h"
#include <cdp_txrx_handle.h>
#include <pld_common.h>


#define OL_RX_INDICATION_MAX_RECORDS 2048

/**
 * enum ol_rx_ind_record_type - OL rx indication events
 * @OL_RX_INDICATION_POP_START: event recorded before netbuf pop
 * @OL_RX_INDICATION_POP_END: event recorded after netbuf pop
 * @OL_RX_INDICATION_BUF_REPLENISH: event recorded after buffer replenishment
 */
enum ol_rx_ind_record_type {
	OL_RX_INDICATION_POP_START,
	OL_RX_INDICATION_POP_END,
	OL_RX_INDICATION_BUF_REPLENISH,
};

/**
 * struct ol_rx_ind_record - structure for detailing ol txrx rx ind. event
 * @value: info corresponding to rx indication event
 * @type: what the event was
 * @time: when it happened
 */
struct ol_rx_ind_record {
	uint16_t value;
	enum ol_rx_ind_record_type type;
	uint64_t time;
};

#ifdef OL_RX_INDICATION_RECORD
static uint32_t ol_rx_ind_record_index;
static struct ol_rx_ind_record
	      ol_rx_indication_record_history[OL_RX_INDICATION_MAX_RECORDS];

/**
 * ol_rx_ind_record_event() - record ol rx indication events
 * @value: contains rx ind. event related info
 * @type: ol rx indication message type
 *
 * This API record the ol rx indiation event in a rx indication
 * record buffer.
 *
 * Return: None
 */
static void ol_rx_ind_record_event(uint32_t value,
				    enum ol_rx_ind_record_type type)
{
	ol_rx_indication_record_history[ol_rx_ind_record_index].value = value;
	ol_rx_indication_record_history[ol_rx_ind_record_index].type = type;
	ol_rx_indication_record_history[ol_rx_ind_record_index].time =
							qdf_get_log_timestamp();

	ol_rx_ind_record_index++;
	if (ol_rx_ind_record_index >= OL_RX_INDICATION_MAX_RECORDS)
		ol_rx_ind_record_index = 0;
}
#else
static inline
void ol_rx_ind_record_event(uint32_t value, enum ol_rx_ind_record_type type)
{
}

#endif /* OL_RX_INDICATION_RECORD */

void ol_rx_data_process(struct ol_txrx_peer_t *peer,
			qdf_nbuf_t rx_buf_list);

/**
 * ol_rx_send_pktlog_event() - send rx packetlog event
 * @pdev: pdev handle
 * @peer: peer handle
 * @msdu: skb list
 * @pktlog_bit: packetlog bit from firmware
 *
 * Return: none
 */
#ifdef HELIUMPLUS
void ol_rx_send_pktlog_event(struct ol_txrx_pdev_t *pdev,
	struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, uint8_t pktlog_bit)
{
	struct ol_rx_remote_data data;

	if (!pktlog_bit)
		return;

	data.msdu = msdu;
	if (peer)
		data.mac_id = peer->vdev->mac_id;
	else
		data.mac_id = 0;

	wdi_event_handler(WDI_EVENT_RX_DESC_REMOTE, pdev, &data);
}
#else
void ol_rx_send_pktlog_event(struct ol_txrx_pdev_t *pdev,
	struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, uint8_t pktlog_bit)
{
	struct ol_rx_remote_data data;

	data.msdu = msdu;
	if (peer)
		data.mac_id = peer->vdev->mac_id;
	else
		data.mac_id = 0;

	wdi_event_handler(WDI_EVENT_RX_DESC_REMOTE, pdev, &data);
}
#endif

#ifdef HTT_RX_RESTORE

static void ol_rx_restore_handler(struct work_struct *htt_rx)
{
	qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);

	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
		  "Enter: %s", __func__);
	pld_device_self_recovery(qdf_ctx->dev);
	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
		  "Exit: %s", __func__);
}

static DECLARE_WORK(ol_rx_restore_work, ol_rx_restore_handler);

void ol_rx_trigger_restore(htt_pdev_handle htt_pdev, qdf_nbuf_t head_msdu,
			   qdf_nbuf_t tail_msdu)
{
	qdf_nbuf_t next;

	while (head_msdu) {
		next = qdf_nbuf_next(head_msdu);
		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
			  "freeing %p\n", head_msdu);
		qdf_nbuf_free(head_msdu);
		head_msdu = next;
	}

	if (!htt_pdev->rx_ring.htt_rx_restore) {
		cds_set_recovery_in_progress(true);
		htt_pdev->rx_ring.htt_rx_restore = 1;
		schedule_work(&ol_rx_restore_work);
	}
}
#endif

/**
 * ol_rx_update_histogram_stats() - update rx histogram statistics
 * @msdu_count: msdu count
 * @frag_ind: fragment indication set
 * @offload_ind: offload indication set
 *
 * Return: none
 */
void ol_rx_update_histogram_stats(uint32_t msdu_count, uint8_t frag_ind,
		 uint8_t offload_ind)
{
	struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX);

	if (!pdev) {
		ol_txrx_err("%s pdev is NULL\n", __func__);
		return;
	}

	if (msdu_count > 60) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_61_plus, 1);
	} else if (msdu_count > 50) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_51_60, 1);
	} else if (msdu_count > 40) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_41_50, 1);
	} else if (msdu_count > 30) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_31_40, 1);
	} else if (msdu_count > 20) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_21_30, 1);
	} else if (msdu_count > 10) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_11_20, 1);
	} else if (msdu_count > 1) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_2_10, 1);
	} else if (msdu_count == 1) {
		TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_1, 1);
	}

	if (frag_ind)
		TXRX_STATS_ADD(pdev, pub.rx.msdus_with_frag_ind, msdu_count);

	if (offload_ind)
		TXRX_STATS_ADD(pdev, pub.rx.msdus_with_offload_ind, msdu_count);

}

static void ol_rx_process_inv_peer(ol_txrx_pdev_handle pdev,
				   void *rx_mpdu_desc, qdf_nbuf_t msdu)
{
	uint8_t a1[IEEE80211_ADDR_LEN];
	htt_pdev_handle htt_pdev = pdev->htt_pdev;
	struct ol_txrx_vdev_t *vdev = NULL;
	struct ieee80211_frame *wh;
	struct wdi_event_rx_peer_invalid_msg msg;

	wh = (struct ieee80211_frame *)
	     htt_rx_mpdu_wifi_hdr_retrieve(htt_pdev, rx_mpdu_desc);
	/*
	 * Klocwork issue #6152
	 *  All targets that send a "INVALID_PEER" rx status provide a
	 *  802.11 header for each rx MPDU, so it is certain that
	 *  htt_rx_mpdu_wifi_hdr_retrieve will succeed.
	 *  However, both for robustness, e.g. if this function is given a
	 *  MSDU descriptor rather than a MPDU descriptor, and to make it
	 *  clear to static analysis that this code is safe, add an explicit
	 *  check that htt_rx_mpdu_wifi_hdr_retrieve provides a non-NULL value.
	 */
	if (wh == NULL || !IEEE80211_IS_DATA(wh))
		return;

	/* ignore frames for non-existent bssids */
	qdf_mem_copy(a1, wh->i_addr1, IEEE80211_ADDR_LEN);
	TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
		if (qdf_mem_cmp(a1, vdev->mac_addr.raw, IEEE80211_ADDR_LEN))
			break;
	}
	if (!vdev)
		return;

	msg.wh = wh;
	msg.msdu = msdu;
	msg.vdev_id = vdev->vdev_id;
#ifdef WDI_EVENT_ENABLE
	wdi_event_handler(WDI_EVENT_RX_PEER_INVALID, pdev, &msg);
#endif
}

#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI
static inline int16_t
ol_rx_rssi_avg(struct ol_txrx_pdev_t *pdev, int16_t rssi_old, int16_t rssi_new)
{
	int rssi_old_weight;

	if (rssi_new == HTT_RSSI_INVALID)
		return rssi_old;
	if (rssi_old == HTT_RSSI_INVALID)
		return rssi_new;

	rssi_old_weight =
		(1 << pdev->rssi_update_shift) - pdev->rssi_new_weight;
	return (rssi_new * pdev->rssi_new_weight +
		rssi_old * rssi_old_weight) >> pdev->rssi_update_shift;
}

static void
ol_rx_ind_rssi_update(struct ol_txrx_peer_t *peer, qdf_nbuf_t rx_ind_msg)
{
	struct ol_txrx_pdev_t *pdev = peer->vdev->pdev;

	peer->rssi_dbm = ol_rx_rssi_avg(pdev, peer->rssi_dbm,
					htt_rx_ind_rssi_dbm(pdev->htt_pdev,
							    rx_ind_msg));
}

static void
ol_rx_mpdu_rssi_update(struct ol_txrx_peer_t *peer, void *rx_mpdu_desc)
{
	struct ol_txrx_pdev_t *pdev = peer->vdev->pdev;

	if (!peer)
		return;
	peer->rssi_dbm = ol_rx_rssi_avg(pdev, peer->rssi_dbm,
					htt_rx_mpdu_desc_rssi_dbm(
						pdev->htt_pdev,
						rx_mpdu_desc));
}

#else
#define ol_rx_ind_rssi_update(peer, rx_ind_msg) /* no-op */
#define ol_rx_mpdu_rssi_update(peer, rx_mpdu_desc)      /* no-op */
#endif /* QCA_SUPPORT_PEER_DATA_RX_RSSI */

static void discard_msdus(htt_pdev_handle htt_pdev,
			  qdf_nbuf_t head_msdu,
			  qdf_nbuf_t tail_msdu)
{
	while (1) {
		qdf_nbuf_t next;

		next = qdf_nbuf_next(
			head_msdu);
		htt_rx_desc_frame_free
			(htt_pdev,
			 head_msdu);
		if (head_msdu ==
		    tail_msdu) {
			break;
		}
		head_msdu = next;
	}
}

static void chain_msdus(htt_pdev_handle htt_pdev,
			qdf_nbuf_t head_msdu,
			qdf_nbuf_t tail_msdu)
{
	while (1) {
		qdf_nbuf_t next;

		next = qdf_nbuf_next(head_msdu);
		htt_rx_desc_frame_free(
			htt_pdev,
			head_msdu);
		if (head_msdu == tail_msdu)
			break;
		head_msdu = next;
	}
}

static void process_reorder(ol_txrx_pdev_handle pdev,
			    void *rx_mpdu_desc,
			    uint8_t tid,
			    struct ol_txrx_peer_t *peer,
			    qdf_nbuf_t head_msdu,
			    qdf_nbuf_t tail_msdu,
			    int num_mpdu_ranges,
			    int num_pdus,
			    bool rx_ind_release)
{
	htt_pdev_handle htt_pdev = pdev->htt_pdev;
	enum htt_rx_status mpdu_status;
	int reorder_idx;

	reorder_idx = htt_rx_mpdu_desc_reorder_idx(htt_pdev, rx_mpdu_desc);
	OL_RX_REORDER_TRACE_ADD(pdev, tid,
				reorder_idx,
				htt_rx_mpdu_desc_seq_num(htt_pdev,
							 rx_mpdu_desc),
				1);
	ol_rx_mpdu_rssi_update(peer, rx_mpdu_desc);
	/*
	 * In most cases, out-of-bounds and duplicate sequence number detection
	 * is performed by the target, but in some cases it is done by the host.
	 * Specifically, the host does rx out-of-bounds sequence number
	 * detection for:
	 * 1.  Peregrine or Rome target
	 *     for peer-TIDs that do not have aggregation enabled, if the
	 *     RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK flag
	 *     is set during the driver build.
	 * 2.  Riva-family targets, which have rx reorder timeouts handled by
	 *     the host rather than the target.
	 *     (The target already does duplicate detection, but the host
	 *     may have given up waiting for a particular sequence number before
	 *     it arrives.  In this case, the out-of-bounds sequence number
	 *     of the late frame allows the host to discard it, rather than
	 *     sending it out of order.
	 */
	mpdu_status = OL_RX_SEQ_NUM_CHECK(pdev,
						  peer,
						  tid,
						  rx_mpdu_desc);
	if (mpdu_status != htt_rx_status_ok) {
		/*
		 * If the sequence number was out of bounds, the MPDU needs
		 * to be discarded.
		 */
		discard_msdus(htt_pdev, head_msdu, tail_msdu);
		/*
		 * For Peregrine and Rome,
		 * OL_RX_REORDER_SEQ_NUM_CHECK should only fail for the case
		 * of (duplicate) non-aggregates.
		 *
		 * For Riva, Pronto and Northstar,
		 * there should be only one MPDU delivered at a time.
		 * Thus, there are no further MPDUs that need to be
		 * processed here.
		 * Just to be sure this is true, check the assumption
		 * that this was the only MPDU referenced by the rx
		 * indication.
		 */
		TXRX_ASSERT2((num_mpdu_ranges == 1) && num_mpdus == 1);

		/*
		 * The MPDU was not stored in the rx reorder array, so
		 * there's nothing to release.
		 */
		rx_ind_release = false;
	} else {
		ol_rx_reorder_store(pdev, peer, tid,
				    reorder_idx, head_msdu, tail_msdu);
		if (peer->tids_rx_reorder[tid].win_sz_mask == 0) {
			peer->tids_last_seq[tid] = htt_rx_mpdu_desc_seq_num(
				htt_pdev,
				rx_mpdu_desc);
		}
	}
} /* process_reorder */

void
ol_rx_indication_handler(ol_txrx_pdev_handle pdev,
			 qdf_nbuf_t rx_ind_msg,
			 uint16_t peer_id, uint8_t tid, int num_mpdu_ranges)
{
	int mpdu_range, i;
	unsigned int seq_num_start = 0, seq_num_end = 0;
	bool rx_ind_release = false;
	struct ol_txrx_vdev_t *vdev = NULL;
	struct ol_txrx_peer_t *peer;
	htt_pdev_handle htt_pdev;
	uint16_t center_freq;
	uint16_t chan1;
	uint16_t chan2;
	uint8_t phymode;
	bool ret;
	uint32_t msdu_count = 0;

	htt_pdev = pdev->htt_pdev;
	peer = ol_txrx_peer_find_by_id(pdev, peer_id);
	if (!peer) {
		/*
		 * If we can't find a peer send this packet to OCB interface
		 * using OCB self peer
		 */
		if (!ol_txrx_get_ocb_peer(pdev, &peer))
			peer = NULL;
	}

	if (peer) {
		vdev = peer->vdev;
		ol_rx_ind_rssi_update(peer, rx_ind_msg);

		if (vdev->opmode == wlan_op_mode_ocb) {
			htt_rx_ind_legacy_rate(pdev->htt_pdev, rx_ind_msg,
				       &peer->last_pkt_legacy_rate,
				       &peer->last_pkt_legacy_rate_sel);
			peer->last_pkt_rssi_cmb = htt_rx_ind_rssi_dbm(
				pdev->htt_pdev, rx_ind_msg);
			for (i = 0; i < 4; i++)
				peer->last_pkt_rssi[i] =
					htt_rx_ind_rssi_dbm_chain(
					pdev->htt_pdev, rx_ind_msg, i);
			htt_rx_ind_timestamp(pdev->htt_pdev, rx_ind_msg,
					&peer->last_pkt_timestamp_microsec,
					&peer->last_pkt_timestamp_submicrosec);
			peer->last_pkt_tsf = htt_rx_ind_tsf32(pdev->htt_pdev,
							      rx_ind_msg);
			peer->last_pkt_tid = htt_rx_ind_ext_tid(pdev->htt_pdev,
								rx_ind_msg);
		}
	}

	TXRX_STATS_INCR(pdev, priv.rx.normal.ppdus);

	OL_RX_REORDER_TIMEOUT_MUTEX_LOCK(pdev);

	if (htt_rx_ind_flush(pdev->htt_pdev, rx_ind_msg) && peer) {
		htt_rx_ind_flush_seq_num_range(pdev->htt_pdev, rx_ind_msg,
					       &seq_num_start, &seq_num_end);
		if (tid == HTT_INVALID_TID) {
			/*
			 * host/FW reorder state went out-of sync
			 * for a while because FW ran out of Rx indication
			 * buffer. We have to discard all the buffers in
			 * reorder queue.
			 */
			ol_rx_reorder_peer_cleanup(vdev, peer);
		} else {
			ol_rx_reorder_flush(vdev, peer, tid, seq_num_start,
					    seq_num_end, htt_rx_flush_release);
		}
	}

	if (htt_rx_ind_release(pdev->htt_pdev, rx_ind_msg)) {
		/*
		 * The ind info of release is saved here and do release at the
		 * end. This is for the reason of in HL case, the qdf_nbuf_t
		 * for msg and payload are the same buf. And the buf will be
		 * changed during processing
		 */
		rx_ind_release = true;
		htt_rx_ind_release_seq_num_range(pdev->htt_pdev, rx_ind_msg,
						 &seq_num_start, &seq_num_end);
	}
#ifdef DEBUG_DMA_DONE
	pdev->htt_pdev->rx_ring.dbg_initial_msdu_payld =
		pdev->htt_pdev->rx_ring.sw_rd_idx.msdu_payld;
#endif

	for (mpdu_range = 0; mpdu_range < num_mpdu_ranges; mpdu_range++) {
		enum htt_rx_status status;
		int i, num_mpdus;
		qdf_nbuf_t head_msdu, tail_msdu, msdu;
		void *rx_mpdu_desc;

#ifdef DEBUG_DMA_DONE
		pdev->htt_pdev->rx_ring.dbg_mpdu_range = mpdu_range;
#endif

		htt_rx_ind_mpdu_range_info(pdev->htt_pdev, rx_ind_msg,
					   mpdu_range, &status, &num_mpdus);
		if ((status == htt_rx_status_ok) && peer) {
			TXRX_STATS_ADD(pdev, priv.rx.normal.mpdus, num_mpdus);
			/* valid frame - deposit it into rx reordering buffer */
			for (i = 0; i < num_mpdus; i++) {
				int msdu_chaining;
				/*
				 * Get a linked list of the MSDUs that comprise
				 * this MPDU.
				 * This also attaches each rx MSDU descriptor to
				 * the corresponding rx MSDU network buffer.
				 * (In some systems, the rx MSDU desc is already
				 * in the same buffer as the MSDU payload; in
				 * other systems they are separate, so a pointer
				 * needs to be set in the netbuf to locate the
				 * corresponding rx descriptor.)
				 *
				 * It is neccessary to call htt_rx_amsdu_pop
				 * before htt_rx_mpdu_desc_list_next, because
				 * the (MPDU) rx descriptor has DMA unmapping
				 * done during the htt_rx_amsdu_pop call.
				 * The rx desc should not be accessed until this
				 * DMA unmapping has been done, since the DMA
				 * unmapping involves making sure the cache area
				 * for the mapped buffer is flushed, so the data
				 * written by the MAC DMA into memory will be
				 * fetched, rather than garbage from the cache.
				 */

#ifdef DEBUG_DMA_DONE
				pdev->htt_pdev->rx_ring.dbg_mpdu_count = i;
#endif

				msdu_chaining =
					htt_rx_amsdu_pop(htt_pdev,
							 rx_ind_msg,
							 &head_msdu,
							 &tail_msdu,
							 &msdu_count);
#ifdef HTT_RX_RESTORE
				if (htt_pdev->rx_ring.rx_reset) {
					ol_rx_trigger_restore(htt_pdev,
							      head_msdu,
							      tail_msdu);
					return;
				}
#endif
				rx_mpdu_desc =
					htt_rx_mpdu_desc_list_next(htt_pdev,
								   rx_ind_msg);
				ret = htt_rx_msdu_center_freq(htt_pdev, peer,
					rx_mpdu_desc, &center_freq, &chan1,
					&chan2, &phymode);
				if (ret == true) {
					peer->last_pkt_center_freq =
						center_freq;
				} else {
					peer->last_pkt_center_freq = 0;
				}

				/* Pktlog */
#ifdef WDI_EVENT_ENABLE
		ol_rx_send_pktlog_event(pdev, peer, head_msdu, 1);
#endif

				if (msdu_chaining) {
					/*
					 * TBDXXX - to deliver SDU with
					 * chaining, we need to stitch those
					 * scattered buffers into one single
					 * buffer.
					 * Just discard it now.
					 */
					chain_msdus(htt_pdev,
						    head_msdu,
						    tail_msdu);
				} else {
					process_reorder(pdev, rx_mpdu_desc,
							tid, peer,
							head_msdu, tail_msdu,
							num_mpdu_ranges,
							num_mpdus,
							rx_ind_release);
				}

			}
		} else {
			/* invalid frames - discard them */
			OL_RX_REORDER_TRACE_ADD(pdev, tid,
						TXRX_SEQ_NUM_ERR(status),
						TXRX_SEQ_NUM_ERR(status),
						num_mpdus);
			TXRX_STATS_ADD(pdev, priv.rx.err.mpdu_bad, num_mpdus);
			for (i = 0; i < num_mpdus; i++) {
				/* pull the MPDU's MSDUs off the buffer queue */
				htt_rx_amsdu_pop(htt_pdev, rx_ind_msg, &msdu,
						 &tail_msdu, &msdu_count);
#ifdef HTT_RX_RESTORE
				if (htt_pdev->rx_ring.rx_reset) {
					ol_rx_trigger_restore(htt_pdev, msdu,
							      tail_msdu);
					return;
				}
#endif
				/* pull the MPDU desc off the desc queue */
				rx_mpdu_desc =
					htt_rx_mpdu_desc_list_next(htt_pdev,
								   rx_ind_msg);
				OL_RX_ERR_STATISTICS_2(pdev, vdev, peer,
						       rx_mpdu_desc, msdu,
						       status);

				if (status == htt_rx_status_tkip_mic_err &&
				    vdev != NULL && peer != NULL) {
					union htt_rx_pn_t pn;
					uint8_t key_id;

					htt_rx_mpdu_desc_pn(
						pdev->htt_pdev,
						htt_rx_msdu_desc_retrieve(
							pdev->htt_pdev,
							msdu), &pn, 48);
					if (htt_rx_msdu_desc_key_id(
						    pdev->htt_pdev,
						    htt_rx_msdu_desc_retrieve(
							    pdev->htt_pdev,
							    msdu),
						    &key_id) == true) {
						ol_rx_err(pdev->ctrl_pdev,
							  vdev->vdev_id,
							  peer->mac_addr.raw,
							  tid, 0,
							  OL_RX_ERR_TKIP_MIC,
							  msdu, &pn.pn48,
							  key_id);
					}
				}
#ifdef WDI_EVENT_ENABLE
				if (status != htt_rx_status_ctrl_mgmt_null) {
					/* Pktlog */
					ol_rx_send_pktlog_event(pdev,
						 peer, msdu, 1);
				}
#endif
				if (status == htt_rx_status_err_inv_peer) {
					/* once per mpdu */
					ol_rx_process_inv_peer(pdev,
							       rx_mpdu_desc,
							       msdu);
				}
				while (1) {
					/* Free the nbuf */
					qdf_nbuf_t next;

					next = qdf_nbuf_next(msdu);
					htt_rx_desc_frame_free(htt_pdev, msdu);
					if (msdu == tail_msdu)
						break;
					msdu = next;
				}
			}
		}
	}
	/*
	 * Now that a whole batch of MSDUs have been pulled out of HTT
	 * and put into the rx reorder array, it is an appropriate time
	 * to request HTT to provide new rx MSDU buffers for the target
	 * to fill.
	 * This could be done after the end of this function, but it's
	 * better to do it now, rather than waiting until after the driver
	 * and OS finish processing the batch of rx MSDUs.
	 */
	htt_rx_msdu_buff_replenish(htt_pdev);

	if ((true == rx_ind_release) && peer && vdev) {
		ol_rx_reorder_release(vdev, peer, tid, seq_num_start,
				      seq_num_end);
	}
	OL_RX_REORDER_TIMEOUT_UPDATE(peer, tid);
	OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK(pdev);

	if (pdev->rx.flags.defrag_timeout_check)
		ol_rx_defrag_waitlist_flush(pdev);
}

void
ol_rx_sec_ind_handler(ol_txrx_pdev_handle pdev,
		      uint16_t peer_id,
		      enum htt_sec_type sec_type,
		      int is_unicast, uint32_t *michael_key, uint32_t *rx_pn)
{
	struct ol_txrx_peer_t *peer;
	int sec_index, i;

	peer = ol_txrx_peer_find_by_id(pdev, peer_id);
	if (!peer) {
		ol_txrx_err(
			"Couldn't find peer from ID %d - skipping security inits\n",
			peer_id);
		return;
	}
	ol_txrx_dbg(
		"sec spec for peer %p (%02x:%02x:%02x:%02x:%02x:%02x): %s key of type %d\n",
		peer,
		peer->mac_addr.raw[0], peer->mac_addr.raw[1],
		peer->mac_addr.raw[2], peer->mac_addr.raw[3],
		peer->mac_addr.raw[4], peer->mac_addr.raw[5],
		is_unicast ? "ucast" : "mcast", sec_type);
	sec_index = is_unicast ? txrx_sec_ucast : txrx_sec_mcast;
	peer->security[sec_index].sec_type = sec_type;
	/*
	 * michael key only valid for TKIP
	 * but for simplicity, copy it anyway
	 */
	qdf_mem_copy(&peer->security[sec_index].michael_key[0],
		     michael_key,
		     sizeof(peer->security[sec_index].michael_key));

	if (sec_type != htt_sec_type_wapi) {
		qdf_mem_set(peer->tids_last_pn_valid,
			    OL_TXRX_NUM_EXT_TIDS, 0x00);
	} else if (sec_index == txrx_sec_mcast || peer->tids_last_pn_valid[0]) {
		for (i = 0; i < OL_TXRX_NUM_EXT_TIDS; i++) {
			/*
			 * Setting PN valid bit for WAPI sec_type,
			 * since WAPI PN has to be started with predefined value
			 */
			peer->tids_last_pn_valid[i] = 1;
			qdf_mem_copy((uint8_t *) &peer->tids_last_pn[i],
				     (uint8_t *) rx_pn,
				     sizeof(union htt_rx_pn_t));
			peer->tids_last_pn[i].pn128[1] =
				qdf_cpu_to_le64(
					peer->tids_last_pn[i].pn128[1]);
			peer->tids_last_pn[i].pn128[0] =
				qdf_cpu_to_le64(
					peer->tids_last_pn[i].pn128[0]);
		}
	}
}

#if defined(PERE_IP_HDR_ALIGNMENT_WAR)

#include <cds_ieee80211_common.h>

static void transcap_nwifi_to_8023(qdf_nbuf_t msdu)
{
	struct ieee80211_frame *wh;
	uint32_t hdrsize;
	struct llc *llchdr;
	struct ether_header *eth_hdr;
	uint16_t ether_type = 0;
	uint8_t a1[IEEE80211_ADDR_LEN];
	uint8_t a2[IEEE80211_ADDR_LEN];
	uint8_t a3[IEEE80211_ADDR_LEN];
	uint8_t fc1;

	wh = (struct ieee80211_frame *)qdf_nbuf_data(msdu);
	qdf_mem_copy(a1, wh->i_addr1, IEEE80211_ADDR_LEN);
	qdf_mem_copy(a2, wh->i_addr2, IEEE80211_ADDR_LEN);
	qdf_mem_copy(a3, wh->i_addr3, IEEE80211_ADDR_LEN);
	fc1 = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
	/* Native Wifi header is 80211 non-QoS header */
	hdrsize = sizeof(struct ieee80211_frame);

	llchdr = (struct llc *)(((uint8_t *) qdf_nbuf_data(msdu)) + hdrsize);
	ether_type = llchdr->llc_un.type_snap.ether_type;

	/*
	 * Now move the data pointer to the beginning of the mac header :
	 * new-header = old-hdr + (wifhdrsize + llchdrsize - ethhdrsize)
	 */
	qdf_nbuf_pull_head(msdu,
			   (hdrsize + sizeof(struct llc) -
			    sizeof(struct ether_header)));
	eth_hdr = (struct ether_header *)(qdf_nbuf_data(msdu));
	switch (fc1) {
	case IEEE80211_FC1_DIR_NODS:
		qdf_mem_copy(eth_hdr->ether_dhost, a1, IEEE80211_ADDR_LEN);
		qdf_mem_copy(eth_hdr->ether_shost, a2, IEEE80211_ADDR_LEN);
		break;
	case IEEE80211_FC1_DIR_TODS:
		qdf_mem_copy(eth_hdr->ether_dhost, a3, IEEE80211_ADDR_LEN);
		qdf_mem_copy(eth_hdr->ether_shost, a2, IEEE80211_ADDR_LEN);
		break;
	case IEEE80211_FC1_DIR_FROMDS:
		qdf_mem_copy(eth_hdr->ether_dhost, a1, IEEE80211_ADDR_LEN);
		qdf_mem_copy(eth_hdr->ether_shost, a3, IEEE80211_ADDR_LEN);
		break;
	case IEEE80211_FC1_DIR_DSTODS:
		break;
	}
	eth_hdr->ether_type = ether_type;
}
#endif

void ol_rx_notify(struct cdp_cfg *cfg_pdev,
		  uint8_t vdev_id,
		  uint8_t *peer_mac_addr,
		  int tid,
		  uint32_t tsf32,
		  enum ol_rx_notify_type notify_type, qdf_nbuf_t rx_frame)
{
	/*
	 * NOTE: This is used in qca_main for AP mode to handle IGMP
	 * packets specially. Umac has a corresponding handler for this
	 * not sure if we need to have this for CLD as well.
	 */
}

/**
 * @brief Look into a rx MSDU to see what kind of special handling it requires
 * @details
 *      This function is called when the host rx SW sees that the target
 *      rx FW has marked a rx MSDU as needing inspection.
 *      Based on the results of the inspection, the host rx SW will infer
 *      what special handling to perform on the rx frame.
 *      Currently, the only type of frames that require special handling
 *      are IGMP frames.  The rx data-path SW checks if the frame is IGMP
 *      (it should be, since the target would not have set the inspect flag
 *      otherwise), and then calls the ol_rx_notify function so the
 *      control-path SW can perform multicast group membership learning
 *      by sniffing the IGMP frame.
 */
#define SIZEOF_80211_HDR (sizeof(struct ieee80211_frame))
static void
ol_rx_inspect(struct ol_txrx_vdev_t *vdev,
	      struct ol_txrx_peer_t *peer,
	      unsigned int tid, qdf_nbuf_t msdu, void *rx_desc)
{
	ol_txrx_pdev_handle pdev = vdev->pdev;
	uint8_t *data, *l3_hdr;
	uint16_t ethertype;
	int offset;

	data = qdf_nbuf_data(msdu);
	if (pdev->frame_format == wlan_frm_fmt_native_wifi) {
		offset = SIZEOF_80211_HDR + LLC_SNAP_HDR_OFFSET_ETHERTYPE;
		l3_hdr = data + SIZEOF_80211_HDR + LLC_SNAP_HDR_LEN;
	} else {
		offset = ETHERNET_ADDR_LEN * 2;
		l3_hdr = data + ETHERNET_HDR_LEN;
	}
	ethertype = (data[offset] << 8) | data[offset + 1];
	if (ethertype == ETHERTYPE_IPV4) {
		offset = IPV4_HDR_OFFSET_PROTOCOL;
		if (l3_hdr[offset] == IP_PROTOCOL_IGMP) {
			ol_rx_notify(pdev->ctrl_pdev,
				     vdev->vdev_id,
				     peer->mac_addr.raw,
				     tid,
				     htt_rx_mpdu_desc_tsf32(pdev->htt_pdev,
							    rx_desc),
				     OL_RX_NOTIFY_IPV4_IGMP, msdu);
		}
	}
}

void
ol_rx_offload_deliver_ind_handler(ol_txrx_pdev_handle pdev,
				  qdf_nbuf_t msg, int msdu_cnt)
{
	int vdev_id, peer_id, tid;
	qdf_nbuf_t head_buf, tail_buf, buf;
	struct ol_txrx_peer_t *peer;
	uint8_t fw_desc;
	htt_pdev_handle htt_pdev = pdev->htt_pdev;

	while (msdu_cnt) {
		if (!htt_rx_offload_msdu_pop(htt_pdev, msg, &vdev_id, &peer_id,
					&tid, &fw_desc, &head_buf, &tail_buf)) {
			peer = ol_txrx_peer_find_by_id(pdev, peer_id);
			if (peer) {
				ol_rx_data_process(peer, head_buf);
			} else {
				buf = head_buf;
				while (1) {
					qdf_nbuf_t next;

					next = qdf_nbuf_next(buf);
					htt_rx_desc_frame_free(htt_pdev, buf);
					if (buf == tail_buf)
						break;
					buf = next;
				}
			}
		}
		msdu_cnt--;
	}
	htt_rx_msdu_buff_replenish(htt_pdev);
}

#ifdef WDI_EVENT_ENABLE
static inline
void ol_rx_mic_error_send_pktlog_event(struct ol_txrx_pdev_t *pdev,
	struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, uint8_t pktlog_bit)
{
	ol_rx_send_pktlog_event(pdev, peer, msdu, pktlog_bit);
}

#else
static inline
void ol_rx_mic_error_send_pktlog_event(struct ol_txrx_pdev_t *pdev,
	struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, uint8_t pktlog_bit)
{
}

#endif


void
ol_rx_mic_error_handler(
	ol_txrx_pdev_handle pdev,
	u_int8_t tid,
	u_int16_t peer_id,
	void *msdu_desc,
	qdf_nbuf_t msdu)
{
	union htt_rx_pn_t pn = {0};
	u_int8_t key_id = 0;

	struct ol_txrx_peer_t *peer = NULL;
	struct ol_txrx_vdev_t *vdev = NULL;

	if (pdev) {
		TXRX_STATS_MSDU_INCR(pdev, rx.dropped_mic_err, msdu);
		peer = ol_txrx_peer_find_by_id(pdev, peer_id);
		if (peer) {
			vdev = peer->vdev;
			if (vdev) {
				htt_rx_mpdu_desc_pn(vdev->pdev->htt_pdev,
						    msdu_desc, &pn, 48);

				if (htt_rx_msdu_desc_key_id(
					vdev->pdev->htt_pdev, msdu_desc,
					&key_id) == true) {
					ol_rx_err(vdev->pdev->ctrl_pdev,
						  vdev->vdev_id,
						  peer->mac_addr.raw, tid, 0,
						  OL_RX_ERR_TKIP_MIC, msdu,
						  &pn.pn48, key_id);
				}
			}
		}
		/* Pktlog */
		ol_rx_mic_error_send_pktlog_event(pdev, peer, msdu, 1);
	}
}

/**
 * @brief Check the first msdu to decide whether the a-msdu should be accepted.
 */
static bool
ol_rx_filter(struct ol_txrx_vdev_t *vdev,
	     struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, void *rx_desc)
{
#define FILTER_STATUS_REJECT 1
#define FILTER_STATUS_ACCEPT 0
	uint8_t *wh;
	uint32_t offset = 0;
	uint16_t ether_type = 0;
	bool is_encrypted = false, is_mcast = false;
	uint8_t i;
	enum privacy_filter_packet_type packet_type =
		PRIVACY_FILTER_PACKET_UNICAST;
	ol_txrx_pdev_handle pdev = vdev->pdev;
	htt_pdev_handle htt_pdev = pdev->htt_pdev;
	int sec_idx;

	/*
	 * Safemode must avoid the PrivacyExemptionList and
	 * ExcludeUnencrypted checking
	 */
	if (vdev->safemode)
		return FILTER_STATUS_ACCEPT;

	is_mcast = htt_rx_msdu_is_wlan_mcast(htt_pdev, rx_desc);
	if (vdev->num_filters > 0) {
		if (pdev->frame_format == wlan_frm_fmt_native_wifi) {
			offset = SIZEOF_80211_HDR +
				LLC_SNAP_HDR_OFFSET_ETHERTYPE;
		} else {
			offset = ETHERNET_ADDR_LEN * 2;
		}
		/* get header info from msdu */
		wh = qdf_nbuf_data(msdu);

		/* get ether type */
		ether_type = (wh[offset] << 8) | wh[offset + 1];
		/* get packet type */
		if (true == is_mcast)
			packet_type = PRIVACY_FILTER_PACKET_MULTICAST;
		else
			packet_type = PRIVACY_FILTER_PACKET_UNICAST;
	}
	/* get encrypt info */
	is_encrypted = htt_rx_mpdu_is_encrypted(htt_pdev, rx_desc);
#ifdef ATH_SUPPORT_WAPI
	if ((true == is_encrypted) && (ETHERTYPE_WAI == ether_type)) {
		/*
		 * We expect the WAI frames to be always unencrypted when
		 * the UMAC gets it
		 */
		return FILTER_STATUS_REJECT;
	}
#endif /* ATH_SUPPORT_WAPI */

	for (i = 0; i < vdev->num_filters; i++) {
		enum privacy_filter filter_type;
		enum privacy_filter_packet_type filter_packet_type;

		/* skip if the ether type does not match */
		if (vdev->privacy_filters[i].ether_type != ether_type)
			continue;

		/* skip if the packet type does not match */
		filter_packet_type = vdev->privacy_filters[i].packet_type;
		if (filter_packet_type != packet_type &&
		    filter_packet_type != PRIVACY_FILTER_PACKET_BOTH) {
			continue;
		}

		filter_type = vdev->privacy_filters[i].filter_type;
		if (filter_type == PRIVACY_FILTER_ALWAYS) {
			/*
			 * In this case, we accept the frame if and only if
			 * it was originally NOT encrypted.
			 */
			if (true == is_encrypted)
				return FILTER_STATUS_REJECT;
			else
				return FILTER_STATUS_ACCEPT;

		} else if (filter_type == PRIVACY_FILTER_KEY_UNAVAILABLE) {
			/*
			 * In this case, we reject the frame if it was
			 * originally NOT encrypted but we have the key mapping
			 * key for this frame.
			 */
			if (!is_encrypted &&
			    !is_mcast &&
			    (peer->security[txrx_sec_ucast].sec_type !=
			     htt_sec_type_none) &&
			    (peer->keyinstalled || !ETHERTYPE_IS_EAPOL_WAPI(
				    ether_type))) {
				return FILTER_STATUS_REJECT;
			} else {
				return FILTER_STATUS_ACCEPT;
			}
		} else {
			/*
			 * The privacy exemption does not apply to this frame.
			 */
			break;
		}
	}

	/*
	 * If the privacy exemption list does not apply to the frame,
	 * check ExcludeUnencrypted.
	 * If ExcludeUnencrypted is not set, or if this was oringially
	 * an encrypted frame, it will be accepted.
	 */
	if (!vdev->drop_unenc || (true == is_encrypted))
		return FILTER_STATUS_ACCEPT;

	/*
	 *  If this is a open connection, it will be accepted.
	 */
	sec_idx = (true == is_mcast) ? txrx_sec_mcast : txrx_sec_ucast;
	if (peer->security[sec_idx].sec_type == htt_sec_type_none)
		return FILTER_STATUS_ACCEPT;

	if ((false == is_encrypted) && vdev->drop_unenc) {
		OL_RX_ERR_STATISTICS(pdev, vdev, OL_RX_ERR_PRIVACY,
				     pdev->sec_types[htt_sec_type_none],
				     is_mcast);
	}
	return FILTER_STATUS_REJECT;
}

#ifdef WLAN_FEATURE_TSF_PLUS
static inline void ol_rx_timestamp(void *rx_desc, qdf_nbuf_t msdu)
{
	struct htt_rx_ppdu_desc_t *rx_ppdu_desc;

	if (!rx_desc || !msdu)
		return;

	rx_ppdu_desc = (struct htt_rx_ppdu_desc_t *)((uint8_t *)(rx_desc) -
			HTT_RX_IND_HL_BYTES + HTT_RX_IND_HDR_PREFIX_BYTES);
	msdu->tstamp = ns_to_ktime((u_int64_t)rx_ppdu_desc->tsf32 *
			NSEC_PER_USEC);
}
#else
static inline void ol_rx_timestamp(void *rx_desc, qdf_nbuf_t msdu)
{
}
#endif

void
ol_rx_deliver(struct ol_txrx_vdev_t *vdev,
	      struct ol_txrx_peer_t *peer, unsigned int tid,
	      qdf_nbuf_t msdu_list)
{
	ol_txrx_pdev_handle pdev = vdev->pdev;
	htt_pdev_handle htt_pdev = pdev->htt_pdev;
	qdf_nbuf_t deliver_list_head = NULL;
	qdf_nbuf_t deliver_list_tail = NULL;
	qdf_nbuf_t msdu;
	bool filter = false;
#ifdef QCA_SUPPORT_SW_TXRX_ENCAP
	struct ol_rx_decap_info_t info;

	qdf_mem_set(&info, sizeof(info), 0);
#endif

	msdu = msdu_list;
	/*
	 * Check each MSDU to see whether it requires special handling,
	 * and free each MSDU's rx descriptor
	 */
	while (msdu) {
		void *rx_desc;
		int discard, inspect, dummy_fwd;
		qdf_nbuf_t next = qdf_nbuf_next(msdu);

		rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu);
		/* for HL, point to payload right now*/
		if (pdev->cfg.is_high_latency)
			qdf_nbuf_pull_head(msdu,
				htt_rx_msdu_rx_desc_size_hl(htt_pdev, rx_desc));

#ifdef QCA_SUPPORT_SW_TXRX_ENCAP
		info.is_msdu_cmpl_mpdu =
			htt_rx_msdu_desc_completes_mpdu(htt_pdev, rx_desc);
		info.is_first_subfrm =
			htt_rx_msdu_first_msdu_flag(htt_pdev, rx_desc);
		if (OL_RX_DECAP(vdev, peer, msdu, &info) != A_OK) {
			discard = 1;
			ol_txrx_dbg(
				"decap error %p from peer %p (%02x:%02x:%02x:%02x:%02x:%02x) len %d\n",
				msdu, peer,
				peer->mac_addr.raw[0], peer->mac_addr.raw[1],
				peer->mac_addr.raw[2], peer->mac_addr.raw[3],
				peer->mac_addr.raw[4], peer->mac_addr.raw[5],
				qdf_nbuf_len(msdu));
			goto DONE;
		}
#endif
		htt_rx_msdu_actions(pdev->htt_pdev, rx_desc, &discard,
				    &dummy_fwd, &inspect);
		if (inspect)
			ol_rx_inspect(vdev, peer, tid, msdu, rx_desc);

		/*
		 * Check the first msdu in the mpdu, if it will be filtered out,
		 * then discard the entire mpdu.
		 */
		if (htt_rx_msdu_first_msdu_flag(htt_pdev, rx_desc))
			filter = ol_rx_filter(vdev, peer, msdu, rx_desc);

#ifdef QCA_SUPPORT_SW_TXRX_ENCAP
DONE:
#endif
		htt_rx_msdu_desc_free(htt_pdev, msdu);
		if (discard || (true == filter)) {
			ol_txrx_frms_dump("rx discarding:",
					  pdev, deliver_list_head,
					  ol_txrx_frm_dump_tcp_seq |
					  ol_txrx_frm_dump_contents,
					  0 /* don't print contents */);
			qdf_nbuf_free(msdu);
			/*
			 * If discarding packet is last packet of the delivery
			 * list, NULL terminator should be added
			 * for delivery list.
			 */
			if (next == NULL && deliver_list_head) {
				/* add NULL terminator */
				qdf_nbuf_set_next(deliver_list_tail, NULL);
			}
		} else {
			/*
			 *  If this is for OCB,
			 *  then prepend the RX stats header.
			 */
			if (vdev->opmode == wlan_op_mode_ocb) {
				int i;
				struct ol_txrx_ocb_chan_info *chan_info = 0;
				int packet_freq = peer->last_pkt_center_freq;

				for (i = 0; i < vdev->ocb_channel_count; i++) {
					if (vdev->ocb_channel_info[i].
						chan_freq == packet_freq) {
						chan_info = &vdev->
							ocb_channel_info[i];
						break;
					}
				}
				if (!chan_info || !chan_info->
					disable_rx_stats_hdr) {
					struct ether_header eth_header = {
						{0} };
					struct ocb_rx_stats_hdr_t rx_header = {
						0};

					/*
					 * Construct the RX stats header and
					 * push that to the frontof the packet.
					 */
					rx_header.version = 1;
					rx_header.length = sizeof(rx_header);
					rx_header.channel_freq =
						peer->last_pkt_center_freq;
					rx_header.rssi_cmb =
						peer->last_pkt_rssi_cmb;
					qdf_mem_copy(rx_header.rssi,
							peer->last_pkt_rssi,
							sizeof(rx_header.rssi));
					if (peer->last_pkt_legacy_rate_sel)
						rx_header.datarate = 0xFF;
					else if (peer->last_pkt_legacy_rate ==
						 0x8)
						rx_header.datarate = 6;
					else if (peer->last_pkt_legacy_rate ==
						 0x9)
						rx_header.datarate = 4;
					else if (peer->last_pkt_legacy_rate ==
						 0xA)
						rx_header.datarate = 2;
					else if (peer->last_pkt_legacy_rate ==
						 0xB)
						rx_header.datarate = 0;
					else if (peer->last_pkt_legacy_rate ==
						 0xC)
						rx_header.datarate = 7;
					else if (peer->last_pkt_legacy_rate ==
						 0xD)
						rx_header.datarate = 5;
					else if (peer->last_pkt_legacy_rate ==
						 0xE)
						rx_header.datarate = 3;
					else if (peer->last_pkt_legacy_rate ==
						 0xF)
						rx_header.datarate = 1;
					else
						rx_header.datarate = 0xFF;

					rx_header.timestamp_microsec = peer->
						last_pkt_timestamp_microsec;
					rx_header.timestamp_submicrosec = peer->
						last_pkt_timestamp_submicrosec;
					rx_header.tsf32 = peer->last_pkt_tsf;
					rx_header.ext_tid = peer->last_pkt_tid;

					qdf_nbuf_push_head(msdu,
						sizeof(rx_header));
					qdf_mem_copy(qdf_nbuf_data(msdu),
						&rx_header, sizeof(rx_header));

					/*
					 * Construct the ethernet header with
					 * type 0x8152 and push that to the
					 * front of the packet to indicate the
					 * RX stats header.
					 */
					eth_header.ether_type = QDF_SWAP_U16(
						ETHERTYPE_OCB_RX);
					qdf_nbuf_push_head(msdu,
							   sizeof(eth_header));
					qdf_mem_copy(qdf_nbuf_data(msdu),
							&eth_header,
							 sizeof(eth_header));
				}
			}
			OL_RX_PEER_STATS_UPDATE(peer, msdu);
			OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, rx_desc,
					       OL_RX_ERR_NONE);
			TXRX_STATS_MSDU_INCR(vdev->pdev, rx.delivered, msdu);

			ol_rx_timestamp(rx_desc, msdu);
			OL_TXRX_LIST_APPEND(deliver_list_head,
					    deliver_list_tail, msdu);
		}
		msdu = next;
	}
	/* sanity check - are there any frames left to give to the OS shim? */
	if (!deliver_list_head)
		return;

#if defined(PERE_IP_HDR_ALIGNMENT_WAR)
	if (pdev->host_80211_enable)
		for (msdu = deliver_list_head; msdu; msdu = qdf_nbuf_next(msdu))
			transcap_nwifi_to_8023(msdu);
#endif

	ol_txrx_frms_dump("rx delivering:",
			  pdev, deliver_list_head,
			  ol_txrx_frm_dump_tcp_seq | ol_txrx_frm_dump_contents,
			  0 /* don't print contents */);

	ol_rx_data_process(peer, deliver_list_head);
}

void
ol_rx_discard(struct ol_txrx_vdev_t *vdev,
	      struct ol_txrx_peer_t *peer, unsigned int tid,
	      qdf_nbuf_t msdu_list)
{
	ol_txrx_pdev_handle pdev = vdev->pdev;
	htt_pdev_handle htt_pdev = pdev->htt_pdev;

	while (msdu_list) {
		qdf_nbuf_t msdu = msdu_list;

		msdu_list = qdf_nbuf_next(msdu_list);
		ol_txrx_dbg(
			"discard rx %p from partly-deleted peer %p (%02x:%02x:%02x:%02x:%02x:%02x)\n",
			msdu, peer,
			peer->mac_addr.raw[0], peer->mac_addr.raw[1],
			peer->mac_addr.raw[2], peer->mac_addr.raw[3],
			peer->mac_addr.raw[4], peer->mac_addr.raw[5]);
		htt_rx_desc_frame_free(htt_pdev, msdu);
	}
}

void ol_rx_peer_init(struct ol_txrx_pdev_t *pdev, struct ol_txrx_peer_t *peer)
{
	uint8_t tid;

	for (tid = 0; tid < OL_TXRX_NUM_EXT_TIDS; tid++) {
		ol_rx_reorder_init(&peer->tids_rx_reorder[tid], tid);

		/* invalid sequence number */
		peer->tids_last_seq[tid] = IEEE80211_SEQ_MAX;
	}
	/*
	 * Set security defaults: no PN check, no security.
	 * The target may send a HTT SEC_IND message to overwrite
	 * these defaults.
	 */
	peer->security[txrx_sec_ucast].sec_type =
		peer->security[txrx_sec_mcast].sec_type = htt_sec_type_none;
	peer->keyinstalled = 0;

	peer->last_assoc_rcvd = 0;
	peer->last_disassoc_rcvd = 0;
	peer->last_deauth_rcvd = 0;

	qdf_atomic_init(&peer->fw_pn_check);
}

void
ol_rx_peer_cleanup(struct ol_txrx_vdev_t *vdev, struct ol_txrx_peer_t *peer)
{
	peer->keyinstalled = 0;
	peer->last_assoc_rcvd = 0;
	peer->last_disassoc_rcvd = 0;
	peer->last_deauth_rcvd = 0;
	ol_rx_reorder_peer_cleanup(vdev, peer);
}

/*
 * Free frames including both rx descriptors and buffers
 */
void ol_rx_frames_free(htt_pdev_handle htt_pdev, qdf_nbuf_t frames)
{
	qdf_nbuf_t next, frag = frames;

	while (frag) {
		next = qdf_nbuf_next(frag);
		htt_rx_desc_frame_free(htt_pdev, frag);
		frag = next;
	}
}

void
ol_rx_in_order_indication_handler(ol_txrx_pdev_handle pdev,
				  qdf_nbuf_t rx_ind_msg,
				  uint16_t peer_id,
				  uint8_t tid, uint8_t is_offload)
{
	struct ol_txrx_vdev_t *vdev = NULL;
	struct ol_txrx_peer_t *peer = NULL;
	htt_pdev_handle htt_pdev = NULL;
	int status;
	qdf_nbuf_t head_msdu = NULL, tail_msdu = NULL;
	uint8_t *rx_ind_data;
	uint32_t *msg_word;
	uint32_t msdu_count;
#ifdef WDI_EVENT_ENABLE
	uint8_t pktlog_bit;
#endif
	uint32_t filled = 0;

	if (pdev) {
		if (qdf_unlikely(QDF_GLOBAL_MONITOR_MODE == cds_get_conparam()))
			peer = pdev->self_peer;
		else
			peer = ol_txrx_peer_find_by_id(pdev, peer_id);
		htt_pdev = pdev->htt_pdev;
	} else {
		ol_txrx_err("%s: Invalid pdev passed!\n", __func__);
		qdf_assert_always(pdev);
		return;
	}

#if defined(HELIUMPLUS_DEBUG)
	qdf_print("%s %d: rx_ind_msg 0x%p peer_id %d tid %d is_offload %d\n",
		  __func__, __LINE__, rx_ind_msg, peer_id, tid, is_offload);
#endif

#ifdef WDI_EVENT_ENABLE
	pktlog_bit = (htt_rx_amsdu_rx_in_order_get_pktlog(rx_ind_msg) == 0x01);
#endif

	rx_ind_data = qdf_nbuf_data(rx_ind_msg);
	msg_word = (uint32_t *)rx_ind_data;
	/* Get the total number of MSDUs */
	msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1));

	ol_rx_ind_record_event(msdu_count, OL_RX_INDICATION_POP_START);

	/*
	 * Get a linked list of the MSDUs in the rx in order indication.
	 * This also attaches each rx MSDU descriptor to the
	 * corresponding rx MSDU network buffer.
	 */
	status = htt_rx_amsdu_pop(htt_pdev, rx_ind_msg, &head_msdu,
				  &tail_msdu, &msdu_count);
	ol_rx_ind_record_event(status, OL_RX_INDICATION_POP_END);

	if (qdf_unlikely(0 == status)) {
		ol_txrx_warn("%s: Pop status is 0, returning here", __func__);
		return;
	}

	/*
	 * Replenish the rx buffer ring first to provide buffers to the target
	 * rather than waiting for the indeterminate time taken by the OS
	 * to consume the rx frames
	 */
	filled = htt_rx_msdu_buff_in_order_replenish(htt_pdev, msdu_count);
	ol_rx_ind_record_event(filled, OL_RX_INDICATION_BUF_REPLENISH);

	if (!head_msdu) {
		ol_txrx_warn("%s: No packet to send to HDD",
			     __func__);
		return;
	}

	/* Send the chain of MSDUs to the OS */
	/* rx_opt_proc takes a NULL-terminated list of msdu netbufs */
	qdf_nbuf_set_next(tail_msdu, NULL);

	/* Pktlog */
#ifdef WDI_EVENT_ENABLE
	ol_rx_send_pktlog_event(pdev, peer, head_msdu, pktlog_bit);
#endif

	/*
	 * if this is an offload indication, peer id is carried in the
	 * rx buffer
	 */
	if (peer) {
		vdev = peer->vdev;
	} else {
		ol_txrx_dbg(
			   "%s: Couldn't find peer from ID 0x%x\n",
			   __func__, peer_id);
		while (head_msdu) {
			qdf_nbuf_t msdu = head_msdu;

			head_msdu = qdf_nbuf_next(head_msdu);
			TXRX_STATS_MSDU_INCR(pdev,
				 rx.dropped_peer_invalid, msdu);
			htt_rx_desc_frame_free(htt_pdev, msdu);
		}
		return;
	}

	peer->rx_opt_proc(vdev, peer, tid, head_msdu);
}

/**
 * ol_rx_pkt_dump_call() - updates status and
 * calls packetdump callback to log rx packet
 *
 * @msdu: rx packet
 * @peer_id: peer id
 * @status: status of rx packet
 *
 * This function is used to update the status of rx packet
 * and then calls packetdump callback to log that packet.
 *
 * Return: None
 *
 */
void ol_rx_pkt_dump_call(
	qdf_nbuf_t msdu,
	uint8_t peer_id,
	uint8_t status)
{
	v_CONTEXT_t vos_context;
	ol_txrx_pdev_handle pdev;
	struct ol_txrx_peer_t *peer = NULL;
	tp_ol_packetdump_cb packetdump_cb;

	vos_context = cds_get_global_context();
	pdev = cds_get_context(QDF_MODULE_ID_TXRX);

	if (!pdev) {
		ol_txrx_err("%s: pdev is NULL", __func__);
		return;
	}

	peer = ol_txrx_peer_find_by_id(pdev, peer_id);
	if (!peer) {
		ol_txrx_dbg("%s: peer with peer id %d is NULL", __func__,
			peer_id);
		return;
	}

	packetdump_cb = pdev->ol_rx_packetdump_cb;
	if (packetdump_cb)
		packetdump_cb(msdu, status, peer->vdev->vdev_id, RX_DATA_PKT);
}

/* the msdu_list passed here must be NULL terminated */
void
ol_rx_in_order_deliver(struct ol_txrx_vdev_t *vdev,
		       struct ol_txrx_peer_t *peer,
		       unsigned int tid, qdf_nbuf_t msdu_list)
{
	qdf_nbuf_t msdu;

	msdu = msdu_list;
	/*
	 * Currently, this does not check each MSDU to see whether it requires
	 * special handling. MSDUs that need special handling (example: IGMP
	 * frames) should be sent via a seperate HTT message. Also, this does
	 * not do rx->tx forwarding or filtering.
	 */

	while (msdu) {
		qdf_nbuf_t next = qdf_nbuf_next(msdu);

		DPTRACE(qdf_dp_trace(msdu,
			QDF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD,
			QDF_TRACE_DEFAULT_PDEV_ID,
			qdf_nbuf_data_addr(msdu),
			sizeof(qdf_nbuf_data(msdu)), QDF_RX));

		OL_RX_PEER_STATS_UPDATE(peer, msdu);
		OL_RX_ERR_STATISTICS_1(vdev->pdev, vdev, peer, rx_desc,
				       OL_RX_ERR_NONE);
		TXRX_STATS_MSDU_INCR(vdev->pdev, rx.delivered, msdu);

		msdu = next;
	}

	ol_txrx_frms_dump("rx delivering:",
			  pdev, deliver_list_head,
			  ol_txrx_frm_dump_tcp_seq | ol_txrx_frm_dump_contents,
			  0 /* don't print contents */);

	ol_rx_data_process(peer, msdu_list);
}

void ol_rx_log_packet(htt_pdev_handle htt_pdev,
		uint8_t peer_id, qdf_nbuf_t msdu)
{
	struct ol_txrx_peer_t *peer;

	peer = ol_txrx_peer_find_by_id(htt_pdev->txrx_pdev, peer_id);
	if (peer)
		qdf_dp_trace_log_pkt(peer->vdev->vdev_id, msdu, QDF_RX,
				QDF_TRACE_DEFAULT_PDEV_ID);
}

void
ol_rx_offload_paddr_deliver_ind_handler(htt_pdev_handle htt_pdev,
					uint32_t msdu_count,
					uint32_t *msg_word)
{
	int vdev_id, peer_id, tid;
	qdf_nbuf_t head_buf, tail_buf, buf;
	struct ol_txrx_peer_t *peer;
	uint8_t fw_desc;
	int msdu_iter = 0;

	while (msdu_count) {
		htt_rx_offload_paddr_msdu_pop_ll(htt_pdev, msg_word, msdu_iter,
						 &vdev_id, &peer_id, &tid,
						 &fw_desc, &head_buf,
						 &tail_buf);

		peer = ol_txrx_peer_find_by_id(htt_pdev->txrx_pdev, peer_id);
		if (peer) {
			QDF_NBUF_CB_DP_TRACE_PRINT(head_buf) = false;
			qdf_dp_trace_set_track(head_buf, QDF_RX);
			QDF_NBUF_CB_TX_PACKET_TRACK(head_buf) =
						QDF_NBUF_TX_PKT_DATA_TRACK;
			qdf_dp_trace_log_pkt(peer->vdev->vdev_id,
				head_buf, QDF_RX,
				QDF_TRACE_DEFAULT_PDEV_ID);
			DPTRACE(qdf_dp_trace(head_buf,
				QDF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD,
				QDF_TRACE_DEFAULT_PDEV_ID,
				qdf_nbuf_data_addr(head_buf),
				sizeof(qdf_nbuf_data(head_buf)), QDF_RX));
			ol_rx_data_process(peer, head_buf);
		} else {
			buf = head_buf;
			while (1) {
				qdf_nbuf_t next;

				next = qdf_nbuf_next(buf);
				htt_rx_desc_frame_free(htt_pdev, buf);
				if (buf == tail_buf)
					break;
				buf = next;
			}
		}
		msdu_iter++;
		msdu_count--;
	}
	htt_rx_msdu_buff_replenish(htt_pdev);
}

/**
 * ol_htt_mon_note_chan() - Update monitor channel information
 * @pdev:  handle to the physical device
 * @mon_ch: Monitor channel
 *
 * Return: None
 */
void ol_htt_mon_note_chan(struct cdp_pdev *ppdev, int mon_ch)
{
	struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
	htt_rx_mon_note_capture_channel(pdev->htt_pdev, mon_ch);
}

#ifdef NEVERDEFINED
/**
 * @brief populates vow ext stats in given network buffer.
 * @param msdu - network buffer handle
 * @param pdev - handle to htt dev.
 */
void ol_ath_add_vow_extstats(htt_pdev_handle pdev, qdf_nbuf_t msdu)
{
	/* FIX THIS:
	 * txrx should not be directly using data types (scn)
	 * that are internal to other modules.
	 */
	struct ol_ath_softc_net80211 *scn =
		(struct ol_ath_softc_net80211 *)pdev->ctrl_pdev;
	uint8_t *data, *l3_hdr, *bp;
	uint16_t ethertype;
	int offset;
	struct vow_extstats vowstats;

	if (scn->vow_extstats == 0)
		return;

	data = qdf_nbuf_data(msdu);

	offset = ETHERNET_ADDR_LEN * 2;
	l3_hdr = data + ETHERNET_HDR_LEN;
	ethertype = (data[offset] << 8) | data[offset + 1];
	if (ethertype == ETHERTYPE_IPV4) {
		offset = IPV4_HDR_OFFSET_PROTOCOL;
		if ((l3_hdr[offset] == IP_PROTOCOL_UDP) &&
				(l3_hdr[0] == IP_VER4_N_NO_EXTRA_HEADERS)) {
			bp = data + EXT_HDR_OFFSET;

			if ((data[RTP_HDR_OFFSET] == UDP_PDU_RTP_EXT) &&
					(bp[0] == 0x12) &&
					(bp[1] == 0x34) &&
					(bp[2] == 0x00) && (bp[3] == 0x08)) {
				/*
				 * Clear UDP checksum so we do not have
				 * to recalculate it
				 * after filling in status fields.
				 */
				data[UDP_CKSUM_OFFSET] = 0;
				data[(UDP_CKSUM_OFFSET + 1)] = 0;

				bp += IPERF3_DATA_OFFSET;

				htt_rx_get_vowext_stats(msdu,
						&vowstats);

				/* control channel RSSI */
				*bp++ = vowstats.rx_rssi_ctl0;
				*bp++ = vowstats.rx_rssi_ctl1;
				*bp++ = vowstats.rx_rssi_ctl2;

				/* rx rate info */
				*bp++ = vowstats.rx_bw;
				*bp++ = vowstats.rx_sgi;
				*bp++ = vowstats.rx_nss;

				*bp++ = vowstats.rx_rssi_comb;
				/* rsflags */
				*bp++ = vowstats.rx_rs_flags;

				/* Time stamp Lo */
				*bp++ = (uint8_t)
					((vowstats.
					  rx_macTs & 0x0000ff00) >> 8);
				*bp++ = (uint8_t)
					(vowstats.rx_macTs & 0x0000ff);
				/* rx phy errors */
				*bp++ = (uint8_t)
					((scn->chan_stats.
					  phy_err_cnt >> 8) & 0xff);
				*bp++ =
					(uint8_t) (scn->chan_stats.
							phy_err_cnt & 0xff);
				/* rx clear count */
				*bp++ = (uint8_t)
					((scn->mib_cycle_cnts.
					  rx_clear_count >> 24) & 0xff);
				*bp++ = (uint8_t)
					((scn->mib_cycle_cnts.
					  rx_clear_count >> 16) & 0xff);
				*bp++ = (uint8_t)
					((scn->mib_cycle_cnts.
					  rx_clear_count >> 8) & 0xff);
				*bp++ = (uint8_t)
					(scn->mib_cycle_cnts.
					 rx_clear_count & 0xff);
				/* rx cycle count */
				*bp++ = (uint8_t)
					((scn->mib_cycle_cnts.
					  cycle_count >> 24) & 0xff);
				*bp++ = (uint8_t)
					((scn->mib_cycle_cnts.
					  cycle_count >> 16) & 0xff);
				*bp++ = (uint8_t)
					((scn->mib_cycle_cnts.
					  cycle_count >> 8) & 0xff);
				*bp++ = (uint8_t)
					(scn->mib_cycle_cnts.
					 cycle_count & 0xff);

				*bp++ = vowstats.rx_ratecode;
				*bp++ = vowstats.rx_moreaggr;

				/* sequence number */
				*bp++ = (uint8_t)
					((vowstats.rx_seqno >> 8) &
					 0xff);
				*bp++ = (uint8_t)
					(vowstats.rx_seqno & 0xff);
			}
		}
	}
}

#endif
