/*
 * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 *
 * 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.
 */

/**
 *  DOC:  qdf_trace
 *  QCA driver framework (QDF) trace APIs
 *  Trace, logging, and debugging definitions and APIs
 */

/* Include Files */
#include "qdf_str.h"
#include <qdf_trace.h>
#include <qdf_parse.h>
#include <qdf_module.h>
#include <qdf_util.h>
#include <qdf_mem.h>
#include <qdf_list.h>

/* macro to map qdf trace levels into the bitmask */
#define QDF_TRACE_LEVEL_TO_MODULE_BITMASK(_level) ((1 << (_level)))

#include <wlan_logging_sock_svc.h>
#include <qdf_module.h>
static int qdf_pidx = -1;
static bool qdf_log_dump_at_kernel_enable = true;
qdf_declare_param(qdf_log_dump_at_kernel_enable, bool);

/* This value of 0 will disable the timer by default. */
static uint32_t qdf_log_flush_timer_period;
qdf_declare_param(qdf_log_flush_timer_period, uint);

#include "qdf_time.h"
#include "qdf_mc_timer.h"
#include <host_diag_core_log.h>

#ifdef CONNECTIVITY_DIAG_EVENT
#include <wlan_connectivity_logging.h>
#include "i_host_diag_core_event.h"
#endif

#ifdef WLAN_CHIPSET_STATS
#include "wlan_cp_stats_chipset_stats.h"
#endif
/* Global qdf print id */

/* Preprocessor definitions and constants */

enum qdf_timestamp_unit qdf_log_timestamp_type = QDF_LOG_TIMESTAMP_UNIT;

#define DP_TRACE_META_DATA_STRLEN 50

#ifdef TRACE_RECORD
/* Static and Global variables */
static spinlock_t ltrace_lock;
/* global qdf trace data */
static t_qdf_trace_data g_qdf_trace_data;
/*
 * all the call back functions for dumping MTRACE messages from ring buffer
 * are stored in qdf_trace_cb_table,these callbacks are initialized during init
 * only so, we will make a copy of these call back functions and maintain in to
 * qdf_trace_restore_cb_table. Incase if we make modifications to
 * qdf_trace_cb_table, we can certainly retrieve all the call back functions
 * back from Restore Table
 */
static tp_qdf_trace_cb qdf_trace_cb_table[QDF_MODULE_ID_MAX];
static tp_qdf_trace_cb qdf_trace_restore_cb_table[QDF_MODULE_ID_MAX];

#ifdef WLAN_LOGGING_BUFFERS_DYNAMICALLY
static qdf_trace_record_t *g_qdf_trace_tbl;
#else
static qdf_trace_record_t g_qdf_trace_tbl[MAX_QDF_TRACE_RECORDS];
#endif

#endif

#ifdef WLAN_FEATURE_MEMDUMP_ENABLE
static tp_qdf_state_info_cb qdf_state_info_table[QDF_MODULE_ID_MAX];
#endif

#ifdef CONFIG_DP_TRACE
/* Static and Global variables */
#ifdef WLAN_LOGGING_BUFFERS_DYNAMICALLY
static struct qdf_dp_trace_record_s *g_qdf_dp_trace_tbl;
#else
static struct qdf_dp_trace_record_s
			g_qdf_dp_trace_tbl[MAX_QDF_DP_TRACE_RECORDS];
#endif
static spinlock_t l_dp_trace_lock;

/*
 * all the options to configure/control DP trace are
 * defined in this structure
 */
static struct s_qdf_dp_trace_data g_qdf_dp_trace_data;
/*
 * all the call back functions for dumping DPTRACE messages from ring buffer
 * are stored in qdf_dp_trace_cb_table, callbacks are initialized during init
 */
static tp_qdf_dp_trace_cb qdf_dp_trace_cb_table[QDF_DP_TRACE_MAX + 1];
#endif

#ifdef QCA_WIFI_MODULE_PARAMS_FROM_INI
#define QDF_PARAM_STR_LENGTH 40

enum qdf_num_module_param {
	MEM_DEBUG_DISABLED,
	QDF_DBG_MASK,
	PREALLOC_DISABLED,
	QDF_LOG_DUMP_AT_KERNEL_ENABLE,
	QDF_DBG_ARR,
	QDF_LOG_FLUSH_TIMER_PERIOD,
	QDF_PARAM_MAX,
};

static char qdf_module_param[QDF_PARAM_MAX][QDF_PARAM_STR_LENGTH] = {
	"mem_debug_disabled",
	"qdf_dbg_mask",
	"prealloc_disabled",
	"qdf_log_dump_at_kernel_enable",
	"qdf_dbg_arr",
	"qdf_log_flush_timer_period",
};
#endif

int qdf_snprintf(char *str_buffer, unsigned int size, char *str_format, ...)
{
	va_list args;
	int i;

	va_start(args, str_format);
	i = vsnprintf(str_buffer, size, str_format, args);
	va_end(args);

	return i;
}
qdf_export_symbol(qdf_snprintf);

#ifdef QDF_ENABLE_TRACING

void qdf_trace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
		   const char *str_format, ...)
{
	va_list val;

	va_start(val, str_format);
	qdf_trace_msg_cmn(qdf_pidx, module, level, str_format, val);
	va_end(val);
}
qdf_export_symbol(qdf_trace_msg);

void qdf_vtrace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
		    const char *str_format, va_list val)
{
	qdf_trace_msg_cmn(qdf_pidx, module, level, str_format, val);
}
qdf_export_symbol(qdf_vtrace_msg);

#define ROW_SIZE 16
/* Buffer size = data bytes(2 hex chars plus space) + NULL */
#define BUFFER_SIZE ((QDF_DP_TRACE_RECORD_SIZE * 3) + 1)

static void __qdf_trace_hex_dump(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
				 void *data, int buf_len, bool print_ascii)
{
	const u8 *ptr = data;
	int i = 0;

	if (!qdf_print_is_verbose_enabled(qdf_pidx, module, level))
		return;

	while (buf_len > 0) {
		unsigned char linebuf[BUFFER_SIZE] = {0};
		int linelen = min(buf_len, ROW_SIZE);

		buf_len -= ROW_SIZE;

		hex_dump_to_buffer(ptr, linelen, ROW_SIZE, 1,
				   linebuf, sizeof(linebuf), print_ascii);

		qdf_trace_msg(module, level, "%.8x: %s", i, linebuf);
		ptr += ROW_SIZE;
		i += ROW_SIZE;
	}
}

void qdf_trace_hex_dump(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
			void *data, int buf_len)
{
	__qdf_trace_hex_dump(module, level, data, buf_len, false);
}

qdf_export_symbol(qdf_trace_hex_dump);

void qdf_trace_hex_ascii_dump(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
			      void *data, int buf_len)
{
	__qdf_trace_hex_dump(module, level, data, buf_len, true);
}

qdf_export_symbol(qdf_trace_hex_ascii_dump);

#endif

#ifdef TRACE_RECORD

#ifdef WLAN_LOGGING_BUFFERS_DYNAMICALLY
static inline QDF_STATUS allocate_g_qdf_trace_tbl_buffer(void)
{
	g_qdf_trace_tbl = qdf_mem_valloc(MAX_QDF_TRACE_RECORDS *
					 sizeof(*g_qdf_trace_tbl));
	QDF_BUG(g_qdf_trace_tbl);
	return g_qdf_trace_tbl ? QDF_STATUS_SUCCESS : QDF_STATUS_E_NOMEM;
}

static inline void free_g_qdf_trace_tbl_buffer(void)
{
	qdf_mem_vfree(g_qdf_trace_tbl);
	g_qdf_trace_tbl = NULL;
}
#else
static inline QDF_STATUS allocate_g_qdf_trace_tbl_buffer(void)
{
	return QDF_STATUS_SUCCESS;
}

static inline void free_g_qdf_trace_tbl_buffer(void)
{ }
#endif
void qdf_trace_enable(uint32_t bitmask_of_module_id, uint8_t enable)
{
	int i;

	if (bitmask_of_module_id) {
		for (i = 0; i < QDF_MODULE_ID_MAX; i++) {
			if (((bitmask_of_module_id >> i) & 1)) {
				if (enable) {
					if (NULL !=
					    qdf_trace_restore_cb_table[i]) {
						qdf_trace_cb_table[i] =
						qdf_trace_restore_cb_table[i];
					}
				} else {
					qdf_trace_restore_cb_table[i] =
						qdf_trace_cb_table[i];
					qdf_trace_cb_table[i] = NULL;
				}
			}
		}
	} else {
		if (enable) {
			for (i = 0; i < QDF_MODULE_ID_MAX; i++) {
				if (qdf_trace_restore_cb_table[i]) {
					qdf_trace_cb_table[i] =
						qdf_trace_restore_cb_table[i];
				}
			}
		} else {
			for (i = 0; i < QDF_MODULE_ID_MAX; i++) {
				qdf_trace_restore_cb_table[i] =
					qdf_trace_cb_table[i];
				qdf_trace_cb_table[i] = NULL;
			}
		}
	}
}
qdf_export_symbol(qdf_trace_enable);

void qdf_trace_init(void)
{
	uint8_t i;

	if (allocate_g_qdf_trace_tbl_buffer() != QDF_STATUS_SUCCESS)
		return;
	g_qdf_trace_data.head = INVALID_QDF_TRACE_ADDR;
	g_qdf_trace_data.tail = INVALID_QDF_TRACE_ADDR;
	g_qdf_trace_data.num = 0;
	g_qdf_trace_data.enable = true;
	g_qdf_trace_data.dump_count = DEFAULT_QDF_TRACE_DUMP_COUNT;
	g_qdf_trace_data.num_since_last_dump = 0;

	for (i = 0; i < QDF_MODULE_ID_MAX; i++) {
		qdf_trace_cb_table[i] = NULL;
		qdf_trace_restore_cb_table[i] = NULL;
	}
}
qdf_export_symbol(qdf_trace_init);

void qdf_trace_deinit(void)
{
	g_qdf_trace_data.enable = false;
	g_qdf_trace_data.num = 0;
	g_qdf_trace_data.head = INVALID_QDF_TRACE_ADDR;
	g_qdf_trace_data.tail = INVALID_QDF_TRACE_ADDR;

	free_g_qdf_trace_tbl_buffer();
}

qdf_export_symbol(qdf_trace_deinit);

void qdf_trace(uint8_t module, uint16_t code, uint16_t session, uint32_t data)
{
	tp_qdf_trace_record rec = NULL;
	unsigned long flags;
	char time[18];

	if (!g_qdf_trace_data.enable)
		return;

	/* if module is not registered, don't record for that module */
	if (!qdf_trace_cb_table[module])
		return;

	qdf_get_time_of_the_day_in_hr_min_sec_usec(time, sizeof(time));
	/* Acquire the lock so that only one thread at a time can fill the ring
	 * buffer
	 */
	spin_lock_irqsave(&ltrace_lock, flags);

	g_qdf_trace_data.num++;

	if (g_qdf_trace_data.num > MAX_QDF_TRACE_RECORDS)
		g_qdf_trace_data.num = MAX_QDF_TRACE_RECORDS;

	if (INVALID_QDF_TRACE_ADDR == g_qdf_trace_data.head) {
		/* first record */
		g_qdf_trace_data.head = 0;
		g_qdf_trace_data.tail = 0;
	} else {
		/* queue is not empty */
		uint32_t tail = g_qdf_trace_data.tail + 1;

		if (MAX_QDF_TRACE_RECORDS == tail)
			tail = 0;

		if (g_qdf_trace_data.head == tail) {
			/* full */
			if (MAX_QDF_TRACE_RECORDS == ++g_qdf_trace_data.head)
				g_qdf_trace_data.head = 0;
		}
		g_qdf_trace_data.tail = tail;
	}

	rec = &g_qdf_trace_tbl[g_qdf_trace_data.tail];
	rec->code = code;
	rec->session = session;
	rec->data = data;
	rec->qtime = qdf_get_log_timestamp();
	scnprintf(rec->time, sizeof(rec->time), "%s", time);
	rec->module = module;
	rec->pid = (in_interrupt() ? 0 : current->pid);
	g_qdf_trace_data.num_since_last_dump++;
	spin_unlock_irqrestore(&ltrace_lock, flags);
}
qdf_export_symbol(qdf_trace);

#ifdef ENABLE_MTRACE_LOG
void qdf_mtrace_log(QDF_MODULE_ID src_module, QDF_MODULE_ID dst_module,
		    uint16_t message_id, uint8_t vdev_id)
{
	uint32_t trace_log, payload;
	static uint16_t counter;

	trace_log = (src_module << 23) | (dst_module << 15) | message_id;
	payload = (vdev_id << 16) | counter++;

	QDF_TRACE(src_module, QDF_TRACE_LEVEL_TRACE, "%x %x",
		  trace_log, payload);
}

qdf_export_symbol(qdf_mtrace_log);
#endif

void qdf_mtrace(QDF_MODULE_ID src_module, QDF_MODULE_ID dst_module,
		uint16_t message_id, uint8_t vdev_id, uint32_t data)
{
	qdf_trace(src_module, message_id, vdev_id, data);
	qdf_mtrace_log(src_module, dst_module, message_id, vdev_id);
}

qdf_export_symbol(qdf_mtrace);

QDF_STATUS qdf_trace_spin_lock_init(void)
{
	spin_lock_init(&ltrace_lock);

	return QDF_STATUS_SUCCESS;
}
qdf_export_symbol(qdf_trace_spin_lock_init);

void qdf_trace_register(QDF_MODULE_ID module_id,
			tp_qdf_trace_cb qdf_trace_callback)
{
	qdf_trace_cb_table[module_id] = qdf_trace_callback;
}
qdf_export_symbol(qdf_trace_register);

void qdf_trace_dump_all(void *p_mac, uint8_t code, uint8_t session,
	uint32_t count, uint32_t bitmask_of_module)
{
	qdf_trace_record_t p_record;
	int32_t i, tail;

	if (!g_qdf_trace_data.enable) {
		QDF_TRACE(QDF_MODULE_ID_SYS,
			  QDF_TRACE_LEVEL_ERROR, "Tracing Disabled");
		return;
	}

	QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_INFO,
		  "DPT: Total Records: %d, Head: %d, Tail: %d",
		  g_qdf_trace_data.num, g_qdf_trace_data.head,
		  g_qdf_trace_data.tail);

	/* acquire the lock so that only one thread at a time can read
	 * the ring buffer
	 */
	spin_lock(&ltrace_lock);

	if (g_qdf_trace_data.head != INVALID_QDF_TRACE_ADDR) {
		i = g_qdf_trace_data.head;
		tail = g_qdf_trace_data.tail;

		if (count) {
			if (count > g_qdf_trace_data.num)
				count = g_qdf_trace_data.num;
			if (tail >= (count - 1))
				i = tail - count + 1;
			else if (count != MAX_QDF_TRACE_RECORDS)
				i = MAX_QDF_TRACE_RECORDS - ((count - 1) -
							     tail);
		}

		p_record = g_qdf_trace_tbl[i];
		/* right now we are not using num_since_last_dump member but
		 * in future we might re-visit and use this member to track
		 * how many latest messages got added while we were dumping
		 * from ring buffer
		 */
		g_qdf_trace_data.num_since_last_dump = 0;
		spin_unlock(&ltrace_lock);
		for (;; ) {
			if ((code == 0 || (code == p_record.code)) &&
			    (qdf_trace_cb_table[p_record.module])) {
				if (0 == bitmask_of_module) {
					qdf_trace_cb_table[p_record.
							   module] (p_mac,
								    &p_record,
								    (uint16_t)
								    i);
				} else {
					if (bitmask_of_module &
					    (1 << p_record.module)) {
						qdf_trace_cb_table[p_record.
								   module]
							(p_mac, &p_record,
							(uint16_t) i);
					}
				}
			}

			if (i == tail)
				break;
			i += 1;

			spin_lock(&ltrace_lock);
			if (MAX_QDF_TRACE_RECORDS == i) {
				i = 0;
				p_record = g_qdf_trace_tbl[0];
			} else {
				p_record = g_qdf_trace_tbl[i];
			}
			spin_unlock(&ltrace_lock);
		}
	} else {
		spin_unlock(&ltrace_lock);
	}
}
qdf_export_symbol(qdf_trace_dump_all);
#endif

#ifdef WLAN_FEATURE_MEMDUMP_ENABLE
void qdf_register_debugcb_init(void)
{
	uint8_t i;

	for (i = 0; i < QDF_MODULE_ID_MAX; i++)
		qdf_state_info_table[i] = NULL;
}
qdf_export_symbol(qdf_register_debugcb_init);

void qdf_register_debug_callback(QDF_MODULE_ID module_id,
					tp_qdf_state_info_cb qdf_state_infocb)
{
	qdf_state_info_table[module_id] = qdf_state_infocb;
}
qdf_export_symbol(qdf_register_debug_callback);

QDF_STATUS qdf_state_info_dump_all(char *buf, uint16_t size,
				   uint16_t *driver_dump_size)
{
	uint8_t module, ret = QDF_STATUS_SUCCESS;
	uint16_t buf_len = size;
	char *buf_ptr = buf;

	for (module = 0; module < QDF_MODULE_ID_MAX; module++) {
		if (qdf_state_info_table[module]) {
			qdf_state_info_table[module](&buf_ptr, &buf_len);
			if (!buf_len) {
				ret = QDF_STATUS_E_NOMEM;
				break;
			}
		}
	}

	*driver_dump_size = size - buf_len;
	return ret;
}
qdf_export_symbol(qdf_state_info_dump_all);
#endif

#ifdef CONFIG_DP_TRACE

#ifdef WLAN_LOGGING_BUFFERS_DYNAMICALLY
static inline QDF_STATUS allocate_g_qdf_dp_trace_tbl_buffer(void)
{
	g_qdf_dp_trace_tbl = qdf_mem_valloc(MAX_QDF_DP_TRACE_RECORDS *
					    sizeof(*g_qdf_dp_trace_tbl));
	QDF_BUG(g_qdf_dp_trace_tbl);
	return g_qdf_dp_trace_tbl ? QDF_STATUS_SUCCESS : QDF_STATUS_E_NOMEM;
}

static inline void free_g_qdf_dp_trace_tbl_buffer(void)
{
	qdf_mem_vfree(g_qdf_dp_trace_tbl);
	g_qdf_dp_trace_tbl = NULL;
}
#else
static inline QDF_STATUS allocate_g_qdf_dp_trace_tbl_buffer(void)
{
	return QDF_STATUS_SUCCESS;
}

static inline void free_g_qdf_dp_trace_tbl_buffer(void)
{ }
#endif

#define QDF_DP_TRACE_PREPEND_STR_SIZE 100
/*
 * one dp trace record can't be greater than 300 bytes.
 * Max Size will be QDF_DP_TRACE_PREPEND_STR_SIZE(100) + BUFFER_SIZE(121).
 * Always make sure to change this QDF_DP_TRACE_MAX_RECORD_SIZE
 * value accordingly whenever above two mentioned MACRO value changes.
 */
#define QDF_DP_TRACE_MAX_RECORD_SIZE 300

static void qdf_dp_unused(struct qdf_dp_trace_record_s *record,
			  uint16_t index, uint8_t pdev_id, uint8_t info)
{
	qdf_print("%s: QDF_DP_TRACE_MAX event should not be generated",
		  __func__);
}

void qdf_dp_trace_init(bool live_mode_config, uint8_t thresh,
				uint16_t time_limit, uint8_t verbosity,
				uint32_t proto_bitmap)
{
	uint8_t i;

	if (allocate_g_qdf_dp_trace_tbl_buffer() != QDF_STATUS_SUCCESS) {
		QDF_TRACE_ERROR(QDF_MODULE_ID_QDF,
				"Failed!!! DP Trace buffer allocation");
		return;
	}
	qdf_dp_trace_spin_lock_init();
	qdf_dp_trace_clear_buffer();
	g_qdf_dp_trace_data.enable = true;
	g_qdf_dp_trace_data.no_of_record = 1;

	g_qdf_dp_trace_data.live_mode_config = live_mode_config;
	g_qdf_dp_trace_data.live_mode = live_mode_config;
	g_qdf_dp_trace_data.high_tput_thresh = thresh;
	g_qdf_dp_trace_data.thresh_time_limit = time_limit;
	g_qdf_dp_trace_data.proto_bitmap = proto_bitmap;
	g_qdf_dp_trace_data.verbosity = verbosity;
	g_qdf_dp_trace_data.ini_conf_verbosity = verbosity;

	for (i = 0; i < ARRAY_SIZE(qdf_dp_trace_cb_table); i++)
		qdf_dp_trace_cb_table[i] = qdf_dp_display_record;

	qdf_dp_trace_cb_table[QDF_DP_TRACE_HDD_TX_PACKET_RECORD] =
		qdf_dp_trace_cb_table[QDF_DP_TRACE_HDD_RX_PACKET_RECORD] =
		qdf_dp_trace_cb_table[QDF_DP_TRACE_TX_PACKET_RECORD] =
		qdf_dp_trace_cb_table[QDF_DP_TRACE_RX_PACKET_RECORD] =
		qdf_dp_trace_cb_table[QDF_DP_TRACE_DROP_PACKET_RECORD] =
		qdf_dp_trace_cb_table[QDF_DP_TRACE_LI_DP_TX_PACKET_RECORD] =
		qdf_dp_trace_cb_table[QDF_DP_TRACE_LI_DP_RX_PACKET_RECORD] =
		qdf_dp_display_data_pkt_record;

	qdf_dp_trace_cb_table[QDF_DP_TRACE_TXRX_PACKET_PTR_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_FREE_PACKET_PTR_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_LI_DP_FREE_PACKET_PTR_RECORD] =
						qdf_dp_display_ptr_record;
	qdf_dp_trace_cb_table[QDF_DP_TRACE_EAPOL_PACKET_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_DHCP_PACKET_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_ARP_PACKET_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_ICMP_PACKET_RECORD] =
	qdf_dp_trace_cb_table[QDF_DP_TRACE_ICMPv6_PACKET_RECORD] =
						qdf_dp_display_proto_pkt;
	qdf_dp_trace_cb_table[QDF_DP_TRACE_MGMT_PACKET_RECORD] =
					qdf_dp_display_mgmt_pkt;
	qdf_dp_trace_cb_table[QDF_DP_TRACE_TX_CREDIT_RECORD] =
					qdf_dp_display_credit_record;
	qdf_dp_trace_cb_table[QDF_DP_TRACE_EVENT_RECORD] =
					qdf_dp_display_event_record;

	qdf_dp_trace_cb_table[QDF_DP_TRACE_MAX] = qdf_dp_unused;
}
qdf_export_symbol(qdf_dp_trace_init);

