blob: 3d59c53d272f7e2f7213518bf89988d2994adf3c [file] [log] [blame]
/*
* Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**
* DOC: cdf_trace
*
* Connectivity driver framework (CDF) trace APIs
*
* Trace, logging, and debugging definitions and APIs
*
*/
/* Include Files */
#include <cdf_trace.h>
#include <ani_global.h>
#include <wlan_logging_sock_svc.h>
#include "cdf_time.h"
/* Preprocessor definitions and constants */
#define CDF_TRACE_BUFFER_SIZE (512)
/* macro to map cdf trace levels into the bitmask */
#define CDF_TRACE_LEVEL_TO_MODULE_BITMASK(_level) ((1 << (_level)))
typedef struct {
/* Trace level for a module, as a bitmask. The bits in this mask
* are ordered by CDF_TRACE_LEVEL. For example, each bit represents
* one of the bits in CDF_TRACE_LEVEL that may be turned on to have
* traces at that level logged, i.e. if CDF_TRACE_LEVEL_ERROR is
* == 2, then if bit 2 (low order) is turned ON, then ERROR traces
* will be printed to the trace log.
* Note that all bits turned OFF means no traces
*/
uint16_t moduleTraceLevel;
/* 3 character string name for the module */
unsigned char moduleNameStr[4]; /* 3 chars plus the NULL */
} moduleTraceInfo;
#define CDF_DEFAULT_TRACE_LEVEL \
((1 << CDF_TRACE_LEVEL_FATAL) | (1 << CDF_TRACE_LEVEL_ERROR))
/* Array of static data that contains all of the per module trace
* information. This includes the trace level for the module and
* the 3 character 'name' of the module for marking the trace logs
*/
moduleTraceInfo g_cdf_trace_info[CDF_MODULE_ID_MAX] = {
[CDF_MODULE_ID_TLSHIM] = {CDF_DEFAULT_TRACE_LEVEL, "DP"},
[CDF_MODULE_ID_WMI] = {CDF_DEFAULT_TRACE_LEVEL, "WMI"},
[CDF_MODULE_ID_HDD] = {CDF_DEFAULT_TRACE_LEVEL, "HDD"},
[CDF_MODULE_ID_SME] = {CDF_DEFAULT_TRACE_LEVEL, "SME"},
[CDF_MODULE_ID_PE] = {CDF_DEFAULT_TRACE_LEVEL, "PE "},
[CDF_MODULE_ID_WMA] = {CDF_DEFAULT_TRACE_LEVEL, "WMA"},
[CDF_MODULE_ID_SYS] = {CDF_DEFAULT_TRACE_LEVEL, "SYS"},
[CDF_MODULE_ID_CDF] = {CDF_DEFAULT_TRACE_LEVEL, "CDF"},
[CDF_MODULE_ID_SAP] = {CDF_DEFAULT_TRACE_LEVEL, "SAP"},
[CDF_MODULE_ID_HDD_SOFTAP] = {CDF_DEFAULT_TRACE_LEVEL, "HSP"},
[CDF_MODULE_ID_HDD_DATA] = {CDF_DEFAULT_TRACE_LEVEL, "HDP"},
[CDF_MODULE_ID_HDD_SAP_DATA] = {CDF_DEFAULT_TRACE_LEVEL, "SDP"},
[CDF_MODULE_ID_BMI] = {CDF_DEFAULT_TRACE_LEVEL, "BMI"},
[CDF_MODULE_ID_HIF] = {CDF_DEFAULT_TRACE_LEVEL, "HIF"},
[CDF_MODULE_ID_TXRX] = {CDF_DEFAULT_TRACE_LEVEL, "TRX"},
[CDF_MODULE_ID_HTT] = {CDF_DEFAULT_TRACE_LEVEL, "HTT"},
};
/* Static and Global variables */
static spinlock_t ltrace_lock;
static cdf_trace_record_t g_cdf_trace_tbl[MAX_CDF_TRACE_RECORDS];
/* global cdf trace data */
static t_cdf_trace_data g_cdf_trace_data;
/*
* all the call back functions for dumping MTRACE messages from ring buffer
* are stored in cdf_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
* cdf_trace_restore_cb_table. Incase if we make modifications to
* cdf_trace_cb_table, we can certainly retrieve all the call back functions
* back from Restore Table
*/
static tp_cdf_trace_cb cdf_trace_cb_table[CDF_MODULE_ID_MAX];
static tp_cdf_trace_cb cdf_trace_restore_cb_table[CDF_MODULE_ID_MAX];
/* Static and Global variables */
static spinlock_t l_dp_trace_lock;
static struct cdf_dp_trace_record_s
g_cdf_dp_trace_tbl[MAX_CDF_DP_TRACE_RECORDS];
/*
* all the options to configure/control DP trace are
* defined in this structure
*/
static struct s_cdf_dp_trace_data g_cdf_dp_trace_data;
/*
* all the call back functions for dumping DPTRACE messages from ring buffer
* are stored in cdf_dp_trace_cb_table, callbacks are initialized during init
*/
static tp_cdf_dp_trace_cb cdf_dp_trace_cb_table[CDF_DP_TRACE_MAX];
/**
* cdf_trace_set_level() - Set the trace level for a particular module
* @level : trace level
*
* Trace level is a member of the CDF_TRACE_LEVEL enumeration indicating
* the severity of the condition causing the trace message to be issued.
* More severe conditions are more likely to be logged.
*
* This is an external API that allows trace levels to be set for each module.
*
* Return: nothing
*/
void cdf_trace_set_level(CDF_MODULE_ID module, CDF_TRACE_LEVEL level)
{
/* make sure the caller is passing in a valid LEVEL */
if (level >= CDF_TRACE_LEVEL_MAX) {
pr_err("%s: Invalid trace level %d passed in!\n", __func__,
level);
return;
}
/* Treat 'none' differently. NONE means we have to run off all
* the bits in the bit mask so none of the traces appear. Anything
* other than 'none' means we need to turn ON a bit in the bitmask
*/
if (CDF_TRACE_LEVEL_NONE == level)
g_cdf_trace_info[module].moduleTraceLevel =
CDF_TRACE_LEVEL_NONE;
else
/* set the desired bit in the bit mask for the module trace
* level */
g_cdf_trace_info[module].moduleTraceLevel |=
CDF_TRACE_LEVEL_TO_MODULE_BITMASK(level);
}
/**
* cdf_trace_set_module_trace_level() - Set module trace level
* @module: Module id
* @level: Trace level for a module, as a bitmask as per 'moduleTraceInfo'
*
* Sets the module trace level where the trace level is given as a bit mask
*
* Return: None
*/
void cdf_trace_set_module_trace_level(CDF_MODULE_ID module, uint32_t level)
{
if (module < 0 || module >= CDF_MODULE_ID_MAX) {
pr_err("%s: Invalid module id %d passed\n", __func__, module);
return;
}
g_cdf_trace_info[module].moduleTraceLevel = level;
}
void cdf_trace_set_value(CDF_MODULE_ID module, CDF_TRACE_LEVEL level,
uint8_t on)
{
/* make sure the caller is passing in a valid LEVEL */
if (level < 0 || level >= CDF_TRACE_LEVEL_MAX) {
pr_err("%s: Invalid trace level %d passed in!\n", __func__,
level);
return;
}
/* make sure the caller is passing in a valid module */
if (module < 0 || module >= CDF_MODULE_ID_MAX) {
pr_err("%s: Invalid module id %d passed in!\n", __func__,
module);
return;
}
/* Treat 'none' differently. NONE means we have to turn off all
the bits in the bit mask so none of the traces appear */
if (CDF_TRACE_LEVEL_NONE == level) {
g_cdf_trace_info[module].moduleTraceLevel =
CDF_TRACE_LEVEL_NONE;
}
/* Treat 'All' differently. All means we have to turn on all
the bits in the bit mask so all of the traces appear */
else if (CDF_TRACE_LEVEL_ALL == level) {
g_cdf_trace_info[module].moduleTraceLevel = 0xFFFF;
} else {
if (on)
/* set the desired bit in the bit mask for the module
trace level */
g_cdf_trace_info[module].moduleTraceLevel |=
CDF_TRACE_LEVEL_TO_MODULE_BITMASK(level);
else
/* clear the desired bit in the bit mask for the module
trace level */
g_cdf_trace_info[module].moduleTraceLevel &=
~(CDF_TRACE_LEVEL_TO_MODULE_BITMASK(level));
}
}
/**
* cdf_trace_get_level() - get the trace level
* @level : trace level
*
* This is an external API that returns a bool value to signify if a
* particular trace level is set for the specified module.
* A member of the CDF_TRACE_LEVEL enumeration indicating the severity
* of the condition causing the trace message to be issued.
*
* Note that individual trace levels are the only valid values
* for this API. CDF_TRACE_LEVEL_NONE and CDF_TRACE_LEVEL_ALL
* are not valid input and will return false
*
* Return:
* false - the specified trace level for the specified module is OFF
* true - the specified trace level for the specified module is ON
*/
bool cdf_trace_get_level(CDF_MODULE_ID module, CDF_TRACE_LEVEL level)
{
bool traceOn = false;
if ((CDF_TRACE_LEVEL_NONE == level) ||
(CDF_TRACE_LEVEL_ALL == level) || (level >= CDF_TRACE_LEVEL_MAX)) {
traceOn = false;
} else {
traceOn = (level & g_cdf_trace_info[module].moduleTraceLevel)
? true : false;
}
return traceOn;
}
void cdf_snprintf(char *strBuffer, unsigned int size, char *strFormat, ...)
{
va_list val;
va_start(val, strFormat);
snprintf(strBuffer, size, strFormat, val);
va_end(val);
}
#ifdef CDF_ENABLE_TRACING
/**
* cdf_trace_msg() - externally called trace function
* @module : Module identifier a member of the CDF_MODULE_ID
* enumeration that identifies the module issuing the trace message.
* @level : Trace level a member of the CDF_TRACE_LEVEL enumeration
* indicating the severity of the condition causing the trace message
* to be issued. More severe conditions are more likely to be logged.
* @strFormat : Format string in which the message to be logged. This format
* string contains printf-like replacement parameters, which follow
* this parameter in the variable argument list.
*
* Checks the level of severity and accordingly prints the trace messages
*
* Return: nothing
*
*/
void cdf_trace_msg(CDF_MODULE_ID module, CDF_TRACE_LEVEL level,
char *strFormat, ...)
{
char strBuffer[CDF_TRACE_BUFFER_SIZE];
int n;
/* Print the trace message when the desired level bit is set in
the module tracel level mask */
if (g_cdf_trace_info[module].moduleTraceLevel &
CDF_TRACE_LEVEL_TO_MODULE_BITMASK(level)) {
/* the trace level strings in an array. these are ordered in
* the same order as the trace levels are defined in the enum
* (see CDF_TRACE_LEVEL) so we can index into this array with
* the level and get the right string. The cdf trace levels
* are... none, Fatal, Error, Warning, Info, InfoHigh, InfoMed,
* InfoLow, Debug
*/
static const char *TRACE_LEVEL_STR[] = { " ", "F ", "E ", "W ",
"I ", "IH", "IM", "IL", "D" };
va_list val;
va_start(val, strFormat);
/* print the prefix string into the string buffer... */
n = snprintf(strBuffer, CDF_TRACE_BUFFER_SIZE,
"wlan: [%d:%2s:%3s] ",
in_interrupt() ? 0 : current->pid,
(char *)TRACE_LEVEL_STR[level],
(char *)g_cdf_trace_info[module].moduleNameStr);
/* print the formatted log message after the prefix string */
if ((n >= 0) && (n < CDF_TRACE_BUFFER_SIZE)) {
vsnprintf(strBuffer + n, CDF_TRACE_BUFFER_SIZE - n,
strFormat, val);
#if defined(WLAN_LOGGING_SOCK_SVC_ENABLE)
wlan_log_to_user(level, (char *)strBuffer,
strlen(strBuffer));
#else
pr_err("%s\n", strBuffer);
#endif
}
va_end(val);
}
}
void cdf_trace_display(void)
{
CDF_MODULE_ID moduleId;
pr_err
(" 1)FATAL 2)ERROR 3)WARN 4)INFO 5)INFO_H 6)INFO_M 7)INFO_L 8)DEBUG\n");
for (moduleId = 0; moduleId < CDF_MODULE_ID_MAX; ++moduleId) {
pr_err
("%2d)%s %s %s %s %s %s %s %s %s\n",
(int)moduleId, g_cdf_trace_info[moduleId].moduleNameStr,
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_FATAL)) ? "X" :
" ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_ERROR)) ? "X" :
" ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_WARN)) ? "X" :
" ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_INFO)) ? "X" :
" ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_INFO_HIGH)) ? "X"
: " ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_INFO_MED)) ? "X"
: " ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_INFO_LOW)) ? "X"
: " ",
(g_cdf_trace_info[moduleId].
moduleTraceLevel & (1 << CDF_TRACE_LEVEL_DEBUG)) ? "X" :
" ");
}
}
/**
* cdf_trace_hex_dump() - externally called hex dump function
* @module : Module identifier a member of the CDF_MODULE_ID enumeration that
* identifies the module issuing the trace message.
* @level : Trace level a member of the CDF_TRACE_LEVEL enumeration indicating
* the severity of the condition causing the trace message to be
* issued. More severe conditions are more likely to be logged.
* @data : The base address of the buffer to be logged.
* @buf_len : The size of the buffer to be logged.
*
* Checks the level of severity and accordingly prints the trace messages
*
* Return : nothing
*/
void cdf_trace_hex_dump(CDF_MODULE_ID module, CDF_TRACE_LEVEL level,
void *data, int buf_len)
{
char *buf = (char *)data;
int i;
if (!(g_cdf_trace_info[module].moduleTraceLevel &
CDF_TRACE_LEVEL_TO_MODULE_BITMASK(level)))
return;
for (i = 0; (i + 15) < buf_len; i += 16) {
cdf_trace_msg(module, level,
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
buf[i],
buf[i + 1],
buf[i + 2],
buf[i + 3],
buf[i + 4],
buf[i + 5],
buf[i + 6],
buf[i + 7],
buf[i + 8],
buf[i + 9],
buf[i + 10],
buf[i + 11],
buf[i + 12],
buf[i + 13], buf[i + 14], buf[i + 15]);
}
/* Dump the bytes in the last line */
for (; i < buf_len; i++)
cdf_trace_msg(module, level, "%02x ", buf[i]);
}
#endif
/**
* cdf_trace_enable() - Enable MTRACE for specific modules
* @bitmask_of_moduleId : Bitmask according to enum of the modules.
* 32 [dec] = 0010 0000 [bin] <enum of HDD is 5>
* 64 [dec] = 0100 0000 [bin] <enum of SME is 6>
* 128 [dec] = 1000 0000 [bin] <enum of PE is 7>
* @enable : can be true or false true implies enabling MTRACE false implies
* disabling MTRACE.
*
* Enable MTRACE for specific modules whose bits are set in bitmask and enable
* is true. if enable is false it disables MTRACE for that module. set the
* bitmask according to enum value of the modules.
* This functions will be called when you issue ioctl as mentioned following
* [iwpriv wlan0 setdumplog <value> <enable>].
* <value> - Decimal number, i.e. 64 decimal value shows only SME module,
* 128 decimal value shows only PE module, 192 decimal value shows PE and SME.
*
*
* Return : nothing
*/
void cdf_trace_enable(uint32_t bitmask_of_moduleId, uint8_t enable)
{
int i;
if (bitmask_of_moduleId) {
for (i = 0; i < CDF_MODULE_ID_MAX; i++) {
if (((bitmask_of_moduleId >> i) & 1)) {
if (enable) {
if (NULL !=
cdf_trace_restore_cb_table[i]) {
cdf_trace_cb_table[i] =
cdf_trace_restore_cb_table[i];
}
} else {
cdf_trace_restore_cb_table[i] =
cdf_trace_cb_table[i];
cdf_trace_cb_table[i] = NULL;
}
}
}
} else {
if (enable) {
for (i = 0; i < CDF_MODULE_ID_MAX; i++) {
if (NULL != cdf_trace_restore_cb_table[i]) {
cdf_trace_cb_table[i] =
cdf_trace_restore_cb_table[i];
}
}
} else {
for (i = 0; i < CDF_MODULE_ID_MAX; i++) {
cdf_trace_restore_cb_table[i] =
cdf_trace_cb_table[i];
cdf_trace_cb_table[i] = NULL;
}
}
}
}
/**
* cdf_trace_init() - initializes cdf trace structures and variables
*
* Called immediately after cds_preopen, so that we can start recording HDD
* events ASAP.
*
* Return : nothing
*/
void cdf_trace_init(void)
{
uint8_t i;
g_cdf_trace_data.head = INVALID_CDF_TRACE_ADDR;
g_cdf_trace_data.tail = INVALID_CDF_TRACE_ADDR;
g_cdf_trace_data.num = 0;
g_cdf_trace_data.enable = true;
g_cdf_trace_data.dumpCount = DEFAULT_CDF_TRACE_DUMP_COUNT;
g_cdf_trace_data.numSinceLastDump = 0;
for (i = 0; i < CDF_MODULE_ID_MAX; i++) {
cdf_trace_cb_table[i] = NULL;
cdf_trace_restore_cb_table[i] = NULL;
}
}
/**
* cdf_trace() - puts the messages in to ring-buffer
* @module : Enum of module, basically module id.
* @param : Code to be recorded
* @session : Session ID of the log
* @data : Actual message contents
*
* This function will be called from each module who wants record the messages
* in circular queue. Before calling this functions make sure you have
* registered your module with cdf through cdf_trace_register function.
*
*
* Return : nothing
*/
void cdf_trace(uint8_t module, uint8_t code, uint16_t session, uint32_t data)
{
tp_cdf_trace_record rec = NULL;
unsigned long flags;
if (!g_cdf_trace_data.enable)
return;
/* if module is not registered, don't record for that module */
if (NULL == cdf_trace_cb_table[module])
return;
/* Aquire the lock so that only one thread at a time can fill the ring
* buffer
*/
spin_lock_irqsave(&ltrace_lock, flags);
g_cdf_trace_data.num++;
if (g_cdf_trace_data.num > MAX_CDF_TRACE_RECORDS)
g_cdf_trace_data.num = MAX_CDF_TRACE_RECORDS;
if (INVALID_CDF_TRACE_ADDR == g_cdf_trace_data.head) {
/* first record */
g_cdf_trace_data.head = 0;
g_cdf_trace_data.tail = 0;
} else {
/* queue is not empty */
uint32_t tail = g_cdf_trace_data.tail + 1;
if (MAX_CDF_TRACE_RECORDS == tail)
tail = 0;
if (g_cdf_trace_data.head == tail) {
/* full */
if (MAX_CDF_TRACE_RECORDS == ++g_cdf_trace_data.head)
g_cdf_trace_data.head = 0;
}
g_cdf_trace_data.tail = tail;
}
rec = &g_cdf_trace_tbl[g_cdf_trace_data.tail];
rec->code = code;
rec->session = session;
rec->data = data;
rec->time = cdf_get_log_timestamp();
rec->module = module;
rec->pid = (in_interrupt() ? 0 : current->pid);
g_cdf_trace_data.numSinceLastDump++;
spin_unlock_irqrestore(&ltrace_lock, flags);
}
/**
* cdf_trace_spin_lock_init() - initializes the lock variable before use
*
* This function will be called from cds_alloc_global_context, we will have lock
* available to use ASAP
*
* Return : nothing
*/
CDF_STATUS cdf_trace_spin_lock_init(void)
{
spin_lock_init(&ltrace_lock);
return CDF_STATUS_SUCCESS;
}
/**
* cdf_trace_register() - registers the call back functions
* @moduleID - enum value of module
* @cdf_trace_callback - call back functions to display the messages in
* particular format.
*
* Registers the call back functions to display the messages in particular
* format mentioned in these call back functions. This functions should be
* called by interested module in their init part as we will be ready to
* register as soon as modules are up.
*
* Return : nothing
*/
void cdf_trace_register(CDF_MODULE_ID moduleID,
tp_cdf_trace_cb cdf_trace_callback)
{
cdf_trace_cb_table[moduleID] = cdf_trace_callback;
}
/**
* cdf_trace_dump_all() - Dump data from ring buffer via call back functions
* registered with CDF
* @pMac : Context of particular module
* @code : Reason code
* @session : Session id of log
* @count : Number of lines to dump starting from tail to head
*
* This function will be called up on issueing ioctl call as mentioned following
* [iwpriv wlan0 dumplog 0 0 <n> <bitmask_of_module>]
*
* <n> - number lines to dump starting from tail to head.
*
* <bitmask_of_module> - if anybody wants to know how many messages were
* recorded for particular module/s mentioned by setbit in bitmask from last
* <n> messages. It is optional, if you don't provide then it will dump
* everything from buffer.
*
* Return : nothing
*/
void cdf_trace_dump_all(void *pMac, uint8_t code, uint8_t session,
uint32_t count, uint32_t bitmask_of_module)
{
cdf_trace_record_t pRecord;
int32_t i, tail;
if (!g_cdf_trace_data.enable) {
CDF_TRACE(CDF_MODULE_ID_SYS,
CDF_TRACE_LEVEL_ERROR, "Tracing Disabled");
return;
}
CDF_TRACE(CDF_MODULE_ID_SYS, CDF_TRACE_LEVEL_ERROR,
"Total Records: %d, Head: %d, Tail: %d",
g_cdf_trace_data.num, g_cdf_trace_data.head,
g_cdf_trace_data.tail);
/* aquire the lock so that only one thread at a time can read
* the ring buffer
*/
spin_lock(&ltrace_lock);
if (g_cdf_trace_data.head != INVALID_CDF_TRACE_ADDR) {
i = g_cdf_trace_data.head;
tail = g_cdf_trace_data.tail;
if (count) {
if (count > g_cdf_trace_data.num)
count = g_cdf_trace_data.num;
if (tail >= (count - 1))
i = tail - count + 1;
else if (count != MAX_CDF_TRACE_RECORDS)
i = MAX_CDF_TRACE_RECORDS - ((count - 1) -
tail);
}
pRecord = g_cdf_trace_tbl[i];
/* right now we are not using numSinceLastDump 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_cdf_trace_data.numSinceLastDump = 0;
spin_unlock(&ltrace_lock);
for (;; ) {
if ((code == 0 || (code == pRecord.code)) &&
(cdf_trace_cb_table[pRecord.module] != NULL)) {
if (0 == bitmask_of_module) {
cdf_trace_cb_table[pRecord.
module] (pMac,
&pRecord,
(uint16_t)
i);
} else {
if (bitmask_of_module &
(1 << pRecord.module)) {
cdf_trace_cb_table[pRecord.
module]
(pMac, &pRecord,
(uint16_t) i);
}
}
}
if (i == tail)
break;
i += 1;
spin_lock(&ltrace_lock);
if (MAX_CDF_TRACE_RECORDS == i) {
i = 0;
pRecord = g_cdf_trace_tbl[0];
} else {
pRecord = g_cdf_trace_tbl[i];
}
spin_unlock(&ltrace_lock);
}
} else {
spin_unlock(&ltrace_lock);
}
}
/**
* cdf_dp_trace_init() - enables the DP trace
* Called during driver load and it enables DP trace
*
* Return: None
*/
void cdf_dp_trace_init(void)
{
uint8_t i;
cdf_dp_trace_spin_lock_init();
g_cdf_dp_trace_data.head = INVALID_CDF_DP_TRACE_ADDR;
g_cdf_dp_trace_data.tail = INVALID_CDF_DP_TRACE_ADDR;
g_cdf_dp_trace_data.num = 0;
g_cdf_dp_trace_data.proto_bitmap = 0;
g_cdf_dp_trace_data.no_of_record = 0;
g_cdf_dp_trace_data.verbosity = CDF_DP_TRACE_VERBOSITY_DEFAULT;
g_cdf_dp_trace_data.enable = true;
for (i = 0; i < CDF_DP_TRACE_MAX; i++)
cdf_dp_trace_cb_table[i] = cdf_dp_display_record;
}
/**
* cdf_dp_trace_set_value() - Configure the value to control DP trace
* @proto_bitmap : defines the protocol to be tracked
* @no_of_records : defines the nth packet which is traced
* @verbosity : defines the verbosity level
*
* Return: None
*/
void cdf_dp_trace_set_value(uint8_t proto_bitmap, uint8_t no_of_record,
uint8_t verbosity)
{
g_cdf_dp_trace_data.proto_bitmap = proto_bitmap;
g_cdf_dp_trace_data.no_of_record = no_of_record;
g_cdf_dp_trace_data.verbosity = verbosity;
return;
}
/**
* cdf_dp_trace_enable_track() - enable the tracing for netbuf
* @code : defines the event
*
* Return: true or false depends on whether tracing enabled
*/
static bool cdf_dp_trace_enable_track(enum CDF_DP_TRACE_ID code)
{
if (g_cdf_dp_trace_data.verbosity == CDF_DP_TRACE_VERBOSITY_HIGH)
return true;
if (g_cdf_dp_trace_data.verbosity == CDF_DP_TRACE_VERBOSITY_MEDIUM
&& (code <= CDF_DP_TRACE_HIF_PACKET_PTR_RECORD))
return true;
if (g_cdf_dp_trace_data.verbosity == CDF_DP_TRACE_VERBOSITY_LOW
&& (code <= CDF_DP_TRACE_CE_PACKET_RECORD))
return true;
if (g_cdf_dp_trace_data.verbosity == CDF_DP_TRACE_VERBOSITY_DEFAULT
&& (code == CDF_DP_TRACE_DROP_PACKET_RECORD))
return true;
return false;
}
/**
* cdf_dp_trace_set_track() - Marks whether the packet needs to be traced
* @nbuf : defines the netbuf
*
* Return: None
*/
void cdf_dp_trace_set_track(cdf_nbuf_t nbuf)
{
spin_lock_bh(&l_dp_trace_lock);
g_cdf_dp_trace_data.count++;
if (g_cdf_dp_trace_data.proto_bitmap != 0) {
if (cds_pkt_get_proto_type(nbuf,
g_cdf_dp_trace_data.proto_bitmap, 0)) {
CDF_NBUF_SET_DP_TRACE(nbuf, 1);
}
}
if ((g_cdf_dp_trace_data.no_of_record != 0) &&
(g_cdf_dp_trace_data.count %
g_cdf_dp_trace_data.no_of_record == 0)) {
CDF_NBUF_SET_DP_TRACE(nbuf, 1);
}
spin_unlock_bh(&l_dp_trace_lock);
return;
}
/**
* dump_hex_trace() - Display the data in buffer
* @buf: buffer which contains data to be displayed
* @buf_len: defines the size of the data to be displayed
*
* Return: None
*/
static void dump_hex_trace(uint8_t *buf, uint8_t buf_len)
{
uint8_t i = 0;
/* Dump the bytes in the last line */
cdf_print("DATA: ");
for (i = 0; i < buf_len; i++)
cdf_print("%02x ", buf[i]);
cdf_print("\n");
}
/**
* cdf_dp_display_trace() - Displays a record in DP trace
* @pRecord : pointer to a record in DP trace
* @recIndex : record index
*
* Return: None
*/
void cdf_dp_display_record(struct cdf_dp_trace_record_s *pRecord ,
uint16_t recIndex)
{
cdf_print("INDEX: %04d TIME: %012llu CODE: %02d\n", recIndex,
pRecord->time, pRecord->code);
switch (pRecord->code) {
case CDF_DP_TRACE_HDD_TX_TIMEOUT:
CDF_TRACE(CDF_MODULE_ID_CDF, CDF_TRACE_LEVEL_ERROR,
"HDD TX Timeout\n");
break;
case CDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
CDF_TRACE(CDF_MODULE_ID_CDF, CDF_TRACE_LEVEL_ERROR,
"HDD SoftAP TX Timeout\n");
break;
case CDF_DP_TRACE_VDEV_PAUSE:
CDF_TRACE(CDF_MODULE_ID_CDF, CDF_TRACE_LEVEL_ERROR,
"VDEV Pause\n");
break;
case CDF_DP_TRACE_VDEV_UNPAUSE:
CDF_TRACE(CDF_MODULE_ID_CDF, CDF_TRACE_LEVEL_ERROR,
"VDEV UnPause\n");
break;
default:
dump_hex_trace(pRecord->data, pRecord->size);
}
return;
}
/**
* cdf_dp_trace() - Stores the data in buffer
* @nbuf : defines the netbuf
* @code : defines the event
* @data : defines the data to be stored
* @size : defines the size of the data record
*
* Return: None
*/
void cdf_dp_trace(cdf_nbuf_t nbuf, enum CDF_DP_TRACE_ID code,
uint8_t *data, uint8_t size)
{
struct cdf_dp_trace_record_s *rec = NULL;
/* Return when Dp trace is not enabled */
if (!g_cdf_dp_trace_data.enable)
return;
/* If nbuf is NULL, check for VDEV PAUSE, UNPAUSE, TIMEOUT */
if (!nbuf) {
switch (code) {
case CDF_DP_TRACE_HDD_TX_TIMEOUT:
case CDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
case CDF_DP_TRACE_VDEV_PAUSE:
case CDF_DP_TRACE_VDEV_UNPAUSE:
if (cdf_dp_trace_enable_track(code))
goto register_record;
else
return;
default:
return;
}
}
/* Return when the packet is not a data packet */
if (NBUF_GET_PACKET_TRACK(nbuf) != NBUF_TX_PKT_DATA_TRACK)
return;
/* Return when nbuf is not marked for dp tracing or
* verbosity does not allow
*/
if (cdf_dp_trace_enable_track(code) == false ||
!CDF_NBUF_GET_DP_TRACE(nbuf))
return;
/* Acquire the lock so that only one thread at a time can fill the ring
* buffer
*/
register_record:
spin_lock_bh(&l_dp_trace_lock);
g_cdf_dp_trace_data.num++;
if (g_cdf_dp_trace_data.num > MAX_CDF_DP_TRACE_RECORDS)
g_cdf_dp_trace_data.num = MAX_CDF_DP_TRACE_RECORDS;
if (INVALID_CDF_DP_TRACE_ADDR == g_cdf_dp_trace_data.head) {
/* first record */
g_cdf_dp_trace_data.head = 0;
g_cdf_dp_trace_data.tail = 0;
} else {
/* queue is not empty */
g_cdf_dp_trace_data.tail++;
if (MAX_CDF_DP_TRACE_RECORDS == g_cdf_dp_trace_data.tail)
g_cdf_dp_trace_data.tail = 0;
if (g_cdf_dp_trace_data.head == g_cdf_dp_trace_data.tail) {
/* full */
if (MAX_CDF_DP_TRACE_RECORDS ==
++g_cdf_dp_trace_data.head)
g_cdf_dp_trace_data.head = 0;
}
}
rec = &g_cdf_dp_trace_tbl[g_cdf_dp_trace_data.tail];
rec->code = code;
rec->size = 0;
if (data != NULL && size > 0) {
if (size > CDF_DP_TRACE_RECORD_SIZE)
size = CDF_DP_TRACE_RECORD_SIZE;
rec->size = size;
switch (code) {
case CDF_DP_TRACE_HDD_PACKET_PTR_RECORD:
case CDF_DP_TRACE_CE_PACKET_PTR_RECORD:
case CDF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD:
case CDF_DP_TRACE_TXRX_PACKET_PTR_RECORD:
case CDF_DP_TRACE_HTT_PACKET_PTR_RECORD:
case CDF_DP_TRACE_HTC_PACKET_PTR_RECORD:
case CDF_DP_TRACE_HIF_PACKET_PTR_RECORD:
cdf_mem_copy(rec->data, (uint8_t *)(&data), size);
break;
case CDF_DP_TRACE_DROP_PACKET_RECORD:
case CDF_DP_TRACE_HDD_PACKET_RECORD:
case CDF_DP_TRACE_CE_PACKET_RECORD:
cdf_mem_copy(rec->data, data, size);
break;
default:
break;
}
}
rec->time = cdf_get_log_timestamp();
rec->pid = (in_interrupt() ? 0 : current->pid);
spin_unlock_bh(&l_dp_trace_lock);
}
/**
* cdf_dp_trace_spin_lock_init() - initializes the lock variable before use
* This function will be called from cds_alloc_global_context, we will have lock
* available to use ASAP
*
* Return : nothing
*/
void cdf_dp_trace_spin_lock_init(void)
{
spin_lock_init(&l_dp_trace_lock);
return;
}
/**
* cdf_dp_trace_dump_all() - Dump data from ring buffer via call back functions
* registered with CDF
* @code : Reason code
* @count : Number of lines to dump starting from tail to head
*
* Return : nothing
*/
void cdf_dp_trace_dump_all(uint32_t count)
{
struct cdf_dp_trace_record_s pRecord;
int32_t i, tail;
if (!g_cdf_dp_trace_data.enable) {
CDF_TRACE(CDF_MODULE_ID_SYS,
CDF_TRACE_LEVEL_ERROR, "Tracing Disabled");
return;
}
CDF_TRACE(CDF_MODULE_ID_SYS, CDF_TRACE_LEVEL_ERROR,
"Total Records: %d, Head: %d, Tail: %d",
g_cdf_dp_trace_data.num, g_cdf_dp_trace_data.head,
g_cdf_dp_trace_data.tail);
/* aquire the lock so that only one thread at a time can read
* the ring buffer
*/
spin_lock_bh(&l_dp_trace_lock);
if (g_cdf_dp_trace_data.head != INVALID_CDF_DP_TRACE_ADDR) {
i = g_cdf_dp_trace_data.head;
tail = g_cdf_dp_trace_data.tail;
if (count) {
if (count > g_cdf_dp_trace_data.num)
count = g_cdf_dp_trace_data.num;
if (tail >= (count - 1))
i = tail - count + 1;
else if (count != MAX_CDF_DP_TRACE_RECORDS)
i = MAX_CDF_DP_TRACE_RECORDS - ((count - 1) -
tail);
}
pRecord = g_cdf_dp_trace_tbl[i];
spin_unlock_bh(&l_dp_trace_lock);
for (;; ) {
cdf_dp_trace_cb_table[pRecord.
code] (&pRecord, (uint16_t)i);
if (i == tail)
break;
i += 1;
spin_lock_bh(&l_dp_trace_lock);
if (MAX_CDF_DP_TRACE_RECORDS == i)
i = 0;
pRecord = g_cdf_dp_trace_tbl[i];
spin_unlock_bh(&l_dp_trace_lock);
}
} else {
spin_unlock_bh(&l_dp_trace_lock);
}
}