| /* |
| * 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(<race_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(<race_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(<race_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(<race_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(<race_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(<race_lock); |
| if (MAX_CDF_TRACE_RECORDS == i) { |
| i = 0; |
| pRecord = g_cdf_trace_tbl[0]; |
| } else { |
| pRecord = g_cdf_trace_tbl[i]; |
| } |
| spin_unlock(<race_lock); |
| } |
| } else { |
| spin_unlock(<race_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); |
| } |
| } |