void qdf_dp_trace_deinit(void)
{
	if (!g_qdf_dp_trace_data.enable)
		return;
	spin_lock_bh(&l_dp_trace_lock);
	g_qdf_dp_trace_data.enable = false;
	g_qdf_dp_trace_data.no_of_record = 0;
	spin_unlock_bh(&l_dp_trace_lock);

	free_g_qdf_dp_trace_tbl_buffer();
}

void qdf_dp_trace_set_value(uint32_t proto_bitmap, uint8_t no_of_record,
			    uint8_t verbosity)
{
	g_qdf_dp_trace_data.proto_bitmap = proto_bitmap;
	g_qdf_dp_trace_data.no_of_record = no_of_record;
	g_qdf_dp_trace_data.verbosity    = verbosity;
	g_qdf_dp_trace_data.dynamic_verbosity_modify = true;
}
qdf_export_symbol(qdf_dp_trace_set_value);

void qdf_dp_trace_set_verbosity(uint32_t val)
{
	g_qdf_dp_trace_data.verbosity = val;
}
qdf_export_symbol(qdf_dp_trace_set_verbosity);

/**
 * qdf_dp_get_verbosity() - get verbosity value
 *
 * Return: int
 */
uint8_t qdf_dp_get_verbosity(void)
{
	return g_qdf_dp_trace_data.verbosity;
}
qdf_export_symbol(qdf_dp_get_verbosity);

void qdf_dp_set_proto_bitmap(uint32_t val)
{
	g_qdf_dp_trace_data.proto_bitmap = val;
}
qdf_export_symbol(qdf_dp_set_proto_bitmap);

void qdf_dp_set_proto_event_bitmap(uint32_t value)
{
	g_qdf_dp_trace_data.proto_event_bitmap = value;
}

qdf_export_symbol(qdf_dp_set_proto_event_bitmap);

static uint32_t qdf_dp_get_proto_event_bitmap(void)
{
	return g_qdf_dp_trace_data.proto_event_bitmap;
}

void qdf_dp_set_no_of_record(uint32_t val)
{
	g_qdf_dp_trace_data.no_of_record = val;
}
qdf_export_symbol(qdf_dp_set_no_of_record);

uint8_t qdf_dp_get_no_of_record(void)
{
	return g_qdf_dp_trace_data.no_of_record;
}
qdf_export_symbol(qdf_dp_get_no_of_record);


/**
 * qdf_dp_trace_verbosity_check() - check whether verbosity level is enabled
 * @code: defines the event
 *
 * In High verbosity all codes are logged.
 * For Med/Low and Default case code which has
 * less value than corresponding verbosity codes
 * are logged.
 *
 * Return: true or false depends on whether tracing enabled
 */
static bool qdf_dp_trace_verbosity_check(enum QDF_DP_TRACE_ID code)
{
	switch (g_qdf_dp_trace_data.verbosity) {
	case QDF_DP_TRACE_VERBOSITY_HIGH:
		return true;
	case QDF_DP_TRACE_VERBOSITY_MEDIUM:
		if (code <= QDF_DP_TRACE_MED_VERBOSITY)
			return true;
		return false;
	case QDF_DP_TRACE_VERBOSITY_LOW:
		if (code <= QDF_DP_TRACE_LOW_VERBOSITY)
			return true;
		return false;
	case QDF_DP_TRACE_VERBOSITY_ULTRA_LOW:
		if (code <= QDF_DP_TRACE_ULTRA_LOW_VERBOSITY)
			return true;
		return false;
	case QDF_DP_TRACE_VERBOSITY_BASE:
		if (code <= QDF_DP_TRACE_BASE_VERBOSITY)
			return true;
		return false;
	default:
		return false;
	}
}

uint32_t qdf_dp_get_proto_bitmap(void)
{
	if (g_qdf_dp_trace_data.enable)
		return g_qdf_dp_trace_data.proto_bitmap;
	else
		return 0;
}

void qdf_dp_trace_set_track(qdf_nbuf_t nbuf, enum qdf_proto_dir dir)
{
	uint32_t count = 0;

	if (!g_qdf_dp_trace_data.enable)
		return;

	spin_lock_bh(&l_dp_trace_lock);
	if (QDF_TX == dir)
		count = ++g_qdf_dp_trace_data.tx_count;
	else if (QDF_RX == dir)
		count = ++g_qdf_dp_trace_data.rx_count;

	if ((g_qdf_dp_trace_data.no_of_record != 0) &&
		(count % g_qdf_dp_trace_data.no_of_record == 0)) {
		if (QDF_TX == dir)
			QDF_NBUF_CB_TX_DP_TRACE(nbuf) = 1;
		else if (QDF_RX == dir)
			QDF_NBUF_CB_RX_DP_TRACE(nbuf) = 1;
	}
	spin_unlock_bh(&l_dp_trace_lock);
}
qdf_export_symbol(qdf_dp_trace_set_track);

/* Number of bytes to be grouped together while printing DP-Trace data */
#define QDF_DUMP_DP_GROUP_SIZE 6

/**
 * dump_dp_hex_trace() - Display the data in buffer
 * @prepend_str:     string to prepend the hexdump with.
 * @inbuf:     buffer which contains data to be displayed
 * @inbuf_len: defines the size of the data to be displayed
 *
 * Return: None
 */
static void
dump_dp_hex_trace(char *prepend_str, uint8_t *inbuf, uint8_t inbuf_len)
{
	unsigned char outbuf[BUFFER_SIZE];
	const uint8_t *inbuf_ptr = inbuf;
	char *outbuf_ptr = outbuf;
	int outbytes_written = 0;

	qdf_mem_zero(outbuf, sizeof(outbuf));
	do {
		outbytes_written += scnprintf(outbuf_ptr,
					BUFFER_SIZE - outbytes_written,
					"%02x", *inbuf_ptr);
		outbuf_ptr = outbuf + outbytes_written;

		if ((inbuf_ptr - inbuf) &&
		    (inbuf_ptr - inbuf + 1) % QDF_DUMP_DP_GROUP_SIZE == 0) {
			outbytes_written += scnprintf(outbuf_ptr,
						BUFFER_SIZE - outbytes_written,
						" ");
			outbuf_ptr = outbuf + outbytes_written;
		}
		inbuf_ptr++;
	} while (inbuf_ptr < (inbuf + inbuf_len));
	DPTRACE_PRINT("%s %s", prepend_str, outbuf);
}

/**
 * qdf_dp_code_to_string() - convert dptrace code to string
 * @code: dptrace code
 *
 * Return: string version of code
 */
static
const char *qdf_dp_code_to_string(enum QDF_DP_TRACE_ID code)
{
	switch (code) {
	case QDF_DP_TRACE_DROP_PACKET_RECORD:
		return "DROP:";
	case QDF_DP_TRACE_EAPOL_PACKET_RECORD:
		return "EAPOL:";
	case QDF_DP_TRACE_DHCP_PACKET_RECORD:
		return "DHCP:";
	case QDF_DP_TRACE_ARP_PACKET_RECORD:
		return "ARP:";
	case QDF_DP_TRACE_ICMP_PACKET_RECORD:
		return "ICMP:";
	case QDF_DP_TRACE_ICMPv6_PACKET_RECORD:
		return "ICMPv6:";
	case QDF_DP_TRACE_MGMT_PACKET_RECORD:
		return "MGMT:";
	case QDF_DP_TRACE_TX_CREDIT_RECORD:
		return "CREDIT:";
	case QDF_DP_TRACE_EVENT_RECORD:
		return "EVENT:";
	case QDF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD:
		return "HDD: TX: PTR:";
	case QDF_DP_TRACE_LI_DP_TX_PACKET_PTR_RECORD:
		return "LI_DP: TX: PTR:";
	case QDF_DP_TRACE_HDD_TX_PACKET_RECORD:
		return "HDD: TX: DATA:";
	case QDF_DP_TRACE_LI_DP_TX_PACKET_RECORD:
	case QDF_DP_TRACE_TX_PACKET_RECORD:
		return "TX:";
	case QDF_DP_TRACE_CE_PACKET_PTR_RECORD:
		return "CE: TX: PTR:";
	case QDF_DP_TRACE_CE_FAST_PACKET_PTR_RECORD:
		return "CE: TX: FAST: PTR:";
	case QDF_DP_TRACE_CE_FAST_PACKET_ERR_RECORD:
		return "CE: TX: FAST: ERR:";
	case QDF_DP_TRACE_LI_DP_FREE_PACKET_PTR_RECORD:
	case QDF_DP_TRACE_FREE_PACKET_PTR_RECORD:
		return "FREE: TX: PTR:";
	case QDF_DP_TRACE_RX_HTT_PACKET_PTR_RECORD:
		return "HTT: RX: PTR:";
	case QDF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD:
		return "HTT: RX: OF: PTR:";
	case QDF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD:
		return "HDD: RX: PTR:";
	case QDF_DP_TRACE_RX_LI_DP_PACKET_PTR_RECORD:
		return "LI_DP: RX: PTR:";
	case QDF_DP_TRACE_HDD_RX_PACKET_RECORD:
		return "HDD: RX: DATA:";
	case QDF_DP_TRACE_LI_DP_NULL_RX_PACKET_RECORD:
		return "LI_DP_NULL: RX: DATA:";
	case QDF_DP_TRACE_LI_DP_RX_PACKET_RECORD:
	case QDF_DP_TRACE_RX_PACKET_RECORD:
		return "RX:";
	case QDF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD:
		return "TXRX: TX: Q: PTR:";
	case QDF_DP_TRACE_TXRX_PACKET_PTR_RECORD:
		return "TXRX: TX: PTR:";
	case QDF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD:
		return "TXRX: TX: FAST: PTR:";
	case QDF_DP_TRACE_HTT_PACKET_PTR_RECORD:
		return "HTT: TX: PTR:";
	case QDF_DP_TRACE_HTC_PACKET_PTR_RECORD:
		return "HTC: TX: PTR:";
	case QDF_DP_TRACE_HIF_PACKET_PTR_RECORD:
		return "HIF: TX: PTR:";
	case QDF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD:
		return "TXRX: RX: PTR:";
	case QDF_DP_TRACE_HDD_TX_TIMEOUT:
		return "HDD: STA: TO:";
	case QDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
		return "HDD: SAP: TO:";
	default:
		return "Invalid";
	}
}

/**
 * qdf_dp_dir_to_str() - convert direction to string
 * @dir: direction
 *
 * Return: string version of direction
 */
static const char *qdf_dp_dir_to_str(enum qdf_proto_dir dir)
{
	switch (dir) {
	case QDF_TX:
		return " --> ";
	case QDF_RX:
		return " <-- ";
	default:
		return "invalid";
	}
}

static const char *qdf_dp_credit_source_to_str(
		enum QDF_CREDIT_UPDATE_SOURCE source)
{
	switch (source) {
	case QDF_TX_SCHED:
		return "TX SCHED";
	case QDF_TX_COMP:
		return "TX COMP";
	case QDF_TX_CREDIT_UPDATE:
		return "CREDIT UP";
	case QDF_TX_HTT_MSG:
		return "HTT TX MSG";
	case QDF_HTT_ATTACH:
		return "HTT ATTACH";
	default:
		return "invalid";
	}
}

static const char *qdf_dp_operation_to_str(enum QDF_CREDIT_OPERATION op)
{
	switch (op) {
	case QDF_CREDIT_INC:
		return "+";
	case QDF_CREDIT_DEC:
		return "-";
	case QDF_CREDIT_ABS:
		return "ABS";
	default:
		return "invalid";
	}
}

/**
 * qdf_dp_type_to_str() - convert packet type to string
 * @type: type
 *
 * Return: string version of packet type
 */
static const char *qdf_dp_type_to_str(enum qdf_proto_type type)
{
	switch (type) {
	case QDF_PROTO_TYPE_DHCP:
		return "DHCP";
	case QDF_PROTO_TYPE_EAPOL:
		return "EAPOL";
	case QDF_PROTO_TYPE_ARP:
		return "ARP";
	case QDF_PROTO_TYPE_ICMP:
		return "ICMP";
	case QDF_PROTO_TYPE_ICMPv6:
		return "ICMPv6";
	case QDF_PROTO_TYPE_MGMT:
		return "MGMT";
	case QDF_PROTO_TYPE_EVENT:
		return "EVENT";
	default:
		return "invalid";
	}
}

/**
 * qdf_dp_subtype_to_str() - convert packet subtype to string
 * @subtype: subtype
 *
 * Return: string version of packet subtype
 */
static const char *qdf_dp_subtype_to_str(enum qdf_proto_subtype subtype)
{
	switch (subtype) {
	case QDF_PROTO_EAPOL_M1:
		return "M1";
	case QDF_PROTO_EAPOL_M2:
		return "M2";
	case QDF_PROTO_EAPOL_M3:
		return "M3";
	case QDF_PROTO_EAPOL_M4:
		return "M4";
	case QDF_PROTO_DHCP_DISCOVER:
		return "DISC";
	case QDF_PROTO_DHCP_REQUEST:
		return "REQ";
	case QDF_PROTO_DHCP_OFFER:
		return "OFF";
	case QDF_PROTO_DHCP_ACK:
		return "ACK";
	case QDF_PROTO_DHCP_NACK:
		return "NACK";
	case QDF_PROTO_DHCP_RELEASE:
		return "REL";
	case QDF_PROTO_DHCP_INFORM:
		return "INFORM";
	case QDF_PROTO_DHCP_DECLINE:
		return "DECL";
	case QDF_PROTO_ARP_REQ:
	case QDF_PROTO_ICMP_REQ:
	case QDF_PROTO_ICMPV6_REQ:
		return "REQ";
	case QDF_PROTO_ARP_RES:
	case QDF_PROTO_ICMP_RES:
	case QDF_PROTO_ICMPV6_RES:
		return "RSP";
	case QDF_PROTO_ICMPV6_RS:
		return "RS";
	case QDF_PROTO_ICMPV6_RA:
		return "RA";
	case QDF_PROTO_ICMPV6_NS:
		return "NS";
	case QDF_PROTO_ICMPV6_NA:
		return "NA";
	case QDF_PROTO_MGMT_ASSOC:
		return "ASSOC";
	case QDF_PROTO_MGMT_DISASSOC:
		return "DISASSOC";
	case QDF_PROTO_MGMT_AUTH:
		return "AUTH";
	case QDF_PROTO_MGMT_DEAUTH:
		return "DEAUTH";
	case QDF_ROAM_SYNCH:
		return "ROAM SYNCH";
	case QDF_ROAM_COMPLETE:
		return "ROAM COMP";
	case QDF_ROAM_EVENTID:
		return "ROAM EVENTID";
	case QDF_PROTO_EAP_REQUEST:
		return "EAP REQ";
	case QDF_PROTO_EAP_RESPONSE:
		return "EAP RSP";
	case QDF_PROTO_EAP_SUCCESS:
		return "EAP SUC";
	case QDF_PROTO_EAP_FAILURE:
		return "EAP FAIL";
	case QDF_PROTO_EAP_INITIATE:
		return "EAP INIT";
	case QDF_PROTO_EAP_FINISH:
		return "EAP FINISH";
	case QDF_PROTO_EAPOL_START:
		return "START";
	case QDF_PROTO_EAPOL_LOGOFF:
		return "LOGOFF";
	case QDF_PROTO_EAPOL_ASF:
		return "ASF";
	case QDF_PROTO_EAP_REQ_ID:
		return "EAP REQ ID";
	case QDF_PROTO_EAP_RSP_ID:
		return "EAP RSP ID";
	case QDF_PROTO_EAP_M1:
		return "EAP M1";
	case QDF_PROTO_EAP_M2:
		return "EAP M2";
	case QDF_PROTO_EAP_M3:
		return "EAP M3";
	case QDF_PROTO_EAP_M4:
		return "EAP M4";
	case QDF_PROTO_EAP_M5:
		return "EAP M5";
	case QDF_PROTO_EAP_M6:
		return "EAP M6";
	case QDF_PROTO_EAP_M7:
		return "EAP M7";
	case QDF_PROTO_EAP_M8:
		return "EAP M8";
	case QDF_PROTO_EAP_WSC_START:
		return "EAP WSC START";
	case QDF_PROTO_EAP_WSC_DONE:
		return "EAP WSC DONE";
	case QDF_PROTO_EAP_WSC_ACK:
		return "EAP WSC ACK";
	case QDF_PROTO_EAP_WSC_NACK:
		return "EAP WSC NACK";
	case QDF_PROTO_EAP_WSC_FRAG_ACK:
		return "EAP WSC FRAG ACK";
	default:
		return "invalid";
	}
}

/**
 * qdf_dp_enable_check() - check if dptrace, TX/RX tracing is enabled
 * @nbuf: nbuf
 * @code: dptrace code
 * @dir: TX or RX direction
 *
 * Return: true/false
 */
static bool qdf_dp_enable_check(qdf_nbuf_t nbuf, enum QDF_DP_TRACE_ID code,
				enum qdf_proto_dir dir)
{
	/* Return when Dp trace is not enabled */
	if (!g_qdf_dp_trace_data.enable)
		return false;

	if (qdf_dp_trace_verbosity_check(code) == false)
		return false;

	if (nbuf && (dir == QDF_TX && ((QDF_NBUF_CB_TX_DP_TRACE(nbuf) == 0) ||
				       (QDF_NBUF_CB_TX_PACKET_TRACK(nbuf) !=
					QDF_NBUF_TX_PKT_DATA_TRACK))))
		return false;

	if (nbuf && (dir == QDF_RX && (QDF_NBUF_CB_RX_DP_TRACE(nbuf) == 0)))
		return false;

	/*
	 * Special packets called with NULL nbuf and this API is expected to
	 * return true
	 */
	return true;
}

/**
 * qdf_dp_trace_fill_meta_str() - fill up a common meta string
 * @prepend_str: pointer to string
 * @size: size of prepend_str
 * @rec_index: index of record
 * @info: info related to the record
 * @record: pointer to the record
 *
 * Return: ret value from scnprintf
 */
static inline
int qdf_dp_trace_fill_meta_str(char *prepend_str, int size,
			       int rec_index, uint8_t info,
			       struct qdf_dp_trace_record_s *record)
{
	char buffer[20];
	int ret = 0;
	bool live = info & QDF_DP_TRACE_RECORD_INFO_LIVE ? true : false;
	bool throttled = info & QDF_DP_TRACE_RECORD_INFO_THROTTLED ?
								true : false;

	scnprintf(buffer, sizeof(buffer), "%llu", record->time);
	ret = scnprintf(prepend_str, size,
			"%s DPT: %04d:%02d%s %s",
			throttled ? "*" : "",
			rec_index,
			record->pdev_id,
			live ? "" : buffer,
			qdf_dp_code_to_string(record->code));

	return ret;
}

/**
 * qdf_dp_fill_record_data() - fill meta data and data into the record
 * @rec: pointer to record data
 * @data: pointer to data
 * @data_size: size of the data
 * @meta_data: pointer to metadata
 * @metadata_size: size of metadata
 *
 * Should be called from within a spin_lock for the qdf record.
 * Fills up rec->data with |metadata|data|
 *
 * Return: none
 */
static void qdf_dp_fill_record_data
	(struct qdf_dp_trace_record_s *rec,
	uint8_t *data, uint8_t data_size,
	uint8_t *meta_data, uint8_t metadata_size)
{
	int32_t available = QDF_DP_TRACE_RECORD_SIZE;
	uint8_t *rec_data = rec->data;
	uint8_t data_to_copy = 0;

	qdf_mem_zero(rec_data, QDF_DP_TRACE_RECORD_SIZE);

	/* copy meta data */
	if (meta_data) {
		if (metadata_size > available) {
			QDF_TRACE_WARN(QDF_MODULE_ID_QDF,
				       "%s: meta data does not fit into the record",
				       __func__);
			goto end;
		}
		qdf_mem_copy(rec_data, meta_data, metadata_size);
		available = available - metadata_size;
	} else {
		metadata_size = 0;
	}

	/* copy data */
	if (data && (data_size > 0) && (available > 0)) {
		data_to_copy = data_size;
		if (data_size > available)
			data_to_copy = available;
		qdf_mem_copy(&rec_data[metadata_size], data, data_to_copy);
	}
end:
	rec->size = data_to_copy;
}

/**
 * qdf_dp_add_record() - add dp trace record
 * @code: dptrace code
 * @pdev_id: pdev_id
 * @print: true to print it in kmsg
 * @data: data pointer
 * @data_size: size of data to be copied
 * @meta_data: meta data to be prepended to data
 * @metadata_size: sizeof meta data
 * @print: whether to print record
 *
 * Return: none
 */
static void qdf_dp_add_record(enum QDF_DP_TRACE_ID code, uint8_t pdev_id,
			      uint8_t *data, uint8_t data_size,
			      uint8_t *meta_data, uint8_t metadata_size,
			      bool print)

{
	struct qdf_dp_trace_record_s *rec = NULL;
	int index;
	bool print_this_record = false;
	u8 info = 0;

	if (code >= QDF_DP_TRACE_MAX) {
		QDF_TRACE_ERROR(QDF_MODULE_ID_QDF,
				"invalid record code %u, max code %u",
				code, QDF_DP_TRACE_MAX);
		return;
	}

	spin_lock_bh(&l_dp_trace_lock);

	if (print || g_qdf_dp_trace_data.force_live_mode) {
		print_this_record = true;
	} else if (g_qdf_dp_trace_data.live_mode == 1) {
		print_this_record = true;
		g_qdf_dp_trace_data.print_pkt_cnt++;
		if (g_qdf_dp_trace_data.print_pkt_cnt >
				g_qdf_dp_trace_data.high_tput_thresh) {
			g_qdf_dp_trace_data.live_mode = 0;
			g_qdf_dp_trace_data.verbosity =
					QDF_DP_TRACE_VERBOSITY_ULTRA_LOW;
			info |= QDF_DP_TRACE_RECORD_INFO_THROTTLED;
		}
	}

	g_qdf_dp_trace_data.num++;

	if (g_qdf_dp_trace_data.num > MAX_QDF_DP_TRACE_RECORDS)
		g_qdf_dp_trace_data.num = MAX_QDF_DP_TRACE_RECORDS;

	if (INVALID_QDF_DP_TRACE_ADDR == g_qdf_dp_trace_data.head) {
		/* first record */
		g_qdf_dp_trace_data.head = 0;
		g_qdf_dp_trace_data.tail = 0;
	} else {
		/* queue is not empty */
		g_qdf_dp_trace_data.tail++;

		if (MAX_QDF_DP_TRACE_RECORDS == g_qdf_dp_trace_data.tail)
			g_qdf_dp_trace_data.tail = 0;

		if (g_qdf_dp_trace_data.head == g_qdf_dp_trace_data.tail) {
			/* full */
			if (MAX_QDF_DP_TRACE_RECORDS ==
				++g_qdf_dp_trace_data.head)
				g_qdf_dp_trace_data.head = 0;
		}
	}

	rec = &g_qdf_dp_trace_tbl[g_qdf_dp_trace_data.tail];
	index = g_qdf_dp_trace_data.tail;
	rec->code = code;
	rec->pdev_id = pdev_id;
	rec->size = 0;
	qdf_dp_fill_record_data(rec, data, data_size,
				meta_data, metadata_size);
	rec->time = qdf_get_log_timestamp();
	rec->pid = (in_interrupt() ? 0 : current->pid);

	if (rec->code >= QDF_DP_TRACE_MAX) {
		QDF_TRACE_ERROR(QDF_MODULE_ID_QDF,
				"invalid record code %u, max code %u",
				rec->code, QDF_DP_TRACE_MAX);
		return;
	}

	spin_unlock_bh(&l_dp_trace_lock);

	info |= QDF_DP_TRACE_RECORD_INFO_LIVE;
	if (print_this_record)
		qdf_dp_trace_cb_table[rec->code] (rec, index,
					QDF_TRACE_DEFAULT_PDEV_ID, info);
}

/**
 * qdf_get_rate_limit_by_type() - Get the rate limit by pkt type
 * @type: packet type
 *
 * Return: Rate limit value for a particular packet type
 */
static inline
uint8_t qdf_get_rate_limit_by_type(uint8_t type)
{
	switch (type) {
	case QDF_PROTO_TYPE_DHCP:
		return QDF_MAX_DHCP_PKTS_PER_SEC;
	case QDF_PROTO_TYPE_EAPOL:
		return QDF_MAX_EAPOL_PKTS_PER_SEC;
	case QDF_PROTO_TYPE_ARP:
		return QDF_MAX_ARP_PKTS_PER_SEC;
	case QDF_PROTO_TYPE_DNS:
		return QDF_MAX_DNS_PKTS_PER_SEC;
	default:
		return QDF_MAX_OTHER_PKTS_PER_SEC;
	}
}

/**
 * qdf_get_pkt_type_string() - Get the string based on pkt type
 * @type: packet type
 * @subtype: packet subtype
 *
 * Return: String based on pkt type
 */
static
uint8_t *qdf_get_pkt_type_string(uint8_t type, uint8_t subtype)
{
	switch (subtype) {
	case QDF_PROTO_EAPOL_M1:
		return "EAPOL-1";
	case QDF_PROTO_EAPOL_M2:
		return "EAPOL-2";
	case QDF_PROTO_EAPOL_M3:
		return "EAPOL-3";
	case QDF_PROTO_EAPOL_M4:
		return "EAPOL-4";
	case QDF_PROTO_DHCP_DISCOVER:
		return "DHCP-D";
	case QDF_PROTO_DHCP_REQUEST:
		return "DHCP-R";
	case QDF_PROTO_DHCP_OFFER:
		return "DHCP-O";
	case QDF_PROTO_DHCP_ACK:
		return "DHCP-A";
	case QDF_PROTO_DHCP_NACK:
		return "DHCP-NA";
	case QDF_PROTO_DHCP_RELEASE:
		return "DHCP-REL";
	case QDF_PROTO_DHCP_INFORM:
		return "DHCP-IN";
	case QDF_PROTO_DHCP_DECLINE:
		return "DHCP-DEC";
	case QDF_PROTO_ARP_REQ:
		return "ARP-RQ";
	case QDF_PROTO_ARP_RES:
		return "ARP-RS";
	case QDF_PROTO_DNS_QUERY:
		return "DNS_Q";
	case QDF_PROTO_DNS_RES:
		return "DNS_RS";
	case QDF_PROTO_EAP_REQUEST:
		return "EAP_REQ";
	case QDF_PROTO_EAP_RESPONSE:
		return "EAP-RSP";
	case QDF_PROTO_EAP_SUCCESS:
		return "EAP-SUCCESS";
	case QDF_PROTO_EAP_FAILURE:
		return "EAP-FAIL";
	case QDF_PROTO_EAP_INITIATE:
		return "EAP-INIT";
	case QDF_PROTO_EAP_FINISH:
		return "EAP-FINISH";
	case QDF_PROTO_EAPOL_START:
		return "EAPOL-START";
	case QDF_PROTO_EAPOL_LOGOFF:
		return "EAPOL-LOGOFF";
	case QDF_PROTO_EAPOL_ASF:
		return "EAPOL-ASF";
	case QDF_PROTO_EAP_REQ_ID:
		return "EAP-REQ-ID";
	case QDF_PROTO_EAP_RSP_ID:
		return "EAP-RSP-ID";
	case QDF_PROTO_EAP_M1:
		return "EAP-M1";
	case QDF_PROTO_EAP_M2:
		return "EAP-M2";
	case QDF_PROTO_EAP_M3:
		return "EAP-M3";
	case QDF_PROTO_EAP_M4:
		return "EAP-M4";
	case QDF_PROTO_EAP_M5:
		return "EAP-M5";
	case QDF_PROTO_EAP_M6:
		return "EAP-M6";
	case QDF_PROTO_EAP_M7:
		return "EAP-M7";
	case QDF_PROTO_EAP_M8:
		return "EAP-M8";
	case QDF_PROTO_EAP_WSC_START:
		return "EAP-WSC-START";
	case QDF_PROTO_EAP_WSC_DONE:
		return "EAP-WSC-DONE";
	case QDF_PROTO_EAP_WSC_ACK:
		return "EAP-WSC-ACK";
	case QDF_PROTO_EAP_WSC_NACK:
		return "EAP-WSC-NACK";
	case QDF_PROTO_EAP_WSC_FRAG_ACK:
		return "EAP-WSC-FRAG-ACK";
	default:
		switch (type) {
		case QDF_PROTO_TYPE_EAPOL:
			return "EAP";
		case QDF_PROTO_TYPE_DHCP:
			return "DHCP";
		case QDF_PROTO_TYPE_ARP:
			return "ARP";
		case QDF_PROTO_TYPE_DNS:
			return "DNS";
		default:
			return "UNKNOWN";
		}
	}
}

/**
 * qdf_get_pkt_status_string() - Get the string based on pkt status
 * @status: packet status
 *
 * Return: String based on pkt status
 */
static
uint8_t *qdf_get_pkt_status_string(uint8_t status)
{
	switch (status) {
	case QDF_TX_RX_STATUS_INVALID:
		return "inv";
	case QDF_TX_RX_STATUS_OK:
		return "succ";
	case QDF_TX_RX_STATUS_FW_DISCARD:
		return "disc";
	case QDF_TX_RX_STATUS_NO_ACK:
		return "nack";
	case QDF_TX_RX_STATUS_DROP:
		return "drop";
	default:
		return "unknown";
	}
}

void qdf_dp_log_proto_pkt_info(uint8_t *sa, uint8_t *da, uint8_t type,
			       uint8_t subtype, uint8_t dir, uint16_t msdu_id,
			       uint8_t status)
{
	uint8_t pkt_rate_limit;
	static ulong last_ticks_tx[QDF_PROTO_SUBTYPE_MAX] = {0};
	static ulong last_ticks_rx[QDF_PROTO_SUBTYPE_MAX] = {0};
	ulong curr_ticks = jiffies;

	pkt_rate_limit = qdf_get_rate_limit_by_type(type);

	if ((dir == QDF_TX &&
	     !time_after(curr_ticks,
			 last_ticks_tx[subtype] + HZ / pkt_rate_limit)) ||
	    (dir == QDF_RX &&
	     !time_after(curr_ticks,
			 last_ticks_rx[subtype] + HZ / pkt_rate_limit)))
		return;

	if (dir == QDF_TX)
		last_ticks_tx[subtype] = curr_ticks;
	else
		last_ticks_rx[subtype] = curr_ticks;

	if (status == QDF_TX_RX_STATUS_INVALID)
		qdf_nofl_info("%s %s: SA:" QDF_MAC_ADDR_FMT " DA:" QDF_MAC_ADDR_FMT,
			      qdf_get_pkt_type_string(type, subtype),
			      dir ? "RX" : "TX", QDF_MAC_ADDR_REF(sa),
			      QDF_MAC_ADDR_REF(da));
	else
		qdf_nofl_info("%s %s: SA:" QDF_MAC_ADDR_FMT " DA:" QDF_MAC_ADDR_FMT " msdu_id:%d status: %s",
			      qdf_get_pkt_type_string(type, subtype),
			      dir ? "RX" : "TX", QDF_MAC_ADDR_REF(sa),
			      QDF_MAC_ADDR_REF(da), msdu_id,
			      qdf_get_pkt_status_string(status));
}

qdf_export_symbol(qdf_dp_log_proto_pkt_info);

/**
 * qdf_log_icmpv6_pkt() - log ICMPv6 packet
 * @vdev_id: ID of the vdev
 * @skb: skb pointer
 * @dir: direction
 * @pdev_id: ID of the pdev
 *
 * Return: true/false
 */
static bool qdf_log_icmpv6_pkt(uint8_t vdev_id, struct sk_buff *skb,
			       enum qdf_proto_dir dir, uint8_t pdev_id)
{
	enum qdf_proto_subtype subtype;
	struct qdf_dp_trace_proto_cmn cmn_info;

	if ((qdf_dp_get_proto_bitmap() & QDF_NBUF_PKT_TRAC_TYPE_ICMPv6) &&
		((dir == QDF_TX && QDF_NBUF_CB_PACKET_TYPE_ICMPv6 ==
			QDF_NBUF_CB_GET_PACKET_TYPE(skb)) ||
		 (dir == QDF_RX && qdf_nbuf_is_icmpv6_pkt(skb) == true))) {

		subtype = qdf_nbuf_get_icmpv6_subtype(skb);

		QDF_NBUF_CB_DP_TRACE_PRINT(skb) = false;
		if (dir == QDF_TX)
			QDF_NBUF_CB_TX_DP_TRACE(skb) = 1;
		else if (dir == QDF_RX)
			QDF_NBUF_CB_RX_DP_TRACE(skb) = 1;

		cmn_info.vdev_id = vdev_id;
		cmn_info.type = QDF_PROTO_TYPE_ICMPv6;
		cmn_info.subtype = subtype;
		cmn_info.proto_priv_data = 0;
		cmn_info.mpdu_seq = qdf_nbuf_get_mpdu_seq_num(skb);
		DPTRACE(qdf_dp_trace_proto_pkt(
			QDF_DP_TRACE_ICMPv6_PACKET_RECORD,
			(skb->data + QDF_NBUF_SRC_MAC_OFFSET),
			(skb->data + QDF_NBUF_DEST_MAC_OFFSET),
			dir, pdev_id, false, &cmn_info));

		switch (subtype) {
		case QDF_PROTO_ICMPV6_REQ:
			g_qdf_dp_trace_data.icmpv6_req++;
			break;
		case QDF_PROTO_ICMPV6_RES:
			g_qdf_dp_trace_data.icmpv6_resp++;
			break;
		case QDF_PROTO_ICMPV6_RS:
			g_qdf_dp_trace_data.icmpv6_rs++;
			break;
		case QDF_PROTO_ICMPV6_RA:
			g_qdf_dp_trace_data.icmpv6_ra++;
			break;
		case QDF_PROTO_ICMPV6_NS:
			g_qdf_dp_trace_data.icmpv6_ns++;
			break;
		case QDF_PROTO_ICMPV6_NA:
			g_qdf_dp_trace_data.icmpv6_na++;
			break;
		default:
			break;
		}
		return true;
	}

	return false;
}

/**
 * qdf_log_icmp_pkt() - log ICMP packet
 * @vdev_id: ID of the vdev
 * @skb: skb pointer
 * @dir: direction
 * @pdev_id: ID of the pdev
 *
 * Return: true/false
 */
static bool qdf_log_icmp_pkt(uint8_t vdev_id, struct sk_buff *skb,
			     enum qdf_proto_dir dir, uint8_t pdev_id)
{
	uint8_t *data = NULL;
	uint16_t seq_num = 0;
	uint16_t icmp_id = 0;
	struct qdf_dp_trace_proto_cmn cmn_info;

	if ((qdf_dp_get_proto_bitmap() & QDF_NBUF_PKT_TRAC_TYPE_ICMP) &&
	    (qdf_nbuf_is_icmp_pkt(skb) == true)) {

		QDF_NBUF_CB_DP_TRACE_PRINT(skb) = false;
		cmn_info.subtype = qdf_nbuf_get_icmp_subtype(skb);

		data = qdf_nbuf_data(skb);
		icmp_id = qdf_cpu_to_be16(*(uint16_t *)(data + ICMP_ID_OFFSET));
		seq_num = qdf_cpu_to_be16(*(uint16_t *)(data + ICMP_SEQ_NUM_OFFSET));

		cmn_info.proto_priv_data = ((uint32_t)icmp_id) << 16;
		cmn_info.proto_priv_data |= (uint32_t)seq_num;
		cmn_info.type = QDF_PROTO_TYPE_ICMP;
		cmn_info.vdev_id = vdev_id;
		cmn_info.mpdu_seq = qdf_nbuf_get_mpdu_seq_num(skb);

		if (QDF_TX == dir)
			QDF_NBUF_CB_TX_DP_TRACE(skb) = 1;
		else if (QDF_RX == dir)
			QDF_NBUF_CB_RX_DP_TRACE(skb) = 1;

		DPTRACE(qdf_dp_trace_proto_pkt(QDF_DP_TRACE_ICMP_PACKET_RECORD,
					       skb->data +
					       QDF_NBUF_SRC_MAC_OFFSET,
					       skb->data +
					       QDF_NBUF_DEST_MAC_OFFSET,
					       dir, pdev_id,
					       false, &cmn_info));

		if (cmn_info.subtype == QDF_PROTO_ICMP_REQ)
			g_qdf_dp_trace_data.icmp_req++;
		else
			g_qdf_dp_trace_data.icmp_resp++;

		return true;
	}
	return false;
}

#ifdef WLAN_CHIPSET_STATS
static void
qdf_log_pkt_cstats(uint8_t *sa, uint8_t *da, enum qdf_proto_type pkt_type,
		   enum qdf_proto_subtype subtype, enum qdf_proto_dir dir,
		   enum qdf_dp_tx_rx_status status, uint8_t vdev_id,
		   enum QDF_OPMODE op_mode)
{
	wlan_cp_stats_cstats_pkt_log(sa, da, pkt_type, subtype, dir,
				     status, vdev_id, op_mode);
}
#else
static void
qdf_log_pkt_cstats(uint8_t *sa, uint8_t *da, enum qdf_proto_type pkt_type,
		   enum qdf_proto_subtype subtype, enum qdf_proto_dir dir,
		   enum qdf_dp_tx_rx_status status, uint8_t vdev_id,
		   enum QDF_OPMODE op_mode)
{
}
#endif

#ifdef CONNECTIVITY_DIAG_EVENT
enum diag_tx_status wlan_get_diag_tx_status(enum qdf_dp_tx_rx_status tx_status)
{
	switch (tx_status) {
	case DIAG_TX_RX_STATUS_FW_DISCARD:
	case DIAG_TX_RX_STATUS_INVALID:
	case DIAG_TX_RX_STATUS_DROP:
	case DIAG_TX_RX_STATUS_DOWNLOAD_SUCC:
	case DIAG_TX_RX_STATUS_DEFAULT:
	default:
		return DIAG_TX_STATUS_FAIL;
	case DIAG_TX_RX_STATUS_NO_ACK:
		return DIAG_TX_STATUS_NO_ACK;
	case DIAG_TX_RX_STATUS_OK:
		return DIAG_TX_STATUS_ACK;
	}

	return DIAG_TX_STATUS_FAIL;
}

/**
 * qdf_subtype_to_wlan_main_tag() - Convert qdf subtype to wlan main tag
 * @subtype: EAPoL key subtype
 *
 * Return: Wlan main tag subtype
 */
static int qdf_subtype_to_wlan_main_tag(enum qdf_proto_subtype subtype)
{
	switch (subtype) {
	case QDF_PROTO_DHCP_DISCOVER:
		return WLAN_CONN_DIAG_DHCP_DISC_EVENT;
	case QDF_PROTO_DHCP_REQUEST:
		return WLAN_CONN_DIAG_DHCP_REQUEST_EVENT;
	case QDF_PROTO_DHCP_OFFER:
		return WLAN_CONN_DIAG_DHCP_OFFER_EVENT;
	case QDF_PROTO_DHCP_ACK:
		return WLAN_CONN_DIAG_DHCP_ACK_EVENT;
	case QDF_PROTO_DHCP_NACK:
		return WLAN_CONN_DIAG_DHCP_NACK_EVENT;
	case QDF_PROTO_EAPOL_M1:
		return WLAN_CONN_DIAG_EAPOL_M1_EVENT;
	case QDF_PROTO_EAPOL_M2:
		return WLAN_CONN_DIAG_EAPOL_M2_EVENT;
	case QDF_PROTO_EAPOL_M3:
		return WLAN_CONN_DIAG_EAPOL_M3_EVENT;
	case QDF_PROTO_EAPOL_M4:
		return WLAN_CONN_DIAG_EAPOL_M4_EVENT;
	case QDF_PROTO_EAP_REQUEST:
		return WLAN_CONN_DIAG_EAP_REQ_EVENT;
	case QDF_PROTO_EAP_RESPONSE:
		return WLAN_CONN_DIAG_EAP_RESP_EVENT;
	case QDF_PROTO_EAP_SUCCESS:
		return WLAN_CONN_DIAG_EAP_SUCC_EVENT;
	case QDF_PROTO_EAP_FAILURE:
		return WLAN_CONN_DIAG_EAP_FAIL_EVENT;
	case QDF_PROTO_EAPOL_START:
		return WLAN_CONN_DIAG_EAP_START_EVENT;
	default:
		return WLAN_CONN_DIAG_MAX;
	}
}

/**
 * qdf_get_wlan_eap_code() - Get EAP code
 * @data: skb data pointer
 *
 * Return: EAP code value
 */
static int qdf_get_wlan_eap_code(uint8_t *data)
{
	uint8_t code = *(data + EAP_CODE_OFFSET);

	switch (code) {
	case QDF_EAP_REQUEST:
		return WLAN_CONN_DIAG_EAP_REQ_EVENT;
	case QDF_EAP_RESPONSE:
		return WLAN_CONN_DIAG_EAP_RESP_EVENT;
	case QDF_EAP_SUCCESS:
		return WLAN_CONN_DIAG_EAP_SUCC_EVENT;
	case QDF_EAP_FAILURE:
		return WLAN_CONN_DIAG_EAP_FAIL_EVENT;
	default:
		return WLAN_CONN_DIAG_MAX;
	}
}

/**
 * qdf_eapol_get_key_type() - Get EAPOL key type
 * @data: skb data pointer
 * @subtype: EAPoL key subtype
 *
 * Return: EAPOL key type
 */
static
uint8_t qdf_eapol_get_key_type(uint8_t *data, enum qdf_proto_subtype subtype)
{
	uint16_t key_info = *(uint16_t *)(data + EAPOL_KEY_INFO_OFFSET);

	/* If key type is PTK, key type will be set in EAPOL Key info */
	if (key_info & EAPOL_KEY_TYPE_MASK)
		return qdf_subtype_to_wlan_main_tag(subtype);
	else if (key_info & EAPOL_KEY_ENCRYPTED_MASK)
		return WLAN_CONN_DIAG_GTK_M1_EVENT;
	else
		return WLAN_CONN_DIAG_GTK_M2_EVENT;
}

/**
 * qdf_skip_wlan_connectivity_log() - Check if connectivity log need to skip
 * @type: Protocol type
 * @subtype: Protocol subtype
 * @dir: Rx or Tx
 * @op_mode: Vdev Operation mode
 *
 * Return: true or false
 */
static inline
bool qdf_skip_wlan_connectivity_log(enum qdf_proto_type type,
				    enum qdf_proto_subtype subtype,
				    enum qdf_proto_dir dir,
				    enum QDF_OPMODE op_mode)
{
	if (op_mode != QDF_STA_MODE)
		return true;

	if (dir == QDF_RX && type == QDF_PROTO_TYPE_DHCP &&
	    (subtype == QDF_PROTO_DHCP_DISCOVER ||
	     subtype == QDF_PROTO_DHCP_REQUEST))
		return true;
	return false;
}

/**
 * qdf_fill_wlan_connectivity_log() - Fill and queue protocol packet to logging
 * the logging queue
 * @type: Protocol type
 * @subtype: Protocol subtype
 * @dir: Rx or Tx
 * @qdf_tx_status: Tx completion status
 * @op_mode: Vdev Operation mode
 * @vdev_id: DP vdev ID
 * @data: skb data pointer
 * @band: band
 *
 * Return: None
 */
static
void qdf_fill_wlan_connectivity_log(enum qdf_proto_type type,
				    enum qdf_proto_subtype subtype,
				    enum qdf_proto_dir dir,
				    enum qdf_dp_tx_rx_status qdf_tx_status,
				    enum QDF_OPMODE op_mode,
				    uint8_t vdev_id, uint8_t *data,
				    uint8_t band)
{
	uint8_t pkt_type;

	WLAN_HOST_DIAG_EVENT_DEF(wlan_diag_event, struct wlan_diag_packet_info);

	if (qdf_skip_wlan_connectivity_log(type, subtype, dir, op_mode))
		return;

	qdf_mem_zero(&wlan_diag_event, sizeof(wlan_diag_event));

	wlan_diag_event.diag_cmn.timestamp_us =
					qdf_get_time_of_the_day_ms() * 1000;
	wlan_diag_event.diag_cmn.ktime_us = qdf_ktime_to_us(qdf_ktime_get());
	wlan_diag_event.diag_cmn.vdev_id = vdev_id;

	wlan_diag_event.version = DIAG_MGMT_VERSION_V2;

	if (type == QDF_PROTO_TYPE_DHCP) {
		wlan_diag_event.subtype =
					qdf_subtype_to_wlan_main_tag(subtype);
	} else if (type == QDF_PROTO_TYPE_EAPOL) {
		pkt_type = *(data + EAPOL_PACKET_TYPE_OFFSET);
		if (pkt_type == EAPOL_PACKET_TYPE_EAP) {
			wlan_diag_event.subtype =
						qdf_get_wlan_eap_code(data);
			wlan_diag_event.eap_type =
						*(data + EAP_TYPE_OFFSET);
			wlan_diag_event.eap_len =
			   qdf_ntohs(*(uint16_t *)(data + EAP_LENGTH_OFFSET));
		} else if (pkt_type == EAPOL_PACKET_TYPE_KEY) {
			wlan_diag_event.subtype =
					qdf_eapol_get_key_type(data, subtype);
		} else if (pkt_type == EAPOL_PACKET_TYPE_START) {
			wlan_diag_event.subtype =
					WLAN_CONN_DIAG_EAP_START_EVENT;
			wlan_diag_event.eap_len =
			    qdf_ntohs(*(uint16_t *)(data + EAPOL_PKT_LEN_OFFSET));
		} else {
			return;
		}
	} else {
		return;
	}

	wlan_diag_event.supported_links = band;

	/*Tx completion status needs to be logged*/
	if (dir == QDF_TX) {
		wlan_diag_event.is_tx = 1;
		wlan_diag_event.tx_status =
					wlan_get_diag_tx_status(qdf_tx_status);
	}

	WLAN_HOST_DIAG_EVENT_REPORT(&wlan_diag_event, EVENT_WLAN_CONN_DP);
}

#else
static inline
void qdf_fill_wlan_connectivity_log(enum qdf_proto_type type,
				    enum qdf_proto_subtype subtype,
				    enum qdf_proto_dir dir,
				    enum qdf_dp_tx_rx_status qdf_tx_status,
				    enum QDF_OPMODE op_mode,
				    uint8_t vdev_id, uint8_t *data,
				    uint8_t band)
{
}
#endif

/**
 * qdf_log_eapol_pkt() - log EAPOL packet
 * @vdev_id: ID of the vdev
 * @skb: skb pointer
 * @dir: direction
 * @pdev_id: ID of the pdev
 * @op_mode: Vdev Operation mode
 *
 * Return: true/false
 */
static bool qdf_log_eapol_pkt(uint8_t vdev_id, struct sk_buff *skb,
			      enum qdf_proto_dir dir, uint8_t pdev_id,
				  enum QDF_OPMODE op_mode)
{
	enum qdf_proto_subtype subtype;
	uint32_t dp_eap_trace;
	uint32_t dp_eap_event;
	struct qdf_dp_trace_proto_cmn cmn_info;

	dp_eap_trace = qdf_dp_get_proto_bitmap() & QDF_NBUF_PKT_TRAC_TYPE_EAPOL;
	dp_eap_event = qdf_dp_get_proto_event_bitmap() &
				QDF_NBUF_PKT_TRAC_TYPE_EAPOL;

	if (!dp_eap_trace && !dp_eap_event)
		return false;

	if (!((dir == QDF_TX && QDF_NBUF_CB_PACKET_TYPE_EAPOL ==
	       QDF_NBUF_CB_GET_PACKET_TYPE(skb)) ||
	      (dir == QDF_RX && qdf_nbuf_is_ipv4_eapol_pkt(skb) == true)))
		return false;

	subtype = qdf_nbuf_get_eapol_subtype(skb);

	if (dp_eap_event && dir == QDF_RX) {
		qdf_dp_log_proto_pkt_info(skb->data + QDF_NBUF_SRC_MAC_OFFSET,
					  skb->data + QDF_NBUF_DEST_MAC_OFFSET,
					  QDF_PROTO_TYPE_EAPOL, subtype, dir,
					  QDF_TRACE_DEFAULT_MSDU_ID,
					  QDF_TX_RX_STATUS_INVALID);
		qdf_fill_wlan_connectivity_log(QDF_PROTO_TYPE_EAPOL, subtype,
					       QDF_RX, 0, op_mode,
					       vdev_id, skb->data,
					       qdf_nbuf_rx_get_band(skb));
		qdf_log_pkt_cstats(skb->data + QDF_NBUF_SRC_MAC_OFFSET,
				   skb->data + QDF_NBUF_DEST_MAC_OFFSET,
				   QDF_PROTO_TYPE_EAPOL, subtype, dir,
				   QDF_TX_RX_STATUS_INVALID, vdev_id, op_mode);
	}

	if (dp_eap_trace) {
		QDF_NBUF_CB_DP_TRACE_PRINT(skb) = true;
		if (QDF_TX == dir)
			QDF_NBUF_CB_TX_DP_TRACE(skb) = 1;
		else if (QDF_RX == dir)
			QDF_NBUF_CB_RX_DP_TRACE(skb) = 1;

		cmn_info.vdev_id = vdev_id;
		cmn_info.type = QDF_PROTO_TYPE_EAPOL;
		cmn_info.subtype = subtype;
		cmn_info.proto_priv_data = 0;
		cmn_info.mpdu_seq = qdf_nbuf_get_mpdu_seq_num(skb);
		DPTRACE(qdf_dp_trace_proto_pkt(QDF_DP_TRACE_EAPOL_PACKET_RECORD,
					       skb->data +
					       QDF_NBUF_SRC_MAC_OFFSET,
					       skb->data +
					       QDF_NBUF_DEST_MAC_OFFSET,
					       dir, pdev_id, true, &cmn_info));

		switch (subtype) {
		case QDF_PROTO_EAPOL_M1:
			g_qdf_dp_trace_data.eapol_m1++;
			break;
		case QDF_PROTO_EAPOL_M2:
			g_qdf_dp_trace_data.eapol_m2++;
			break;
		case QDF_PROTO_EAPOL_M3:
			g_qdf_dp_trace_data.eapol_m3++;
			break;
		case QDF_PROTO_EAPOL_M4:
			g_qdf_dp_trace_data.eapol_m4++;
			break;
		default:
			g_qdf_dp_trace_data.eapol_others++;
			break;
		}
	}

	return true;
}

/**
 * qdf_log_dhcp_pkt() - log DHCP packet
 * @vdev_id: ID of the vdev
 * @skb: skb pointer
 * @dir: direction
 * @pdev_id: ID of the pdev
 * @op_mode: Vdev Operation mode
 *
 * Return: true/false
 */
static bool qdf_log_dhcp_pkt(uint8_t vdev_id, struct sk_buff *skb,
			     enum qdf_proto_dir dir, uint8_t pdev_id,
			     enum QDF_OPMODE op_mode)
{
	enum qdf_proto_subtype subtype = QDF_PROTO_INVALID;
	uint32_t dp_dhcp_trace;
	uint32_t dp_dhcp_event;
	struct qdf_dp_trace_proto_cmn cmn_info;

	dp_dhcp_trace = qdf_dp_get_proto_bitmap() & QDF_NBUF_PKT_TRAC_TYPE_DHCP;
	dp_dhcp_event = qdf_dp_get_proto_event_bitmap() &
				QDF_NBUF_PKT_TRAC_TYPE_DHCP;

	if (!dp_dhcp_trace && !dp_dhcp_event)
		return false;

	if (!((dir == QDF_TX && QDF_NBUF_CB_PACKET_TYPE_DHCP ==
	       QDF_NBUF_CB_GET_PACKET_TYPE(skb)) ||
	      (dir == QDF_RX && qdf_nbuf_is_ipv4_dhcp_pkt(skb) == true)))
		return false;

	subtype = qdf_nbuf_get_dhcp_subtype(skb);

	if (dp_dhcp_event && dir == QDF_RX) {
		qdf_dp_log_proto_pkt_info(skb->data + QDF_NBUF_SRC_MAC_OFFSET,
					  skb->data + QDF_NBUF_DEST_MAC_OFFSET,
					  QDF_PROTO_TYPE_DHCP, subtype, dir,
					  QDF_TRACE_DEFAULT_MSDU_ID,
					  QDF_TX_RX_STATUS_INVALID);
		qdf_fill_wlan_connectivity_log(QDF_PROTO_TYPE_DHCP, subtype,
					       QDF_RX, 0, op_mode, vdev_id, 0,
					       qdf_nbuf_rx_get_band(skb));
		qdf_log_pkt_cstats(skb->data + QDF_NBUF_SRC_MAC_OFFSET,
				   skb->data + QDF_NBUF_DEST_MAC_OFFSET,
				   QDF_PROTO_TYPE_DHCP, subtype, dir,
				   QDF_TX_RX_STATUS_INVALID, vdev_id, op_mode);
	}

	if (dp_dhcp_trace) {
		QDF_NBUF_CB_DP_TRACE_PRINT(skb) = true;
		if (QDF_TX == dir)
			QDF_NBUF_CB_TX_DP_TRACE(skb) = 1;
		else if (QDF_RX == dir)
			QDF_NBUF_CB_RX_DP_TRACE(skb) = 1;

		cmn_info.vdev_id = vdev_id;
		cmn_info.type = QDF_PROTO_TYPE_DHCP;
		cmn_info.subtype = subtype;
		cmn_info.proto_priv_data = 0;
		cmn_info.mpdu_seq = qdf_nbuf_get_mpdu_seq_num(skb);
		DPTRACE(qdf_dp_trace_proto_pkt(QDF_DP_TRACE_DHCP_PACKET_RECORD,
					       skb->data +
					       QDF_NBUF_SRC_MAC_OFFSET,
					       skb->data +
					       QDF_NBUF_DEST_MAC_OFFSET,
					       dir, pdev_id, true, &cmn_info));

		switch (subtype) {
		case QDF_PROTO_DHCP_DISCOVER:
			g_qdf_dp_trace_data.dhcp_disc++;
			break;
		case QDF_PROTO_DHCP_OFFER:
			g_qdf_dp_trace_data.dhcp_off++;
			break;
		case QDF_PROTO_DHCP_REQUEST:
			g_qdf_dp_trace_data.dhcp_req++;
			break;
		case QDF_PROTO_DHCP_ACK:
			g_qdf_dp_trace_data.dhcp_ack++;
			break;
		case QDF_PROTO_DHCP_NACK:
			g_qdf_dp_trace_data.dhcp_nack++;
			break;
		default:
			g_qdf_dp_trace_data.eapol_others++;
			break;
		}
	}

	return true;
}

/**
 * qdf_log_arp_pkt() - log ARP packet
 * @vdev_id: ID of the vdev
 * @skb: skb pointer
 * @dir: direction
 * @pdev_id: ID of the pdev
 *
 * Return: true/false
 */
static bool qdf_log_arp_pkt(uint8_t vdev_id, struct sk_buff *skb,
			    enum qdf_proto_dir dir, uint8_t pdev_id)
{
	enum qdf_proto_subtype proto_subtype;
	struct qdf_dp_trace_proto_cmn cmn_info;

	if ((qdf_dp_get_proto_bitmap() & QDF_NBUF_PKT_TRAC_TYPE_ARP) &&
		((dir == QDF_TX && QDF_NBUF_CB_PACKET_TYPE_ARP ==
			QDF_NBUF_CB_GET_PACKET_TYPE(skb)) ||
		 (dir == QDF_RX && qdf_nbuf_is_ipv4_arp_pkt(skb) == true))) {

		proto_subtype = qdf_nbuf_get_arp_subtype(skb);
		QDF_NBUF_CB_DP_TRACE_PRINT(skb) = true;
		if (QDF_TX == dir)
			QDF_NBUF_CB_TX_DP_TRACE(skb) = 1;
		else if (QDF_RX == dir)
			QDF_NBUF_CB_RX_DP_TRACE(skb) = 1;

		cmn_info.vdev_id = vdev_id;
		cmn_info.type = QDF_PROTO_TYPE_ARP;
		cmn_info.subtype = proto_subtype;
		cmn_info.proto_priv_data = 0;
		cmn_info.mpdu_seq = qdf_nbuf_get_mpdu_seq_num(skb);
		DPTRACE(qdf_dp_trace_proto_pkt(QDF_DP_TRACE_ARP_PACKET_RECORD,
					       skb->data +
					       QDF_NBUF_SRC_MAC_OFFSET,
					       skb->data +
					       QDF_NBUF_DEST_MAC_OFFSET,
					       dir, pdev_id, true,
					       &cmn_info));

		if (QDF_PROTO_ARP_REQ == proto_subtype)
			g_qdf_dp_trace_data.arp_req++;
		else
			g_qdf_dp_trace_data.arp_resp++;

		return true;
	}
	return false;
}


bool qdf_dp_trace_log_pkt(uint8_t vdev_id, struct sk_buff *skb,
			  enum qdf_proto_dir dir, uint8_t pdev_id,
			  enum QDF_OPMODE op_mode)
{
	if (!qdf_dp_get_proto_bitmap() && !qdf_dp_get_proto_event_bitmap())
		return false;
	if (qdf_log_arp_pkt(vdev_id, skb, dir, pdev_id))
		return true;
	if (qdf_log_dhcp_pkt(vdev_id, skb, dir, pdev_id, op_mode))
		return true;
	if (qdf_log_eapol_pkt(vdev_id, skb, dir, pdev_id, op_mode))
		return true;
	if (qdf_log_icmp_pkt(vdev_id, skb, dir, pdev_id))
		return true;
	if (qdf_log_icmpv6_pkt(vdev_id, skb, dir, pdev_id))
		return true;
	return false;
}
qdf_export_symbol(qdf_dp_trace_log_pkt);

void qdf_dp_display_mgmt_pkt(struct qdf_dp_trace_record_s *record,
			      uint16_t index, uint8_t pdev_id, uint8_t info)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_mgmt_buf *buf =
		(struct qdf_dp_trace_mgmt_buf *)record->data;

	qdf_mem_zero(prepend_str, sizeof(prepend_str));
	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, info, record);

	DPTRACE_PRINT("%s [%d] [%s %s]",
		      prepend_str,
		      buf->vdev_id,
		      qdf_dp_type_to_str(buf->type),
		      qdf_dp_subtype_to_str(buf->subtype));
}
qdf_export_symbol(qdf_dp_display_mgmt_pkt);


void qdf_dp_trace_mgmt_pkt(enum QDF_DP_TRACE_ID code, uint8_t vdev_id,
		uint8_t pdev_id, enum qdf_proto_type type,
		enum qdf_proto_subtype subtype)
{
	struct qdf_dp_trace_mgmt_buf buf;
	int buf_size = sizeof(struct qdf_dp_trace_mgmt_buf);

	if (qdf_dp_enable_check(NULL, code, QDF_NA) == false)
		return;

	if (buf_size > QDF_DP_TRACE_RECORD_SIZE)
		QDF_BUG(0);

	buf.type = type;
	buf.subtype = subtype;
	buf.vdev_id = vdev_id;
	qdf_dp_add_record(code, pdev_id, (uint8_t *)&buf, buf_size,
			  NULL, 0, true);
}
qdf_export_symbol(qdf_dp_trace_mgmt_pkt);

static void
qdf_dpt_display_credit_record_debugfs(qdf_debugfs_file_t file,
				      struct qdf_dp_trace_record_s *record,
				      uint32_t index)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_credit_record *buf =
		(struct qdf_dp_trace_credit_record *)record->data;

	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, 0, record);
	if (buf->operation == QDF_OP_NA)
		qdf_debugfs_printf(file, "%s [%s] [T: %d G0: %d G1: %d]\n",
				   prepend_str,
				   qdf_dp_credit_source_to_str(buf->source),
				   buf->total_credits, buf->g0_credit,
				   buf->g1_credit);
	else
		qdf_debugfs_printf(file,
				   "%s [%s] [T: %d G0: %d G1: %d] [%s %d]\n",
				   prepend_str,
				   qdf_dp_credit_source_to_str(buf->source),
				   buf->total_credits, buf->g0_credit,
				   buf->g1_credit,
				   qdf_dp_operation_to_str(buf->operation),
				   buf->delta);
}

void qdf_dp_display_credit_record(struct qdf_dp_trace_record_s *record,
				  uint16_t index, uint8_t pdev_id, uint8_t info)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_credit_record *buf =
		(struct qdf_dp_trace_credit_record *)record->data;

	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, info, record);
	if (buf->operation == QDF_OP_NA)
		DPTRACE_PRINT("%s [%s] [T: %d G0: %d G1: %d]",
			      prepend_str,
			      qdf_dp_credit_source_to_str(buf->source),
			      buf->total_credits, buf->g0_credit,
			      buf->g1_credit);
	else
		DPTRACE_PRINT("%s [%s] [T: %d G0: %d G1: %d] [%s %d]",
			      prepend_str,
			      qdf_dp_credit_source_to_str(buf->source),
			      buf->total_credits, buf->g0_credit,
			      buf->g1_credit,
			      qdf_dp_operation_to_str(buf->operation),
			      buf->delta);
}

void qdf_dp_trace_credit_record(enum QDF_CREDIT_UPDATE_SOURCE source,
				enum QDF_CREDIT_OPERATION operation,
				int delta, int total_credits,
				int g0_credit, int g1_credit)
{
	struct qdf_dp_trace_credit_record buf;
	int buf_size = sizeof(struct qdf_dp_trace_credit_record);
	enum QDF_DP_TRACE_ID code = QDF_DP_TRACE_TX_CREDIT_RECORD;

	if (qdf_dp_enable_check(NULL, code, QDF_NA) == false)
		return;

	if (!(qdf_dp_get_proto_bitmap() & QDF_HL_CREDIT_TRACKING))
		return;

	if (buf_size > QDF_DP_TRACE_RECORD_SIZE)
		QDF_BUG(0);

	buf.source = source;
	buf.operation = operation;
	buf.delta = delta;
	buf.total_credits = total_credits;
	buf.g0_credit = g0_credit;
	buf.g1_credit = g1_credit;

	qdf_dp_add_record(code, QDF_TRACE_DEFAULT_PDEV_ID, (uint8_t *)&buf,
			  buf_size, NULL, 0, false);
}
qdf_export_symbol(qdf_dp_trace_credit_record);

void qdf_dp_display_event_record(struct qdf_dp_trace_record_s *record,
			      uint16_t index, uint8_t pdev_id, uint8_t info)
{
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_event_buf *buf =
		(struct qdf_dp_trace_event_buf *)record->data;

	qdf_mem_zero(prepend_str, sizeof(prepend_str));
	qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
				   index, info, record);

	DPTRACE_PRINT("%s [%d] [%s %s]",
		      prepend_str,
		      buf->vdev_id,
		      qdf_dp_type_to_str(buf->type),
		      qdf_dp_subtype_to_str(buf->subtype));
}
qdf_export_symbol(qdf_dp_display_event_record);

void qdf_dp_trace_record_event(enum QDF_DP_TRACE_ID code, uint8_t vdev_id,
			       uint8_t pdev_id, enum qdf_proto_type type,
			       enum qdf_proto_subtype subtype)
{
	struct qdf_dp_trace_event_buf buf;
	int buf_size = sizeof(struct qdf_dp_trace_event_buf);

	if (qdf_dp_enable_check(NULL, code, QDF_NA) == false)
		return;

	if (buf_size > QDF_DP_TRACE_RECORD_SIZE)
		QDF_BUG(0);

	buf.type = type;
	buf.subtype = subtype;
	buf.vdev_id = vdev_id;
	qdf_dp_add_record(code, pdev_id,
			  (uint8_t *)&buf, buf_size, NULL, 0, true);
}
qdf_export_symbol(qdf_dp_trace_record_event);


void qdf_dp_display_proto_pkt(struct qdf_dp_trace_record_s *record,
			      uint16_t index, uint8_t pdev_id, uint8_t info)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_proto_buf *buf =
		(struct qdf_dp_trace_proto_buf *)record->data;

	qdf_mem_zero(prepend_str, sizeof(prepend_str));
	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, info, record);

	if (QDF_RX == buf->dir)
		DPTRACE_PRINT("%s [%d] [%d] [%s] SA: "
			      QDF_MAC_ADDR_FMT " %s DA:"
			      QDF_MAC_ADDR_FMT " proto priv data = %08x",
			      prepend_str,
			      buf->cmn_info.vdev_id,
			      buf->cmn_info.mpdu_seq,
			      qdf_dp_subtype_to_str(buf->cmn_info.subtype),
			      QDF_MAC_ADDR_REF(buf->sa.bytes),
			      qdf_dp_dir_to_str(buf->dir),
			      QDF_MAC_ADDR_REF(buf->da.bytes),
			      buf->cmn_info.proto_priv_data);
	else
		DPTRACE_PRINT("%s [%d] [%s] SA: "
			      QDF_MAC_ADDR_FMT " %s DA:"
			      QDF_MAC_ADDR_FMT " proto priv data = %08x",
			      prepend_str,
			      buf->cmn_info.vdev_id,
			      qdf_dp_subtype_to_str(buf->cmn_info.subtype),
			      QDF_MAC_ADDR_REF(buf->sa.bytes),
			      qdf_dp_dir_to_str(buf->dir),
			      QDF_MAC_ADDR_REF(buf->da.bytes),
			      buf->cmn_info.proto_priv_data);
}
qdf_export_symbol(qdf_dp_display_proto_pkt);

void qdf_dp_trace_proto_pkt(enum QDF_DP_TRACE_ID code,
			    uint8_t *sa, uint8_t *da,
			    enum qdf_proto_dir dir,
			    uint8_t pdev_id, bool print,
			    struct qdf_dp_trace_proto_cmn *cmn_info)
{
	struct qdf_dp_trace_proto_buf buf;
	int buf_size = sizeof(struct qdf_dp_trace_proto_buf);

	if (qdf_dp_enable_check(NULL, code, dir) == false)
		return;

	if (buf_size > QDF_DP_TRACE_RECORD_SIZE)
		QDF_BUG(0);

	memcpy(&buf.sa, sa, QDF_NET_ETH_LEN);
	memcpy(&buf.da, da, QDF_NET_ETH_LEN);
	memcpy(&buf.cmn_info, cmn_info, sizeof(*cmn_info));
	buf.dir = dir;
	qdf_dp_add_record(code, pdev_id,
			  (uint8_t *)&buf, buf_size, NULL, 0, print);
}
qdf_export_symbol(qdf_dp_trace_proto_pkt);

void qdf_dp_display_ptr_record(struct qdf_dp_trace_record_s *record,
				uint16_t index, uint8_t pdev_id, uint8_t info)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_ptr_buf *buf =
		(struct qdf_dp_trace_ptr_buf *)record->data;
	bool is_free_pkt_ptr_record = false;

	if ((record->code == QDF_DP_TRACE_FREE_PACKET_PTR_RECORD) ||
	    (record->code == QDF_DP_TRACE_LI_DP_FREE_PACKET_PTR_RECORD))
		is_free_pkt_ptr_record = true;

	qdf_mem_zero(prepend_str, sizeof(prepend_str));
	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, info, record);

	if (loc < sizeof(prepend_str))
		scnprintf(&prepend_str[loc], sizeof(prepend_str) - loc,
			  "[msdu id %d %s %d]",
			  buf->msdu_id,
			  is_free_pkt_ptr_record ? "status" : "vdev_id",
			  buf->status);

	if (info & QDF_DP_TRACE_RECORD_INFO_LIVE) {
		/* In live mode donot dump the contents of the cookie */
		DPTRACE_PRINT("%s", prepend_str);
	} else {
		dump_dp_hex_trace(prepend_str, (uint8_t *)&buf->cookie,
			sizeof(buf->cookie));
	}
}
qdf_export_symbol(qdf_dp_display_ptr_record);

static
enum qdf_proto_type qdf_dp_get_pkt_proto_type(qdf_nbuf_t nbuf)
{
	uint8_t pkt_type;

	if (!nbuf)
		return QDF_PROTO_TYPE_MAX;

	if (qdf_nbuf_data_is_dns_query(nbuf) ||
	    qdf_nbuf_data_is_dns_response(nbuf))
		return QDF_PROTO_TYPE_DNS;

	pkt_type = QDF_NBUF_CB_GET_PACKET_TYPE(nbuf);

	switch (pkt_type) {
	case QDF_NBUF_CB_PACKET_TYPE_EAPOL:
		return QDF_PROTO_TYPE_EAPOL;
	case QDF_NBUF_CB_PACKET_TYPE_ARP:
		return QDF_PROTO_TYPE_ARP;
	case QDF_NBUF_CB_PACKET_TYPE_DHCP:
		return QDF_PROTO_TYPE_DHCP;
	default:
		return QDF_PROTO_TYPE_MAX;
	}
}

static
enum qdf_proto_subtype qdf_dp_get_pkt_subtype(qdf_nbuf_t nbuf,
					      enum qdf_proto_type pkt_type)
{
	switch (pkt_type) {
	case QDF_PROTO_TYPE_EAPOL:
		return qdf_nbuf_get_eapol_subtype(nbuf);
	case QDF_PROTO_TYPE_ARP:
		return qdf_nbuf_get_arp_subtype(nbuf);
	case QDF_PROTO_TYPE_DHCP:
		return qdf_nbuf_get_dhcp_subtype(nbuf);
	case QDF_PROTO_TYPE_DNS:
		return (qdf_nbuf_data_is_dns_query(nbuf)) ?
				QDF_PROTO_DNS_QUERY : QDF_PROTO_DNS_RES;
	default:
		return QDF_PROTO_INVALID;
	}
}

static
bool qdf_dp_proto_log_enable_check(enum qdf_proto_type pkt_type,
				   uint16_t status)
{
	if (pkt_type == QDF_PROTO_TYPE_MAX)
		return false;

	switch (pkt_type) {
	case QDF_PROTO_TYPE_EAPOL:
		return qdf_dp_get_proto_event_bitmap() &
				QDF_NBUF_PKT_TRAC_TYPE_EAPOL;
	case QDF_PROTO_TYPE_DHCP:
		return qdf_dp_get_proto_event_bitmap() &
				QDF_NBUF_PKT_TRAC_TYPE_DHCP;
	case QDF_PROTO_TYPE_ARP:
		if (status == QDF_TX_RX_STATUS_OK)
			return false;
		else
			return qdf_dp_get_proto_event_bitmap() &
					QDF_NBUF_PKT_TRAC_TYPE_ARP;
	case QDF_PROTO_TYPE_DNS:
		if (status == QDF_TX_RX_STATUS_OK)
			return false;
		else
			return qdf_dp_get_proto_event_bitmap() &
					QDF_NBUF_PKT_TRAC_TYPE_DNS;
	default:
		return false;
	}
}

void qdf_dp_track_noack_check(qdf_nbuf_t nbuf, enum qdf_proto_subtype *subtype)
{
	enum qdf_proto_type pkt_type = qdf_dp_get_pkt_proto_type(nbuf);
	uint16_t dp_track = 0;

	switch (pkt_type) {
	case QDF_PROTO_TYPE_EAPOL:
		dp_track = qdf_dp_get_proto_bitmap() &
				QDF_NBUF_PKT_TRAC_TYPE_EAPOL;
		break;
	case QDF_PROTO_TYPE_DHCP:
		dp_track = qdf_dp_get_proto_bitmap() &
				QDF_NBUF_PKT_TRAC_TYPE_DHCP;
		break;
	case QDF_PROTO_TYPE_ARP:
		dp_track = qdf_dp_get_proto_bitmap() &
					QDF_NBUF_PKT_TRAC_TYPE_ARP;
		break;
	case QDF_PROTO_TYPE_DNS:
		dp_track = qdf_dp_get_proto_bitmap() &
					QDF_NBUF_PKT_TRAC_TYPE_DNS;
		break;
	default:
		break;
	}

	if (!dp_track) {
		*subtype = QDF_PROTO_INVALID;
		return;
	}

	*subtype = qdf_dp_get_pkt_subtype(nbuf, pkt_type);
}
qdf_export_symbol(qdf_dp_track_noack_check);

enum qdf_dp_tx_rx_status qdf_dp_get_status_from_a_status(uint8_t status)
{
	if (status == QDF_A_STATUS_ERROR)
		return QDF_TX_RX_STATUS_INVALID;
	else if (status == QDF_A_STATUS_OK)
		return QDF_TX_RX_STATUS_OK;
	else
		return QDF_TX_RX_STATUS_MAX;
}
qdf_export_symbol(qdf_dp_get_status_from_a_status);

void qdf_dp_trace_ptr(qdf_nbuf_t nbuf, enum QDF_DP_TRACE_ID code,
		uint8_t pdev_id, uint8_t *data, uint8_t size,
		uint16_t msdu_id, uint16_t buf_arg_status,
		enum qdf_dp_tx_rx_status qdf_tx_status,
		enum QDF_OPMODE op_mode)
{
	struct qdf_dp_trace_ptr_buf buf;
	int buf_size = sizeof(struct qdf_dp_trace_ptr_buf);
	enum qdf_proto_type pkt_type;
	enum qdf_proto_subtype subtype;

	pkt_type = qdf_dp_get_pkt_proto_type(nbuf);
	if ((code == QDF_DP_TRACE_FREE_PACKET_PTR_RECORD ||
	     code == QDF_DP_TRACE_LI_DP_FREE_PACKET_PTR_RECORD) &&
	    qdf_dp_proto_log_enable_check(pkt_type, qdf_tx_status)) {
		subtype = qdf_dp_get_pkt_subtype(nbuf, pkt_type);
		qdf_dp_log_proto_pkt_info(nbuf->data + QDF_NBUF_SRC_MAC_OFFSET,
					 nbuf->data + QDF_NBUF_DEST_MAC_OFFSET,
					 pkt_type, subtype,
					 QDF_TX, msdu_id, qdf_tx_status);
		qdf_fill_wlan_connectivity_log(pkt_type, subtype,
					       QDF_TX, qdf_tx_status, op_mode,
					       QDF_NBUF_CB_TX_VDEV_CTX(nbuf),
					       nbuf->data,
					       qdf_nbuf_tx_get_band(nbuf));
		qdf_log_pkt_cstats(nbuf->data + QDF_NBUF_SRC_MAC_OFFSET,
				   nbuf->data + QDF_NBUF_DEST_MAC_OFFSET,
				   pkt_type, subtype, QDF_TX,
				   qdf_tx_status, QDF_NBUF_CB_TX_VDEV_CTX(nbuf),
				   op_mode);
	}

	if (qdf_dp_enable_check(nbuf, code, QDF_TX) == false)
		return;

	if (buf_size > QDF_DP_TRACE_RECORD_SIZE)
		QDF_BUG(0);

	qdf_mem_copy(&buf.cookie, data, size);
	buf.msdu_id = msdu_id;
	buf.status = buf_arg_status;
	qdf_dp_add_record(code, pdev_id, (uint8_t *)&buf, buf_size, NULL, 0,
			  QDF_NBUF_CB_DP_TRACE_PRINT(nbuf));
}
qdf_export_symbol(qdf_dp_trace_ptr);

void qdf_dp_trace_data_pkt(qdf_nbuf_t nbuf, uint8_t pdev_id,
			   enum QDF_DP_TRACE_ID code, uint16_t msdu_id,
			   enum qdf_proto_dir dir)
{
	struct qdf_dp_trace_data_buf buf;
	enum qdf_proto_type pkt_type;

	pkt_type = qdf_dp_get_pkt_proto_type(nbuf);
	if (code == QDF_DP_TRACE_DROP_PACKET_RECORD &&
	    qdf_dp_proto_log_enable_check(pkt_type, QDF_TX_RX_STATUS_DROP))
		qdf_dp_log_proto_pkt_info(nbuf->data + QDF_NBUF_SRC_MAC_OFFSET,
					 nbuf->data + QDF_NBUF_DEST_MAC_OFFSET,
					 pkt_type,
					 qdf_dp_get_pkt_subtype(nbuf, pkt_type),
					 QDF_TX, msdu_id,
					 QDF_TX_RX_STATUS_DROP);

	buf.msdu_id = msdu_id;
	if (!qdf_dp_enable_check(nbuf, code, dir))
		return;

	qdf_dp_add_record(code, pdev_id,
			  nbuf ? qdf_nbuf_data(nbuf) : NULL,
			  nbuf ? nbuf->len - nbuf->data_len : 0,
			  (uint8_t *)&buf, sizeof(struct qdf_dp_trace_data_buf),
			  (nbuf) ? QDF_NBUF_CB_DP_TRACE_PRINT(nbuf) : false);
}

qdf_export_symbol(qdf_dp_trace_data_pkt);

void qdf_dp_display_record(struct qdf_dp_trace_record_s *record,
			   uint16_t index, uint8_t pdev_id, uint8_t info)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];

	if (!(pdev_id == QDF_TRACE_DEFAULT_PDEV_ID ||
		pdev_id == record->pdev_id))
		return;

	qdf_mem_zero(prepend_str, sizeof(prepend_str));
	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, info, record);

	switch (record->code) {
	case  QDF_DP_TRACE_HDD_TX_TIMEOUT:
		DPTRACE_PRINT(" %s: HDD TX Timeout", prepend_str);
		break;
	case  QDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
		DPTRACE_PRINT(" %s: HDD SoftAP TX Timeout", prepend_str);
		break;
	case  QDF_DP_TRACE_CE_FAST_PACKET_ERR_RECORD:
		DPTRACE_PRINT(" %s: CE Fast Packet Error", prepend_str);
		break;
	case QDF_DP_TRACE_LI_DP_NULL_RX_PACKET_RECORD:
	default:
		dump_dp_hex_trace(prepend_str, record->data, record->size);
		break;
	};
}
qdf_export_symbol(qdf_dp_display_record);

void
qdf_dp_display_data_pkt_record(struct qdf_dp_trace_record_s *record,
			       uint16_t rec_index, uint8_t pdev_id,
			       uint8_t info)
{
	int loc;
	char prepend_str[DP_TRACE_META_DATA_STRLEN + 10];
	struct qdf_dp_trace_data_buf *buf =
		(struct qdf_dp_trace_data_buf *)record->data;

	qdf_mem_zero(prepend_str, sizeof(prepend_str));

	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 rec_index, info, record);
	if (loc < sizeof(prepend_str))
		loc += snprintf(&prepend_str[loc], sizeof(prepend_str) - loc,
				"[%d]", buf->msdu_id);
	dump_dp_hex_trace(prepend_str,
			  &record->data[sizeof(struct qdf_dp_trace_data_buf)],
			  record->size);
}

void qdf_dp_trace(qdf_nbuf_t nbuf, enum QDF_DP_TRACE_ID code, uint8_t pdev_id,
	uint8_t *data, uint8_t size, enum qdf_proto_dir dir)
{

	if (qdf_dp_enable_check(nbuf, code, dir) == false)
		return;

	qdf_dp_add_record(code, pdev_id, nbuf ? qdf_nbuf_data(nbuf) : NULL,
			  size, NULL, 0,
			  (nbuf) ? QDF_NBUF_CB_DP_TRACE_PRINT(nbuf) : false);
}
qdf_export_symbol(qdf_dp_trace);

void qdf_dp_trace_spin_lock_init(void)
{
	spin_lock_init(&l_dp_trace_lock);
}
qdf_export_symbol(qdf_dp_trace_spin_lock_init);

void qdf_dp_trace_disable_live_mode(void)
{
	g_qdf_dp_trace_data.force_live_mode = 0;
}
qdf_export_symbol(qdf_dp_trace_disable_live_mode);

void qdf_dp_trace_enable_live_mode(void)
{
	g_qdf_dp_trace_data.force_live_mode = 1;
}
qdf_export_symbol(qdf_dp_trace_enable_live_mode);

void qdf_dp_trace_clear_buffer(void)
{
	g_qdf_dp_trace_data.head = INVALID_QDF_DP_TRACE_ADDR;
	g_qdf_dp_trace_data.tail = INVALID_QDF_DP_TRACE_ADDR;
	g_qdf_dp_trace_data.num = 0;
	g_qdf_dp_trace_data.dump_counter = 0;
	g_qdf_dp_trace_data.num_records_to_dump = MAX_QDF_DP_TRACE_RECORDS;
	if (g_qdf_dp_trace_data.enable)
		memset(g_qdf_dp_trace_tbl, 0,
		       MAX_QDF_DP_TRACE_RECORDS *
		       sizeof(struct qdf_dp_trace_record_s));
}
qdf_export_symbol(qdf_dp_trace_clear_buffer);

void qdf_dp_trace_dump_stats(void)
{
		DPTRACE_PRINT("STATS |DPT: tx %u rx %u icmp(%u %u) arp(%u %u) icmpv6(%u %u %u %u %u %u) dhcp(%u %u %u %u %u %u) eapol(%u %u %u %u %u)",
			      g_qdf_dp_trace_data.tx_count,
			      g_qdf_dp_trace_data.rx_count,
			      g_qdf_dp_trace_data.icmp_req,
			      g_qdf_dp_trace_data.icmp_resp,
			      g_qdf_dp_trace_data.arp_req,
			      g_qdf_dp_trace_data.arp_resp,
			      g_qdf_dp_trace_data.icmpv6_req,
			      g_qdf_dp_trace_data.icmpv6_resp,
			      g_qdf_dp_trace_data.icmpv6_ns,
			      g_qdf_dp_trace_data.icmpv6_na,
			      g_qdf_dp_trace_data.icmpv6_rs,
			      g_qdf_dp_trace_data.icmpv6_ra,
			      g_qdf_dp_trace_data.dhcp_disc,
			      g_qdf_dp_trace_data.dhcp_off,
			      g_qdf_dp_trace_data.dhcp_req,
			      g_qdf_dp_trace_data.dhcp_ack,
			      g_qdf_dp_trace_data.dhcp_nack,
			      g_qdf_dp_trace_data.dhcp_others,
			      g_qdf_dp_trace_data.eapol_m1,
			      g_qdf_dp_trace_data.eapol_m2,
			      g_qdf_dp_trace_data.eapol_m3,
			      g_qdf_dp_trace_data.eapol_m4,
			      g_qdf_dp_trace_data.eapol_others);
}
qdf_export_symbol(qdf_dp_trace_dump_stats);

/**
 * qdf_dpt_dump_hex_trace_debugfs() - read data in file
 * @file: file to read
 * @str: string to prepend the hexdump with.
 * @buf: buffer which contains data to be written
 * @buf_len: defines the size of the data to be written
 *
 * Return: None
 */
static void qdf_dpt_dump_hex_trace_debugfs(qdf_debugfs_file_t file,
				char *str, uint8_t *buf, uint8_t buf_len)
{
	unsigned char linebuf[BUFFER_SIZE];
	const u8 *ptr = buf;
	int i, linelen, remaining = buf_len;

	/* Dump the bytes in the last line */
	for (i = 0; i < buf_len; i += ROW_SIZE) {
		linelen = min(remaining, ROW_SIZE);
		remaining -= ROW_SIZE;

		hex_dump_to_buffer(ptr + i, linelen, ROW_SIZE, 1,
				linebuf, sizeof(linebuf), false);

		qdf_debugfs_printf(file, "%s %s\n", str, linebuf);
	}
}

/**
 * qdf_dpt_display_proto_pkt_debugfs() - display proto packet
 * @file: file to read
 * @record: dptrace record
 * @index: index
 *
 * Return: none
 */
static void qdf_dpt_display_proto_pkt_debugfs(qdf_debugfs_file_t file,
				struct qdf_dp_trace_record_s *record,
				uint32_t index)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_proto_buf *buf =
		(struct qdf_dp_trace_proto_buf *)record->data;

	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, 0, record);
	qdf_debugfs_printf(file, "%s [%d] [%s] SA: "
			   QDF_MAC_ADDR_FMT " %s DA: "
			   QDF_MAC_ADDR_FMT,
			   prepend_str,
			   buf->cmn_info.vdev_id,
			   qdf_dp_subtype_to_str(buf->cmn_info.subtype),
			   QDF_MAC_ADDR_REF(buf->sa.bytes),
			   qdf_dp_dir_to_str(buf->dir),
			   QDF_MAC_ADDR_REF(buf->da.bytes));
	qdf_debugfs_printf(file, "\n");
}

/**
 * qdf_dpt_display_mgmt_pkt_debugfs() - display mgmt packet
 * @file: file to read
 * @record: dptrace record
 * @index: index
 *
 * Return: none
 */
static void qdf_dpt_display_mgmt_pkt_debugfs(qdf_debugfs_file_t file,
				struct qdf_dp_trace_record_s *record,
				uint32_t index)
{

	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_mgmt_buf *buf =
		(struct qdf_dp_trace_mgmt_buf *)record->data;

	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, 0, record);

	qdf_debugfs_printf(file, "%s [%d] [%s %s]\n",
			   prepend_str,
			   buf->vdev_id,
			   qdf_dp_type_to_str(buf->type),
			   qdf_dp_subtype_to_str(buf->subtype));
}

/**
 * qdf_dpt_display_event_record_debugfs() - display event records
 * @file: file to read
 * @record: dptrace record
 * @index: index
 *
 * Return: none
 */
static void qdf_dpt_display_event_record_debugfs(qdf_debugfs_file_t file,
				struct qdf_dp_trace_record_s *record,
				uint32_t index)
{
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_event_buf *buf =
		(struct qdf_dp_trace_event_buf *)record->data;

	qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
				   index, 0, record);
	qdf_debugfs_printf(file, "%s [%d] [%s %s]\n",
			   prepend_str,
			   buf->vdev_id,
			   qdf_dp_type_to_str(buf->type),
			   qdf_dp_subtype_to_str(buf->subtype));
}

/**
 * qdf_dpt_display_ptr_record_debugfs() - display record ptr
 * @file: file to read
 * @record: dptrace record
 * @index: index
 *
 * Return: none
 */
static void qdf_dpt_display_ptr_record_debugfs(qdf_debugfs_file_t file,
				struct qdf_dp_trace_record_s *record,
				uint32_t index)
{
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	int loc;
	struct qdf_dp_trace_ptr_buf *buf =
		(struct qdf_dp_trace_ptr_buf *)record->data;
	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, 0, record);

	if (loc < sizeof(prepend_str))
		scnprintf(&prepend_str[loc], sizeof(prepend_str) - loc,
			  "[msdu id %d %s %d]",
			  buf->msdu_id,
			  (record->code ==
				QDF_DP_TRACE_FREE_PACKET_PTR_RECORD) ?
			  "status" : "vdev_id",
			  buf->status);

	qdf_dpt_dump_hex_trace_debugfs(file, prepend_str,
				       (uint8_t *)&buf->cookie,
				       sizeof(buf->cookie));
}

/**
 * qdf_dpt_display_record_debugfs() - display record
 * @file: file to read
 * @record: dptrace record
 * @index: index
 *
 * Return: none
 */
static void qdf_dpt_display_record_debugfs(qdf_debugfs_file_t file,
				struct qdf_dp_trace_record_s *record,
				uint32_t index)
{
	int loc;
	char prepend_str[QDF_DP_TRACE_PREPEND_STR_SIZE];
	struct qdf_dp_trace_data_buf *buf =
		(struct qdf_dp_trace_data_buf *)record->data;

	loc = qdf_dp_trace_fill_meta_str(prepend_str, sizeof(prepend_str),
					 index, 0, record);
	if (loc < sizeof(prepend_str))
		loc += snprintf(&prepend_str[loc], sizeof(prepend_str) - loc,
				"[%d]", buf->msdu_id);
	qdf_dpt_dump_hex_trace_debugfs(file, prepend_str,
				       record->data, record->size);
}

uint32_t qdf_dpt_get_curr_pos_debugfs(qdf_debugfs_file_t file,
				      enum qdf_dpt_debugfs_state state)
{
	uint32_t i = 0;
	uint32_t tail;
	uint32_t count = g_qdf_dp_trace_data.num;

	if (!g_qdf_dp_trace_data.enable) {
		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_DEBUG,
		  "%s: Tracing Disabled", __func__);
		return QDF_STATUS_E_EMPTY;
	}

	if (!count) {
		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_DEBUG,
		  "%s: no packets", __func__);
		return QDF_STATUS_E_EMPTY;
	}

	if (state == QDF_DPT_DEBUGFS_STATE_SHOW_IN_PROGRESS)
		return g_qdf_dp_trace_data.curr_pos;

	qdf_debugfs_printf(file,
		"DPT: config - bitmap 0x%x verb %u #rec %u rec_requested %u live_config %u thresh %u time_limit %u\n",
		g_qdf_dp_trace_data.proto_bitmap,
		g_qdf_dp_trace_data.verbosity,
		g_qdf_dp_trace_data.no_of_record,
		g_qdf_dp_trace_data.num_records_to_dump,
		g_qdf_dp_trace_data.live_mode_config,
		g_qdf_dp_trace_data.high_tput_thresh,
		g_qdf_dp_trace_data.thresh_time_limit);

	qdf_debugfs_printf(file,
		"STATS |DPT: icmp(%u %u) arp(%u %u) icmpv6(%u %u %u %u %u %u) dhcp(%u %u %u %u %u %u) eapol(%u %u %u %u %u)\n",
		g_qdf_dp_trace_data.icmp_req,
		g_qdf_dp_trace_data.icmp_resp,
		g_qdf_dp_trace_data.arp_req,
		g_qdf_dp_trace_data.arp_resp,
		g_qdf_dp_trace_data.icmpv6_req,
		g_qdf_dp_trace_data.icmpv6_resp,
		g_qdf_dp_trace_data.icmpv6_ns,
		g_qdf_dp_trace_data.icmpv6_na,
		g_qdf_dp_trace_data.icmpv6_rs,
		g_qdf_dp_trace_data.icmpv6_ra,
		g_qdf_dp_trace_data.dhcp_disc,
		g_qdf_dp_trace_data.dhcp_off,
		g_qdf_dp_trace_data.dhcp_req,
		g_qdf_dp_trace_data.dhcp_ack,
		g_qdf_dp_trace_data.dhcp_nack,
		g_qdf_dp_trace_data.dhcp_others,
		g_qdf_dp_trace_data.eapol_m1,
		g_qdf_dp_trace_data.eapol_m2,
		g_qdf_dp_trace_data.eapol_m3,
		g_qdf_dp_trace_data.eapol_m4,
		g_qdf_dp_trace_data.eapol_others);

	qdf_debugfs_printf(file,
		"DPT: Total Records: %d, Head: %d, Tail: %d\n",
		g_qdf_dp_trace_data.num, g_qdf_dp_trace_data.head,
		g_qdf_dp_trace_data.tail);

	spin_lock_bh(&l_dp_trace_lock);
	if (g_qdf_dp_trace_data.head != INVALID_QDF_DP_TRACE_ADDR) {
		i = g_qdf_dp_trace_data.head;
		tail = g_qdf_dp_trace_data.tail;

		if (count > g_qdf_dp_trace_data.num)
			count = g_qdf_dp_trace_data.num;

		if (tail >= (count - 1))
			i = tail - count + 1;
		else if (count != MAX_QDF_DP_TRACE_RECORDS)
			i = MAX_QDF_DP_TRACE_RECORDS - ((count - 1) -
						     tail);
		g_qdf_dp_trace_data.curr_pos = 0;
		g_qdf_dp_trace_data.saved_tail = tail;
	}
	spin_unlock_bh(&l_dp_trace_lock);

	return g_qdf_dp_trace_data.saved_tail;
}
qdf_export_symbol(qdf_dpt_get_curr_pos_debugfs);

QDF_STATUS qdf_dpt_dump_stats_debugfs(qdf_debugfs_file_t file,
				      uint32_t curr_pos)
{
	struct qdf_dp_trace_record_s p_record;
	uint32_t i = curr_pos;
	uint16_t num_records_to_dump = g_qdf_dp_trace_data.num_records_to_dump;

	if (!g_qdf_dp_trace_data.enable) {
		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
			  "%s: Tracing Disabled", __func__);
		return QDF_STATUS_E_FAILURE;
	}

	if (num_records_to_dump > g_qdf_dp_trace_data.num)
		num_records_to_dump = g_qdf_dp_trace_data.num;

	/*
	 * Max dp trace record size should always be less than
	 * QDF_DP_TRACE_PREPEND_STR_SIZE(100) + BUFFER_SIZE(121).
	 */
	if (WARN_ON(QDF_DP_TRACE_MAX_RECORD_SIZE <
				QDF_DP_TRACE_PREPEND_STR_SIZE + BUFFER_SIZE))
		return QDF_STATUS_E_FAILURE;

	spin_lock_bh(&l_dp_trace_lock);
	p_record = g_qdf_dp_trace_tbl[i];
	spin_unlock_bh(&l_dp_trace_lock);

	for (;; ) {
		/*
		 * Initially we get file as 1 page size, and
		 * if remaining size in file is less than one record max size,
		 * then return so that it gets an extra page.
		 */
		if ((file->size - file->count) < QDF_DP_TRACE_MAX_RECORD_SIZE) {
			spin_lock_bh(&l_dp_trace_lock);
			g_qdf_dp_trace_data.curr_pos = i;
			spin_unlock_bh(&l_dp_trace_lock);
			return QDF_STATUS_E_FAILURE;
		}

		switch (p_record.code) {
		case QDF_DP_TRACE_TXRX_PACKET_PTR_RECORD:
		case QDF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD:
		case QDF_DP_TRACE_FREE_PACKET_PTR_RECORD:
			qdf_dpt_display_ptr_record_debugfs(file, &p_record, i);
			break;

		case QDF_DP_TRACE_EAPOL_PACKET_RECORD:
		case QDF_DP_TRACE_DHCP_PACKET_RECORD:
		case QDF_DP_TRACE_ARP_PACKET_RECORD:
		case QDF_DP_TRACE_ICMP_PACKET_RECORD:
		case QDF_DP_TRACE_ICMPv6_PACKET_RECORD:
			qdf_dpt_display_proto_pkt_debugfs(file, &p_record, i);
			break;

		case QDF_DP_TRACE_TX_CREDIT_RECORD:
			qdf_dpt_display_credit_record_debugfs(file, &p_record,
							      i);
			break;

		case QDF_DP_TRACE_MGMT_PACKET_RECORD:
			qdf_dpt_display_mgmt_pkt_debugfs(file, &p_record, i);
			break;

		case QDF_DP_TRACE_EVENT_RECORD:
			qdf_dpt_display_event_record_debugfs(file, &p_record,
							     i);
			break;

		case QDF_DP_TRACE_HDD_TX_TIMEOUT:
			qdf_debugfs_printf(
					file, "DPT: %04d: %llu %s\n",
					i, p_record.time,
					qdf_dp_code_to_string(p_record.code));
			qdf_debugfs_printf(file, "HDD TX Timeout\n");
			break;

		case QDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
			qdf_debugfs_printf(
					file, "DPT: %04d: %llu %s\n",
					i, p_record.time,
					qdf_dp_code_to_string(p_record.code));
			qdf_debugfs_printf(file, "HDD SoftAP TX Timeout\n");
			break;

		case QDF_DP_TRACE_CE_FAST_PACKET_ERR_RECORD:
			qdf_debugfs_printf(
					file, "DPT: %04d: %llu %s\n",
					i, p_record.time,
					qdf_dp_code_to_string(p_record.code));
			qdf_debugfs_printf(file, "CE Fast Packet Error\n");
			break;

		case QDF_DP_TRACE_MAX:
			qdf_debugfs_printf(file,
				"%s: QDF_DP_TRACE_MAX event should not be generated\n",
				__func__);
			break;

		case QDF_DP_TRACE_HDD_TX_PACKET_RECORD:
		case QDF_DP_TRACE_HDD_RX_PACKET_RECORD:
		case QDF_DP_TRACE_TX_PACKET_RECORD:
		case QDF_DP_TRACE_RX_PACKET_RECORD:
		case QDF_DP_TRACE_LI_DP_TX_PACKET_RECORD:
		case QDF_DP_TRACE_LI_DP_RX_PACKET_RECORD:

		default:
			qdf_dpt_display_record_debugfs(file, &p_record, i);
			break;
		}

		if (++g_qdf_dp_trace_data.dump_counter == num_records_to_dump)
			break;

		spin_lock_bh(&l_dp_trace_lock);
		if (i == 0)
			i = MAX_QDF_DP_TRACE_RECORDS;

		i -= 1;
		p_record = g_qdf_dp_trace_tbl[i];
		spin_unlock_bh(&l_dp_trace_lock);
	}

	g_qdf_dp_trace_data.dump_counter = 0;

	return QDF_STATUS_SUCCESS;
}
qdf_export_symbol(qdf_dpt_dump_stats_debugfs);

void qdf_dpt_set_value_debugfs(uint8_t proto_bitmap, uint8_t no_of_record,
			    uint8_t verbosity, uint16_t num_records_to_dump)
{
	if (g_qdf_dp_trace_data.enable) {
		g_qdf_dp_trace_data.proto_bitmap = proto_bitmap;
		g_qdf_dp_trace_data.no_of_record = no_of_record;
		g_qdf_dp_trace_data.verbosity    = verbosity;
		g_qdf_dp_trace_data.num_records_to_dump = num_records_to_dump;
	}
}
qdf_export_symbol(qdf_dpt_set_value_debugfs);


void qdf_dp_trace_dump_all(uint32_t count, uint8_t pdev_id)
{
	struct qdf_dp_trace_record_s p_record;
	int32_t i, tail;

	if (!g_qdf_dp_trace_data.enable) {
		DPTRACE_PRINT("Tracing Disabled");
		return;
	}

	DPTRACE_PRINT(
		"DPT: config - bitmap 0x%x verb %u #rec %u live_config %u thresh %u time_limit %u",
		g_qdf_dp_trace_data.proto_bitmap,
		g_qdf_dp_trace_data.verbosity,
		g_qdf_dp_trace_data.no_of_record,
		g_qdf_dp_trace_data.live_mode_config,
		g_qdf_dp_trace_data.high_tput_thresh,
		g_qdf_dp_trace_data.thresh_time_limit);

	qdf_dp_trace_dump_stats();

	DPTRACE_PRINT("DPT: Total Records: %d, Head: %d, Tail: %d",
		      g_qdf_dp_trace_data.num, g_qdf_dp_trace_data.head,
		      g_qdf_dp_trace_data.tail);

	/* acquire the lock so that only one thread at a time can read
	 * the ring buffer
	 */
	spin_lock_bh(&l_dp_trace_lock);

	if (g_qdf_dp_trace_data.head != INVALID_QDF_DP_TRACE_ADDR) {
		i = g_qdf_dp_trace_data.head;
		tail = g_qdf_dp_trace_data.tail;

		if (count) {
			if (count > g_qdf_dp_trace_data.num)
				count = g_qdf_dp_trace_data.num;
			if (tail >= (count - 1))
				i = tail - count + 1;
			else if (count != MAX_QDF_DP_TRACE_RECORDS)
				i = MAX_QDF_DP_TRACE_RECORDS - ((count - 1) -
							     tail);
		}

		p_record = g_qdf_dp_trace_tbl[i];
		spin_unlock_bh(&l_dp_trace_lock);
		for (;; ) {
			qdf_dp_trace_cb_table[p_record.code](&p_record,
							(uint16_t)i, pdev_id, false);
			if (i == tail)
				break;
			i += 1;

			spin_lock_bh(&l_dp_trace_lock);
			if (MAX_QDF_DP_TRACE_RECORDS == i)
				i = 0;

			p_record = g_qdf_dp_trace_tbl[i];
			spin_unlock_bh(&l_dp_trace_lock);
		}
	} else {
		spin_unlock_bh(&l_dp_trace_lock);
	}
}
qdf_export_symbol(qdf_dp_trace_dump_all);

void qdf_dp_trace_throttle_live_mode(bool high_bw_request)
{
	static int bw_interval_counter;

	if (g_qdf_dp_trace_data.enable == false ||
		g_qdf_dp_trace_data.live_mode_config == false)
		return;

	if (high_bw_request) {
		g_qdf_dp_trace_data.live_mode = 0;
		bw_interval_counter = 0;
		return;
	}

	bw_interval_counter++;

	if (0 == (bw_interval_counter %
			g_qdf_dp_trace_data.thresh_time_limit)) {

		spin_lock_bh(&l_dp_trace_lock);
			if (g_qdf_dp_trace_data.print_pkt_cnt <=
				g_qdf_dp_trace_data.high_tput_thresh)
				g_qdf_dp_trace_data.live_mode = 1;

		g_qdf_dp_trace_data.print_pkt_cnt = 0;
		spin_unlock_bh(&l_dp_trace_lock);
	}
}
qdf_export_symbol(qdf_dp_trace_throttle_live_mode);

void qdf_dp_trace_apply_tput_policy(bool is_data_traffic)
{
	if (g_qdf_dp_trace_data.dynamic_verbosity_modify) {
		goto check_live_mode;
		return;
	}

	if (is_data_traffic) {
		g_qdf_dp_trace_data.verbosity =
					QDF_DP_TRACE_VERBOSITY_ULTRA_LOW;
	} else {
		g_qdf_dp_trace_data.verbosity =
					g_qdf_dp_trace_data.ini_conf_verbosity;
	}
check_live_mode:
	qdf_dp_trace_throttle_live_mode(is_data_traffic);
}
#endif

struct qdf_print_ctrl print_ctrl_obj[MAX_PRINT_CONFIG_SUPPORTED];

struct category_name_info g_qdf_category_name[MAX_SUPPORTED_CATEGORY] = {
	[QDF_MODULE_ID_TDLS] = {"tdls"},
	[QDF_MODULE_ID_ACS] = {"ACS"},
	[QDF_MODULE_ID_SCAN_SM] = {"scan state machine"},
	[QDF_MODULE_ID_SCANENTRY] = {"scan entry"},
	[QDF_MODULE_ID_WDS] = {"WDS"},
	[QDF_MODULE_ID_ACTION] = {"action"},
	[QDF_MODULE_ID_ROAM] = {"STA roaming"},
	[QDF_MODULE_ID_INACT] = {"inactivity"},
	[QDF_MODULE_ID_DOTH] = {"11h"},
	[QDF_MODULE_ID_IQUE] = {"IQUE"},
	[QDF_MODULE_ID_WME] = {"WME"},
	[QDF_MODULE_ID_ACL] = {"ACL"},
	[QDF_MODULE_ID_WPA] = {"WPA/RSN"},
	[QDF_MODULE_ID_RADKEYS] = {"dump 802.1x keys"},
	[QDF_MODULE_ID_RADDUMP] = {"dump radius packet"},
	[QDF_MODULE_ID_RADIUS] = {"802.1x radius client"},
	[QDF_MODULE_ID_DOT1XSM] = {"802.1x state machine"},
	[QDF_MODULE_ID_DOT1X] = {"802.1x authenticator"},
	[QDF_MODULE_ID_POWER] = {"power save"},
	[QDF_MODULE_ID_STATE] = {"state"},
	[QDF_MODULE_ID_OUTPUT] = {"output"},
	[QDF_MODULE_ID_SCAN] = {"scan"},
	[QDF_MODULE_ID_AUTH] = {"authentication"},
	[QDF_MODULE_ID_ASSOC] = {"association"},
	[QDF_MODULE_ID_NODE] = {"node"},
	[QDF_MODULE_ID_ELEMID] = {"element ID"},
	[QDF_MODULE_ID_XRATE] = {"rate"},
	[QDF_MODULE_ID_INPUT] = {"input"},
	[QDF_MODULE_ID_CRYPTO] = {"crypto"},
	[QDF_MODULE_ID_DUMPPKTS] = {"dump packet"},
	[QDF_MODULE_ID_DEBUG] = {"debug"},
	[QDF_MODULE_ID_MLME] = {"mlme"},
	[QDF_MODULE_ID_RRM] = {"rrm"},
	[QDF_MODULE_ID_WNM] = {"wnm"},
	[QDF_MODULE_ID_P2P_PROT] = {"p2p_prot"},
	[QDF_MODULE_ID_PROXYARP] = {"proxyarp"},
	[QDF_MODULE_ID_L2TIF] = {"l2tif"},
	[QDF_MODULE_ID_WIFIPOS] = {"wifipos"},
	[QDF_MODULE_ID_WRAP] = {"wrap"},
	[QDF_MODULE_ID_DFS] = {"dfs"},
	[QDF_MODULE_ID_ATF] = {"atf"},
	[QDF_MODULE_ID_SPLITMAC] = {"splitmac"},
	[QDF_MODULE_ID_IOCTL] = {"ioctl"},
	[QDF_MODULE_ID_NAC] = {"nac"},
	[QDF_MODULE_ID_MESH] = {"mesh"},
	[QDF_MODULE_ID_MBO] = {"mbo"},
	[QDF_MODULE_ID_EXTIOCTL_CHANSWITCH] = {"extchanswitch"},
	[QDF_MODULE_ID_EXTIOCTL_CHANSSCAN] = {"extchanscan"},
	[QDF_MODULE_ID_TLSHIM] = {"tlshim"},
	[QDF_MODULE_ID_WMI] = {"WMI"},
	[QDF_MODULE_ID_HTT] = {"HTT"},
	[QDF_MODULE_ID_HDD] = {"HDD"},
	[QDF_MODULE_ID_SME] = {"SME"},
	[QDF_MODULE_ID_PE] = {"PE"},
	[QDF_MODULE_ID_WMA] = {"WMA"},
	[QDF_MODULE_ID_SYS] = {"SYS"},
	[QDF_MODULE_ID_QDF] = {"QDF"},
	[QDF_MODULE_ID_SAP] = {"SAP"},
	[QDF_MODULE_ID_HDD_SOFTAP] = {"HDD_SAP"},
	[QDF_MODULE_ID_HDD_DATA] = {"DATA"},
	[QDF_MODULE_ID_HDD_SAP_DATA] = {"SAP_DATA"},
	[QDF_MODULE_ID_HIF] = {"HIF"},
	[QDF_MODULE_ID_HTC] = {"HTC"},
	[QDF_MODULE_ID_TXRX] = {"TXRX"},
	[QDF_MODULE_ID_QDF_DEVICE] = {"QDF_DEV"},
	[QDF_MODULE_ID_CFG] = {"CFG"},
	[QDF_MODULE_ID_BMI] = {"BMI"},
	[QDF_MODULE_ID_EPPING] = {"EPPING"},
	[QDF_MODULE_ID_QVIT] = {"QVIT"},
	[QDF_MODULE_ID_DP] = {"DP"},
	[QDF_MODULE_ID_HAL] = {"HAL"},
	[QDF_MODULE_ID_SOC] = {"SOC"},
	[QDF_MODULE_ID_OS_IF] = {"OSIF"},
	[QDF_MODULE_ID_TARGET_IF] = {"TIF"},
	[QDF_MODULE_ID_SCHEDULER] = {"SCH"},
	[QDF_MODULE_ID_MGMT_TXRX] = {"MGMT_TXRX"},
	[QDF_MODULE_ID_PMO] = {"PMO"},
	[QDF_MODULE_ID_POLICY_MGR] = {"POLICY_MGR"},
	[QDF_MODULE_ID_SA_API] = {"SA_API"},
	[QDF_MODULE_ID_NAN] = {"NAN"},
	[QDF_MODULE_ID_SPECTRAL] = {"SPECTRAL"},
	[QDF_MODULE_ID_P2P] = {"P2P"},
	[QDF_MODULE_ID_OFFCHAN_TXRX] = {"OFFCHAN"},
	[QDF_MODULE_ID_REGULATORY] = {"REGULATORY"},
	[QDF_MODULE_ID_OBJ_MGR] = {"OBJMGR"},
	[QDF_MODULE_ID_SERIALIZATION] = {"SER"},
	[QDF_MODULE_ID_NSS] = {"NSS"},
	[QDF_MODULE_ID_ROAM_DEBUG] = {"roam debug"},
	[QDF_MODULE_ID_DIRECT_BUF_RX] = {"DIRECT_BUF_RX"},
	[QDF_MODULE_ID_DISA] = {"disa"},
	[QDF_MODULE_ID_GREEN_AP] = {"GREEN_AP"},
	[QDF_MODULE_ID_FD] = {"FILS discovery"},
	[QDF_MODULE_ID_FTM] = {"FTM"},
	[QDF_MODULE_ID_OCB] = {"OCB"},
	[QDF_MODULE_ID_CONFIG] = {"CONFIG"},
	[QDF_MODULE_ID_IPA] = {"IPA"},
	[QDF_MODULE_ID_CP_STATS] = {"CP_STATS"},
	[QDF_MODULE_ID_DCS] = {"DCS"},
	[QDF_MODULE_ID_ACTION_OUI] = {"action_oui"},
	[QDF_MODULE_ID_TARGET] = {"TARGET"},
	[QDF_MODULE_ID_MBSSIE] = {"MBSSIE"},
	[QDF_MODULE_ID_FWOL] = {"fwol"},
	[QDF_MODULE_ID_SM_ENGINE] = {"SM_ENG"},
	[QDF_MODULE_ID_CMN_MLME] = {"CMN_MLME"},
	[QDF_MODULE_ID_BSSCOLOR] = {"BSSCOLOR"},
	[QDF_MODULE_ID_CFR] = {"CFR"},
	[QDF_MODULE_ID_DP_TX_CAPTURE] = {"TX_CAPTURE_ENHANCE"},
	[QDF_MODULE_ID_INTEROP_ISSUES_AP] = {"INTEROP_ISSUES_AP"},
	[QDF_MODULE_ID_DENYLIST_MGR] = {"dlm"},
	[QDF_MODULE_ID_QLD] = {"QLD"},
	[QDF_MODULE_ID_DYNAMIC_MODE_CHG] = {"Dynamic Mode Change"},
	[QDF_MODULE_ID_COEX] = {"COEX"},
	[QDF_MODULE_ID_MON_FILTER] = {"Monitor Filter"},
	[QDF_MODULE_ID_PKT_CAPTURE] = {"pkt_capture"},
	[QDF_MODULE_ID_RPTR] = {"RPTR"},
	[QDF_MODULE_ID_6GHZ] = {"6GHZ"},
	[QDF_MODULE_ID_IOT_SIM] = {"IOT_SIM"},
	[QDF_MODULE_ID_MSCS] = {"MSCS"},
	[QDF_MODULE_ID_GPIO] = {"GPIO_CFG"},
	[QDF_MODULE_ID_IFMGR] = {"IF_MGR"},
	[QDF_MODULE_ID_DIAG] = {"DIAG"},
	[QDF_MODULE_ID_DP_INIT] = {"DP_INIT"},
	[QDF_MODULE_ID_DP_TX] = {"DP_TX"},
	[QDF_MODULE_ID_DP_RX] = {"DP_RX"},
	[QDF_MODULE_ID_DP_STATS] = {"DP_STATS"},
	[QDF_MODULE_ID_DP_HTT] = {"DP_HTT"},
	[QDF_MODULE_ID_DP_PEER] = {"DP_PEER"},
	[QDF_MODULE_ID_DP_RX_ERROR] = {"DP_RX_ERROR"},
	[QDF_MODULE_ID_DP_HTT_TX_STATS] = {"DP_HTT_TX_STATS"},
	[QDF_MODULE_ID_DP_RX_MON_STATUS] = {"DP_RX_MON_STATUS"},
	[QDF_MODULE_ID_DP_RX_MON_DEST] = {"DP_RX_MON_DEST"},
	[QDF_MODULE_ID_DP_REO] = {"DP_REO"},
	[QDF_MODULE_ID_DP_TX_COMP] = {"DP_TX_COMP"},
	[QDF_MODULE_ID_DP_VDEV] = {"DP_VDEV"},
	[QDF_MODULE_ID_DP_CDP] = {"DP_CDP"},
	[QDF_MODULE_ID_TSO] = {"TSO"},
	[QDF_MODULE_ID_ME] = {"ME"},
	[QDF_MODULE_ID_QWRAP] = {"QWRAP"},
	[QDF_MODULE_ID_DBDC_REP] = {"DBDC_REP"},
	[QDF_MODULE_ID_EXT_AP] = {"EXT_AP"},
	[QDF_MODULE_ID_MLO] = {"MLO_MGR"},
	[QDF_MODULE_ID_MGMT_RX_REO] = {"MGMT_RX_REO"},
	[QDF_MODULE_ID_MLOIE] = {"MLOIE"},
	[QDF_MODULE_ID_MBSS] = {"MBSS"},
	[QDF_MODULE_ID_MON] = {"MONITOR"},
	[QDF_MODULE_ID_AFC] = {"AFC"},
	[QDF_MODULE_ID_TWT] = {"TWT"},
	[QDF_MODULE_ID_SON] = {"SON"},
	[QDF_MODULE_ID_WLAN_PRE_CAC] = {"PRE_CAC"},
	[QDF_MODULE_ID_T2LM] = {"T2LM"},
	[QDF_MODULE_ID_DP_SAWF] = {"DP_SAWF"},
	[QDF_MODULE_ID_SCS] = {"SCS"},
	[QDF_MODULE_ID_DP_UMAC_RESET] = {"UMAC_HW_RESET"},
	[QDF_MODULE_ID_COAP] = {"COAP"},
	[QDF_MODULE_ID_FTM_TIME_SYNC] = {"Time Sync"},
	[QDF_MODULE_ID_WIFI_RADAR] = {"WIFI RADAR"},
	[QDF_MODULE_ID_CDP] =  {"CDP"},
	[QDF_MODULE_ID_QMI] = {"QMI"},
	[QDF_MODULE_ID_SOUNDING] = {"SOUNDING"},
	[QDF_MODULE_ID_SAWF] = {"SAWF"},
	[QDF_MODULE_ID_EPCS] = {"EPCS"},
	[QDF_MODULE_ID_LL_SAP] = {"LL_SAP"},
	[QDF_MODULE_ID_COHOSTED_BSS] = {"COHOSTED_BSS"},
	[QDF_MODULE_ID_TELEMETRY_AGENT] = {"TELEMETRY_AGENT"},
	[QDF_MODULE_ID_RF_PATH_SWITCH] = {"Dynamic RF Path Switch"},
	[QDF_MODULE_ID_ANY] = {"ANY"},
};
qdf_export_symbol(g_qdf_category_name);

void qdf_trace_display(void)
{
	QDF_MODULE_ID module_id;

	pr_err("     1)FATAL  2)ERROR  3)WARN  4)INFO  5)INFO_H  6)INFO_M  7)INFO_L 8)DEBUG\n");
	for (module_id = 0; module_id < QDF_MODULE_ID_MAX; ++module_id) {
		pr_err("%2d)%s    %s        %s       %s       %s        %s         %s         %s        %s\n",
		       (int)module_id,
		       g_qdf_category_name[module_id].category_name_str,
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_FATAL) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_ERROR) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_WARN) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_INFO) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_INFO_HIGH) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_INFO_MED) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_INFO_LOW) ? "X" : " ",
		       qdf_print_is_verbose_enabled(qdf_pidx, module_id,
			       QDF_TRACE_LEVEL_DEBUG) ? "X" : " ");
	}
}
qdf_export_symbol(qdf_trace_display);

#ifdef WLAN_MAX_LOGS_PER_SEC
static qdf_time_t __log_window_end;
static qdf_atomic_t __log_window_count;
uint32_t qdf_rl_print_count = WLAN_MAX_LOGS_PER_SEC;
uint32_t qdf_rl_print_time = 1;
uint32_t qdf_rl_print_suppressed;

bool qdf_detected_excessive_logging(void)
{
	qdf_time_t now = qdf_system_ticks();
	bool excessive_prints = false;

	/*
	 * If 'now' is more recent than the end of the window, reset.
	 *
	 * Note: This is not thread safe, and can result in more than one reset.
	 * For our purposes, this is fine.
	 */
	if (!qdf_atomic_read(&__log_window_count)) {
		__log_window_end = now + (qdf_system_ticks_per_sec * qdf_rl_print_time);
	} else if (qdf_system_time_after(now, __log_window_end)) {
		__log_window_end = now + (qdf_system_ticks_per_sec * qdf_rl_print_time);
		qdf_atomic_set(&__log_window_count, 0);
	}

	if (qdf_atomic_inc_return(&__log_window_count) > qdf_rl_print_count)
		excessive_prints = true;

	return excessive_prints;
}

void qdf_rl_print_count_set(uint32_t rl_print_count)
{
	qdf_rl_print_count = rl_print_count;
}

qdf_export_symbol(qdf_rl_print_count_set);

void qdf_rl_print_time_set(uint32_t rl_print_time)
{
	qdf_rl_print_time = rl_print_time;
}

qdf_export_symbol(qdf_rl_print_time_set);

void qdf_rl_print_suppressed_log(void)
{
	if (qdf_rl_print_suppressed) {
		pr_err("QDF Ratelimiting: %d prints suppressed",
		       qdf_rl_print_suppressed);
		qdf_rl_print_suppressed = 0;
	}
}

void qdf_rl_print_suppressed_inc(void)
{
	qdf_rl_print_suppressed++;
}
#else
#define qdf_rl_print_suppressed_log()
#define qdf_rl_print_suppressed_inc()
#endif /* WLAN_MAX_LOGS_PER_SEC */

#ifdef QDF_TRACE_PRINT_ENABLE
static inline void print_to_console(char *str_buffer)
{
	if (qdf_in_interrupt() && qdf_detected_excessive_logging()) {
		qdf_rl_print_suppressed_inc();
		return;
	}
	qdf_rl_print_suppressed_log();
	pr_err("%s\n", str_buffer);
}
#else

#define print_to_console(str)
#endif

#ifdef MULTI_IF_NAME
static const char *qdf_trace_wlan_modname(void)
{
	return MULTI_IF_NAME;
}
#else
static const char *qdf_trace_wlan_modname(void)
{
	return "wlan";
}
#endif

void qdf_trace_msg_cmn(unsigned int idx,
			QDF_MODULE_ID category,
			QDF_TRACE_LEVEL verbose,
			const char *str_format, va_list val)
{
	char str_buffer[QDF_TRACE_BUFFER_SIZE];
	int n;

	/* Check if index passed is valid */
	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_info("%s: Invalid index - %d\n", __func__, idx);
		return;
	}

	/* Check if print control object is in use */
	if (!print_ctrl_obj[idx].in_use) {
		pr_info("%s: Invalid print control object\n", __func__);
		return;
	}

	/* Check if category passed is valid */
	if (category < 0 || category >= MAX_SUPPORTED_CATEGORY) {
		vscnprintf(str_buffer, QDF_TRACE_BUFFER_SIZE, str_format, val);
		pr_info("%s: Invalid category: %d, log: %s\n",
			__func__, category, str_buffer);
		return;
	}

	/* Check if verbose mask is valid */
	if (verbose < 0 || verbose >= QDF_TRACE_LEVEL_MAX) {
		vscnprintf(str_buffer, QDF_TRACE_BUFFER_SIZE, str_format, val);
		pr_info("%s: Invalid verbose level %d, log: %s\n",
			__func__, verbose, str_buffer);
		return;
	}

	/*
	 * Print the trace message when the desired verbose level is set in
	 * the desired category for the print control object
	 */
	if (print_ctrl_obj[idx].cat_info[category].category_verbose_mask &
	    QDF_TRACE_LEVEL_TO_MODULE_BITMASK(verbose)) {
		static const char * const VERBOSE_STR[] = {
			[QDF_TRACE_LEVEL_NONE] = "",
			[QDF_TRACE_LEVEL_FATAL] = "F",
			[QDF_TRACE_LEVEL_ERROR] = "E",
			[QDF_TRACE_LEVEL_WARN] = "W",
			[QDF_TRACE_LEVEL_INFO] = "I",
			[QDF_TRACE_LEVEL_INFO_HIGH] = "IH",
			[QDF_TRACE_LEVEL_INFO_MED] = "IM",
			[QDF_TRACE_LEVEL_INFO_LOW] = "IL",
			[QDF_TRACE_LEVEL_DEBUG] = "D",
			[QDF_TRACE_LEVEL_TRACE] = "T",
			[QDF_TRACE_LEVEL_ALL] = "" };

		/* print the prefix string into the string buffer... */
		n = scnprintf(str_buffer, QDF_TRACE_BUFFER_SIZE,
			     "%s: [%d:%s:%s] ", qdf_trace_wlan_modname(),
			     in_interrupt() ? 0 : current->pid,
			     VERBOSE_STR[verbose],
			     g_qdf_category_name[category].category_name_str);

		/* print the formatted log message after the prefix string */
		vscnprintf(str_buffer + n, QDF_TRACE_BUFFER_SIZE - n,
			   str_format, val);
#if defined(WLAN_LOGGING_SOCK_SVC_ENABLE)
		wlan_log_to_user(verbose, (char *)str_buffer,
				 strlen(str_buffer));
		if (qdf_unlikely(qdf_log_dump_at_kernel_enable))
			print_to_console(str_buffer);
#else
		pr_err("%s\n", str_buffer);
#endif
	}
}
qdf_export_symbol(qdf_trace_msg_cmn);

QDF_STATUS qdf_print_setup(void)
{
	int i;

	/* Loop through all print ctrl objects */
	for (i = 0; i < MAX_PRINT_CONFIG_SUPPORTED; i++) {
		if (qdf_print_ctrl_cleanup(i))
			return QDF_STATUS_E_FAILURE;
	}
	return QDF_STATUS_SUCCESS;
}
qdf_export_symbol(qdf_print_setup);

QDF_STATUS qdf_print_ctrl_cleanup(unsigned int idx)
{
	int i = 0;

	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_info("%s: Invalid index - %d\n", __func__, idx);
		return QDF_STATUS_E_FAILURE;
	}

	/* Clean up the print control object corresponding to that index
	 * If success, callee to change print control index to -1
	 */

	for (i = 0; i < MAX_SUPPORTED_CATEGORY; i++) {
		print_ctrl_obj[idx].cat_info[i].category_verbose_mask =
							QDF_TRACE_LEVEL_NONE;
	}
	print_ctrl_obj[idx].custom_print = NULL;
	print_ctrl_obj[idx].custom_ctxt = NULL;
	qdf_print_clean_node_flag(idx);
	print_ctrl_obj[idx].in_use = false;

	return QDF_STATUS_SUCCESS;
}
qdf_export_symbol(qdf_print_ctrl_cleanup);

int qdf_print_ctrl_register(const struct category_info *cinfo,
			    void *custom_print_handler,
			    void *custom_ctx,
			    const char *pctrl_name)
{
	int idx = -1;
	int i = 0;

	for (i = 0; i < MAX_PRINT_CONFIG_SUPPORTED; i++) {
		if (!print_ctrl_obj[i].in_use) {
			idx = i;
			break;
		}
	}

	/* Callee to handle idx -1 appropriately */
	if (idx == -1) {
		pr_info("%s: Allocation failed! No print control object free\n",
			__func__);
		return idx;
	}

	print_ctrl_obj[idx].in_use = true;

	/*
	 * In case callee does not pass category info,
	 * custom print handler, custom context and print control name,
	 * we do not set any value here. Clean up for the print control
	 * getting allocated would have taken care of initializing
	 * default values.
	 *
	 * We need to only set in_use to 1 in such a case
	 */

	if (pctrl_name) {
		qdf_str_lcopy(print_ctrl_obj[idx].name, pctrl_name,
			      sizeof(print_ctrl_obj[idx].name));
	}

	if (custom_print_handler)
		print_ctrl_obj[idx].custom_print = custom_print_handler;

	if (custom_ctx)
		print_ctrl_obj[idx].custom_ctxt = custom_ctx;

	if (cinfo) {
		for (i = 0; i < MAX_SUPPORTED_CATEGORY; i++) {
			if (cinfo[i].category_verbose_mask ==
			    QDF_TRACE_LEVEL_ALL) {
				print_ctrl_obj[idx].cat_info[i]
				.category_verbose_mask = 0xFFFF;
			} else if ((cinfo[i].category_verbose_mask ==
				   QDF_TRACE_LEVEL_NONE) ||
				   (cinfo[i].category_verbose_mask ==
				   QDF_TRACE_LEVEL_TO_MODULE_BITMASK(
				   QDF_TRACE_LEVEL_NONE))) {
				print_ctrl_obj[idx].cat_info[i]
				.category_verbose_mask = 0;
			} else {
				print_ctrl_obj[idx].cat_info[i]
				.category_verbose_mask =
				cinfo[i].category_verbose_mask;
			}
		}
	}

	return idx;
}
qdf_export_symbol(qdf_print_ctrl_register);

#ifdef QDF_TRACE_PRINT_ENABLE
void qdf_shared_print_ctrl_cleanup(void)
{
	qdf_print_ctrl_cleanup(qdf_pidx);
}
qdf_export_symbol(qdf_shared_print_ctrl_cleanup);

/*
 * Set this to invalid value to differentiate with user-provided
 * value.
 */
int qdf_dbg_mask = QDF_TRACE_LEVEL_MAX;
qdf_export_symbol(qdf_dbg_mask);
qdf_declare_param(qdf_dbg_mask, int);

/*
 * QDF can be passed parameters which indicate the
 * debug level for each module.
 * an array of string values are passed, each string hold the following form
 *
 * <module name string>=<integer debug level value>
 *
 * The array qdf_dbg_arr will hold these module-string=value strings
 * The variable qdf_dbg_arr_cnt will have the count of how many such
 * string values were passed.
 */
static char *qdf_dbg_arr[QDF_MODULE_ID_MAX];
static int qdf_dbg_arr_cnt;
qdf_declare_param_array(qdf_dbg_arr, charp, &qdf_dbg_arr_cnt);

static uint16_t set_cumulative_verbose_mask(QDF_TRACE_LEVEL max_level)
{
	uint16_t category_verbose_mask = 0;
	QDF_TRACE_LEVEL level;

	for (level = QDF_TRACE_LEVEL_FATAL; level <= max_level; level++) {
		category_verbose_mask |=
			QDF_TRACE_LEVEL_TO_MODULE_BITMASK(level);
	}
	return category_verbose_mask;
}

static QDF_MODULE_ID find_qdf_module_from_string(char *str)
{
	QDF_MODULE_ID mod_id;

	for (mod_id = 0; mod_id < QDF_MODULE_ID_MAX; mod_id++) {
		if (strcasecmp(str,
				g_qdf_category_name[mod_id].category_name_str)
				== 0) {
			break;
		}
	}
	return mod_id;
}

static void process_qdf_dbg_arr_param(struct category_info *cinfo,
					int array_index)
{
	char *mod_val_str, *mod_str, *val_str;
	unsigned long dbg_level;
	QDF_MODULE_ID mod_id;

	mod_val_str = qdf_dbg_arr[array_index];
	mod_str = strsep(&mod_val_str, "=");
	val_str = mod_val_str;
	if (!val_str) {
		pr_info("qdf_dbg_arr: %s not in the <mod>=<val> form\n",
				mod_str);
		return;
	}

	mod_id = find_qdf_module_from_string(mod_str);
	if (mod_id >= QDF_MODULE_ID_MAX) {
		pr_info("ERROR!!Module name %s not in the list of modules\n",
				mod_str);
		return;
	}

	if (kstrtol(val_str, 10, &dbg_level) < 0) {
		pr_info("ERROR!!Invalid debug level for module: %s\n",
				mod_str);
		return;
	}

	if (dbg_level >= QDF_TRACE_LEVEL_MAX) {
		pr_info("ERROR!!Debug level for %s too high", mod_str);
		pr_info("max: %d given %lu\n", QDF_TRACE_LEVEL_MAX,
				dbg_level);
		return;
	}

	pr_info("User passed setting module %s(%d) to level %lu\n",
			mod_str,
			mod_id,
			dbg_level);
	cinfo[mod_id].category_verbose_mask =
		set_cumulative_verbose_mask((QDF_TRACE_LEVEL)dbg_level);
}

static void set_default_trace_levels(struct category_info *cinfo)
{
	int i;
	static QDF_TRACE_LEVEL module_trace_default_level[QDF_MODULE_ID_MAX] = {
		[QDF_MODULE_ID_TDLS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ACS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SCAN_SM] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SCANENTRY] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WDS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ACTION] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ROAM] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_INACT] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DOTH] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_IQUE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WME] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ACL] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WPA] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_RADKEYS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_RADDUMP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_RADIUS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DOT1XSM] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DOT1X] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_POWER] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_STATE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_OUTPUT] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SCAN] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_AUTH] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ASSOC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_NODE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ELEMID] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_XRATE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_INPUT] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_CRYPTO] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DUMPPKTS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DEBUG] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_MLME] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_RRM] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WNM] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_P2P_PROT] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_PROXYARP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_L2TIF] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WIFIPOS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WRAP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DFS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ATF] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_SPLITMAC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_IOCTL] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_NAC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_MESH] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_MBO] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_EXTIOCTL_CHANSWITCH] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_EXTIOCTL_CHANSSCAN] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_TLSHIM] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WMI] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_HTT] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_HDD] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SME] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_PE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WMA] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SYS] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_QDF] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_SAP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_HDD_SOFTAP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_HDD_DATA] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_HDD_SAP_DATA] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_HIF] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_HTC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_TXRX] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_QDF_DEVICE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_CFG] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_BMI] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_EPPING] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_QVIT] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DP] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_HAL] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SOC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_OS_IF] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_TARGET_IF] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_SCHEDULER] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_MGMT_TXRX] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SERIALIZATION] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_PMO] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_P2P] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_POLICY_MGR] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_CONFIG] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_REGULATORY] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SA_API] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_NAN] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_OFFCHAN_TXRX] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SON] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SPECTRAL] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_OBJ_MGR] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_NSS] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_ROAM_DEBUG] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_CDP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DIRECT_BUF_RX] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DISA] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_GREEN_AP] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_FTM] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_FD] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_OCB] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_IPA] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_ACTION_OUI] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_CP_STATS] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DCS] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_MBSSIE] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_FWOL] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_SM_ENGINE] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_CMN_MLME] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_BSSCOLOR] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_CFR] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DP_TX_CAPTURE] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_INTEROP_ISSUES_AP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_DENYLIST_MGR] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_QLD] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DYNAMIC_MODE_CHG] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_COEX] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_MON_FILTER] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_PKT_CAPTURE] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_RPTR] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_6GHZ] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_IOT_SIM] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_MSCS] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_GPIO] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_IFMGR] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DIAG] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DP_INIT] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_TX] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_RX] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_STATS] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_HTT] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_PEER] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_RX_ERROR] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_HTT_TX_STATS] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_RX_MON_STATUS] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_RX_MON_DEST] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_REO] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_TX_COMP] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_VDEV] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DP_CDP] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_TSO] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_ME] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_QWRAP] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_DBDC_REP] = QDF_TRACE_LEVEL_FATAL,
		[QDF_MODULE_ID_EXT_AP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_MLO] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_MLOIE] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_MBSS] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_MON] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_MGMT_RX_REO] = QDF_TRACE_LEVEL_WARN,
		[QDF_MODULE_ID_TWT] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_WLAN_PRE_CAC] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_T2LM] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DP_SAWF] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_SCS] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_DP_UMAC_RESET] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_COAP] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_FTM_TIME_SYNC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_AFC] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_WIFI_RADAR] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_TARGET] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_QMI] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_SOUNDING] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_SAWF] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_EPCS] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_LL_SAP] = QDF_TRACE_LEVEL_NONE,
		[QDF_MODULE_ID_COHOSTED_BSS] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_TELEMETRY_AGENT] = QDF_TRACE_LEVEL_ERROR,
		[QDF_MODULE_ID_RF_PATH_SWITCH] = QDF_TRACE_LEVEL_INFO,
		[QDF_MODULE_ID_ANY] = QDF_TRACE_LEVEL_INFO,
	};

	for (i = 0; i < MAX_SUPPORTED_CATEGORY; i++) {
		cinfo[i].category_verbose_mask = set_cumulative_verbose_mask(
				module_trace_default_level[i]);
	}
}

void qdf_shared_print_ctrl_init(void)
{
	int i;
	struct category_info cinfo[MAX_SUPPORTED_CATEGORY];

	set_default_trace_levels(cinfo);

	/*
	 * User specified across-module single debug level
	 */
	if ((qdf_dbg_mask >= 0) && (qdf_dbg_mask < QDF_TRACE_LEVEL_MAX)) {
		pr_info("User specified module debug level of %d\n",
			qdf_dbg_mask);
		for (i = 0; i < MAX_SUPPORTED_CATEGORY; i++) {
			cinfo[i].category_verbose_mask =
			set_cumulative_verbose_mask(qdf_dbg_mask);
		}
	} else if (qdf_dbg_mask != QDF_TRACE_LEVEL_MAX) {
		pr_info("qdf_dbg_mask value is invalid\n");
		pr_info("Using the default module debug levels instead\n");
	}

	/*
	 * Module ID-Level specified as array during module load
	 */
	for (i = 0; i < qdf_dbg_arr_cnt; i++) {
		process_qdf_dbg_arr_param(cinfo, i);
	}
	qdf_pidx = qdf_print_ctrl_register(cinfo, NULL, NULL,
			"LOG_SHARED_OBJ");
}
qdf_export_symbol(qdf_shared_print_ctrl_init);
#endif

#ifdef QCA_WIFI_MODULE_PARAMS_FROM_INI
QDF_STATUS qdf_module_param_handler(void *context, const char *str_param,
				    const char *str)
{
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	uint16_t param = 0;
	uint32_t flush_tmr_prd;
	bool dump_flag;

	while (param < QDF_PARAM_MAX) {
		if (qdf_str_eq(qdf_module_param[param], str_param)) {
			switch (param) {
			case MEM_DEBUG_DISABLED:
				status = qdf_mem_debug_disabled_config_set(str);
				break;
			case QDF_DBG_MASK:
				status = qdf_int32_parse(str, &qdf_dbg_mask);
				break;
			case PREALLOC_DISABLED:
				status = qdf_prealloc_disabled_config_set(str);
				break;
			case QDF_LOG_DUMP_AT_KERNEL_ENABLE:
				status = qdf_bool_parse(str, &dump_flag);
				qdf_log_dump_at_kernel_enable = dump_flag;
				break;
			case QDF_DBG_ARR:
				qdf_dbg_arr[0] = (char *)str;
				status = QDF_STATUS_SUCCESS;
				break;
			case QDF_LOG_FLUSH_TIMER_PERIOD:
				status = qdf_uint32_parse(str, &flush_tmr_prd);
				qdf_log_flush_timer_period = flush_tmr_prd;
				break;
			default:
				break;
			}
			return status;
		}
		param++;
	}

	return QDF_STATUS_SUCCESS;
}

void qdf_initialize_module_param_from_ini(void)
{
	QDF_STATUS status;
	char *path = QDF_WIFI_MODULE_PARAMS_FILE;

	status = qdf_ini_parse(path, NULL, qdf_module_param_handler, NULL);
	if (QDF_IS_STATUS_ERROR(status)) {
		QDF_TRACE_ERROR(QDF_MODULE_ID_QDF,
				"Failed to parse *.ini file @ %s; status:%d",
				path, status);
		return;
	}
}
#endif

QDF_STATUS qdf_print_set_category_verbose(unsigned int idx,
						QDF_MODULE_ID category,
						QDF_TRACE_LEVEL verbose,
						bool is_set)
{
	/* Check if index passed is valid */
	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_err("%s: Invalid index - %d\n", __func__, idx);
		return QDF_STATUS_E_FAILURE;
	}

	/* Check if print control object is in use */
	if (!print_ctrl_obj[idx].in_use) {
		pr_err("%s: Invalid print control object\n", __func__);
		return QDF_STATUS_E_FAILURE;
	}

	/* Check if category passed is valid */
	if (category < 0 || category >= MAX_SUPPORTED_CATEGORY) {
		pr_err("%s: Invalid category: %d\n", __func__, category);
		return QDF_STATUS_E_FAILURE;
	}

	/* Check if verbose mask is valid */
	if (verbose < 0 || verbose >= QDF_TRACE_LEVEL_MAX) {
		pr_err("%s: Invalid verbose level %d\n", __func__, verbose);
		return QDF_STATUS_E_FAILURE;
	}

	if (verbose == QDF_TRACE_LEVEL_ALL) {
		print_ctrl_obj[idx].cat_info[category].category_verbose_mask =
				0xFFFF;
		return QDF_STATUS_SUCCESS;
	}

	if (verbose == QDF_TRACE_LEVEL_NONE) {
		print_ctrl_obj[idx].cat_info[category].category_verbose_mask =
				QDF_TRACE_LEVEL_NONE;
		return QDF_STATUS_SUCCESS;
	}

	if (!is_set) {
		if (print_ctrl_obj[idx].cat_info[category].category_verbose_mask
		    & QDF_TRACE_LEVEL_TO_MODULE_BITMASK(verbose)) {
			print_ctrl_obj[idx].cat_info[category]
				.category_verbose_mask &=
				~QDF_TRACE_LEVEL_TO_MODULE_BITMASK(verbose);
		}
	} else {
		print_ctrl_obj[idx].cat_info[category].category_verbose_mask |=
				QDF_TRACE_LEVEL_TO_MODULE_BITMASK(verbose);
	}

	pr_debug("%s: Print control object %d, Category %d, Verbose level %d\n",
		__func__,
		idx,
		category,
		print_ctrl_obj[idx].cat_info[category].category_verbose_mask);

	return QDF_STATUS_SUCCESS;
}
qdf_export_symbol(qdf_print_set_category_verbose);

void qdf_log_dump_at_kernel_level(bool enable)
{
	if (qdf_log_dump_at_kernel_enable == enable) {
		QDF_TRACE_INFO(QDF_MODULE_ID_QDF,
			       "qdf_log_dump_at_kernel_enable is already %d\n",
			       enable);
	}
	qdf_log_dump_at_kernel_enable = enable;
}

qdf_export_symbol(qdf_log_dump_at_kernel_level);

QDF_TRACE_LEVEL qdf_print_get_category_verbose(unsigned int idx,
					       QDF_MODULE_ID category)
{
	/* Check if index passed is valid */
	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_info("%s: Invalid index - %d\n", __func__, idx);
		return false;
	}

	/* Check if print control object is in use */
	if (!print_ctrl_obj[idx].in_use) {
		pr_info("%s: Invalid print control object\n", __func__);
		return false;
	}

	/* Check if category passed is valid */
	if (category < 0 || category >= MAX_SUPPORTED_CATEGORY) {
		pr_info("%s: Invalid category: %d\n", __func__, category);
		return false;
	}

	return print_ctrl_obj[idx].cat_info[category].category_verbose_mask;
}

qdf_export_symbol(qdf_print_get_category_verbose);

bool qdf_print_is_category_enabled(unsigned int idx, QDF_MODULE_ID category)
{
	QDF_TRACE_LEVEL verbose_mask;

	verbose_mask = qdf_print_get_category_verbose(idx, category);

	if (verbose_mask == QDF_TRACE_LEVEL_NONE)
		return false;
	else
		return true;
}

qdf_export_symbol(qdf_print_is_category_enabled);

bool qdf_print_is_verbose_enabled(unsigned int idx, QDF_MODULE_ID category,
				  QDF_TRACE_LEVEL verbose)
{
	bool verbose_enabled = false;

	/* Check if index passed is valid */
	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_info("%s: Invalid index - %d\n", __func__, idx);
		return verbose_enabled;
	}

	/* Check if print control object is in use */
	if (!print_ctrl_obj[idx].in_use) {
		pr_info("%s: Invalid print control object\n", __func__);
		return verbose_enabled;
	}

	/* Check if category passed is valid */
	if (category < 0 || category >= MAX_SUPPORTED_CATEGORY) {
		pr_info("%s: Invalid category: %d\n", __func__, category);
		return verbose_enabled;
	}

	if ((verbose == QDF_TRACE_LEVEL_NONE) ||
	    (verbose >= QDF_TRACE_LEVEL_MAX)) {
		verbose_enabled = false;
	} else if (verbose == QDF_TRACE_LEVEL_ALL) {
		if (print_ctrl_obj[idx].cat_info[category]
					.category_verbose_mask == 0xFFFF)
			verbose_enabled = true;
	} else {
		verbose_enabled =
		(print_ctrl_obj[idx].cat_info[category].category_verbose_mask &
		 QDF_TRACE_LEVEL_TO_MODULE_BITMASK(verbose)) ? true : false;
	}

	return verbose_enabled;
}
qdf_export_symbol(qdf_print_is_verbose_enabled);

#ifdef DBG_LVL_MAC_FILTERING

QDF_STATUS qdf_print_set_node_flag(unsigned int idx, uint8_t enable)
{
	/* Check if index passed is valid */
	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_info("%s: Invalid index - %d\n", __func__, idx);
		return QDF_STATUS_E_FAILURE;
	}

	/* Check if print control object is in use */
	if (!print_ctrl_obj[idx].in_use) {
		pr_info("%s: Invalid print control object\n", __func__);
		return QDF_STATUS_E_FAILURE;
	}

	if (enable > 1) {
		pr_info("%s: Incorrect input: Use 1 or 0 to enable or disable\n",
			__func__);
		return QDF_STATUS_E_FAILURE;
	}

	print_ctrl_obj[idx].dbglvlmac_on = enable;
	pr_info("%s: DbgLVLmac feature %s\n",
		__func__,
		((enable) ? "enabled" : "disabled"));

	return QDF_STATUS_SUCCESS;
}
qdf_export_symbol(qdf_print_set_node_flag);

bool qdf_print_get_node_flag(unsigned int idx)
{
	bool node_flag = false;

	/* Check if index passed is valid */
	if (idx < 0 || idx >= MAX_PRINT_CONFIG_SUPPORTED) {
		pr_info("%s: Invalid index - %d\n", __func__, idx);
		return node_flag;
	}

	/* Check if print control object is in use */
	if (!print_ctrl_obj[idx].in_use) {
		pr_info("%s: Invalid print control object\n", __func__);
		return node_flag;
	}

	if (print_ctrl_obj[idx].dbglvlmac_on)
		node_flag = true;

	return node_flag;
}
qdf_export_symbol(qdf_print_get_node_flag);

void qdf_print_clean_node_flag(unsigned int idx)
{
	/* Disable dbglvlmac_on during cleanup */
	print_ctrl_obj[idx].dbglvlmac_on = 0;
}

#else

void qdf_print_clean_node_flag(unsigned int idx)
{
	/* No operation in case of no support for DBG_LVL_MAC_FILTERING */
	return;
}
#endif

void QDF_PRINT_INFO(unsigned int idx, QDF_MODULE_ID module,
		    QDF_TRACE_LEVEL level,
		    char *str_format, ...)
{
	va_list args;

	/* Generic wrapper API will compile qdf_vprint in order to
	 * log the message. Once QDF converged debug framework is in
	 * place, this will be changed to adapt to the framework, compiling
	 * call to converged tracing API
	 */
	va_start(args, str_format);
	qdf_vprint(str_format, args);
	va_end(args);
}
qdf_export_symbol(QDF_PRINT_INFO);

#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
void qdf_logging_init(void)
{
	wlan_logging_sock_init_svc();
	nl_srv_init(NULL, WLAN_NLINK_PROTO_FAMILY);
	wlan_logging_notifier_init(qdf_log_dump_at_kernel_enable);
	wlan_logging_set_flush_timer(qdf_log_flush_timer_period);
}

void qdf_logging_exit(void)
{
	wlan_logging_notifier_deinit(qdf_log_dump_at_kernel_enable);
	nl_srv_exit();
	wlan_logging_sock_deinit_svc();
}

int qdf_logging_set_flush_timer(uint32_t milliseconds)
{
	if (wlan_logging_set_flush_timer(milliseconds) == 0)
		return QDF_STATUS_SUCCESS;
	else
		return QDF_STATUS_E_FAILURE;
}

void qdf_logging_flush_logs(void)
{
	wlan_flush_host_logs_for_fatal();
}

#else
void qdf_logging_init(void)
{
	nl_srv_init(NULL, WLAN_NLINK_PROTO_FAMILY);
}

void qdf_logging_exit(void)
{
	nl_srv_exit();
}

int qdf_logging_set_flush_timer(uint32_t milliseconds)
{
	return QDF_STATUS_E_FAILURE;
}

void qdf_logging_flush_logs(void)
{
}
#endif

qdf_export_symbol(qdf_logging_set_flush_timer);
qdf_export_symbol(qdf_logging_flush_logs);

#ifdef CONFIG_KALLSYMS
inline int qdf_sprint_symbol(char *buffer, void *addr)
{
	return sprint_symbol(buffer, (unsigned long)addr);
}
#else
int qdf_sprint_symbol(char *buffer, void *addr)
{
	if (!buffer)
		return 0;

	buffer[0] = '\0';
	return 1;
}
#endif
qdf_export_symbol(qdf_sprint_symbol);

void qdf_set_pidx(int pidx)
{
	qdf_pidx = pidx;
}
qdf_export_symbol(qdf_set_pidx);

int qdf_get_pidx(void)
{
	return qdf_pidx;
}
qdf_export_symbol(qdf_get_pidx);

#ifdef PANIC_ON_BUG
#ifdef CONFIG_SLUB_DEBUG
void __qdf_bug(void)
{
	BUG();
}
qdf_export_symbol(__qdf_bug);
#endif /* CONFIG_SLUB_DEBUG */
#endif /* PANIC_ON_BUG */

#ifdef WLAN_QCOM_VA_MINIDUMP
static bool qdf_va_md_initialized;
static qdf_list_t qdf_va_md_list;
static qdf_spinlock_t qdf_va_md_list_lock;
#define QDF_MINIDUMP_LIST_SIZE 128

struct qdf_va_md_entry {
	qdf_list_node_t node;
	struct va_md_entry data;
};

static int qdf_va_md_notif_handler(struct notifier_block *this,
				   unsigned long event, void *ptr)
{
	struct qdf_va_md_entry *entry;
	struct qdf_va_md_entry *next;

	qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
	qdf_list_for_each_del(&qdf_va_md_list, entry, next, node) {
		qcom_va_md_add_region(&entry->data);
	}

	qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
	return NOTIFY_OK;
}

static struct notifier_block qdf_va_md_notif_blk = {
	.notifier_call = qdf_va_md_notif_handler,
	.priority = INT_MAX,
};

void __qdf_minidump_init(void)
{
	int ret;

	if (qdf_va_md_initialized)
		return;

	qdf_spinlock_create(&qdf_va_md_list_lock);
	qdf_list_create(&qdf_va_md_list, QDF_MINIDUMP_LIST_SIZE);
	ret = qcom_va_md_register(qdf_trace_wlan_modname(),
				  &qdf_va_md_notif_blk);
	qdf_va_md_initialized = !ret;
}

qdf_export_symbol(__qdf_minidump_init);

void __qdf_minidump_deinit(void)
{
	struct qdf_va_md_entry *entry;
	struct qdf_va_md_entry *next;

	if (!qdf_va_md_initialized)
		return;

	qdf_va_md_initialized = false;
	qcom_va_md_unregister(qdf_trace_wlan_modname(),
			      &qdf_va_md_notif_blk);
	qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
	qdf_list_for_each_del(&qdf_va_md_list, entry, next, node) {
		qdf_list_remove_node(&qdf_va_md_list, &entry->node);
		qdf_mem_free(entry);
	}

	qdf_list_destroy(&qdf_va_md_list);
	qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
	qdf_spinlock_destroy(&qdf_va_md_list_lock);
}

qdf_export_symbol(__qdf_minidump_deinit);

void __qdf_minidump_log(void *start_addr, size_t size, const char *name)
{
	struct qdf_va_md_entry *entry;
	QDF_STATUS status;

	if (!qdf_va_md_initialized)
		return;

	entry = qdf_mem_malloc(sizeof(*entry));
	if (!entry) {
		qdf_err("malloc failed for %s: %pK, %zu",
			name, start_addr, size);
		return;
	}

	qdf_str_lcopy(entry->data.owner, name, sizeof(entry->data.owner));
	entry->data.vaddr = (unsigned long)start_addr;
	entry->data.size = size;

	qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
	status = qdf_list_insert_front(&qdf_va_md_list, &entry->node);
	qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
	if (QDF_IS_STATUS_ERROR(status)) {
		qdf_err("Failed to insert qdf va md entry, status %d", status);
		qdf_mem_free(entry);
	}
}

qdf_export_symbol(__qdf_minidump_log);

void __qdf_minidump_remove(void *addr, size_t size, const char *name)
{
	struct qdf_va_md_entry *entry;
	struct qdf_va_md_entry *next;

	if (!qdf_va_md_initialized)
		return;

	qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
	qdf_list_for_each_del(&qdf_va_md_list, entry, next, node) {
		if (entry->data.vaddr == (unsigned long)addr &&
		    entry->data.size == size &&
		    !qdf_str_cmp(entry->data.owner, name)) {
			qdf_list_remove_node(&qdf_va_md_list, &entry->node);
			qdf_mem_free(entry);
			break;
		}
	}

	qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
}

qdf_export_symbol(__qdf_minidump_remove);
#endif