mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
716 lines
24 KiB
C
716 lines
24 KiB
C
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2012 - 2016 Samsung Electronics Co., Ltd. All rights reserved
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <linux/sysfs.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/cdev.h>
|
|
|
|
#include "debug.h"
|
|
#include "procfs.h"
|
|
#include "utils.h"
|
|
|
|
#ifndef CONFIG_SCSC_DEBUG_COMPATIBILITY
|
|
const int SLSI_INIT_DEINIT;
|
|
const int SLSI_NETDEV = 1;
|
|
const int SLSI_CFG80211 = 2;
|
|
const int SLSI_MLME = 3;
|
|
const int SLSI_SUMMARY_FRAMES = 4;
|
|
const int SLSI_HYDRA = 5;
|
|
const int SLSI_TX = 6;
|
|
const int SLSI_RX = 7;
|
|
const int SLSI_UDI = 8;
|
|
|
|
const int SLSI_WIFI_FCQ = 9;
|
|
|
|
const int SLSI_HIP = 10;
|
|
const int SLSI_HIP_INIT_DEINIT = 11;
|
|
const int SLSI_HIP_FW_DL = 12;
|
|
const int SLSI_HIP_SDIO_OP = 13;
|
|
const int SLSI_HIP_PS = 14;
|
|
const int SLSI_HIP_TH = 15;
|
|
const int SLSI_HIP_FH = 16;
|
|
const int SLSI_HIP_SIG = 17;
|
|
|
|
const int SLSI_FUNC_TRACE = 18;
|
|
const int SLSI_TEST = 19; /* Unit test logging */
|
|
const int SLSI_SRC_SINK = 20;
|
|
const int SLSI_FW_TEST = 21;
|
|
const int SLSI_RX_BA = 22;
|
|
|
|
const int SLSI_TDLS = 23;
|
|
const int SLSI_GSCAN = 24;
|
|
const int SLSI_MBULK = 25;
|
|
const int SLSI_FLOWC = 26;
|
|
#endif
|
|
|
|
static uint offline_dbg_level;
|
|
module_param(offline_dbg_level, uint, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(offline_dbg_level, "offline debug level 1-4 [default=0]");
|
|
|
|
static int slsi_dbg_set_param_cb(const char *val, const struct kernel_param *kp);
|
|
static int slsi_dbg_get_param_cb(char *buffer, const struct kernel_param *kp);
|
|
|
|
static struct kernel_param_ops param_ops_log = {
|
|
.set = slsi_dbg_set_param_cb,
|
|
.get = slsi_dbg_get_param_cb,
|
|
};
|
|
|
|
#define ADD_DEBUG_MODULE_PARAM(name, default_level, filter) \
|
|
int slsi_dbg_lvl_ ## name = default_level; \
|
|
module_param_cb(slsi_dbg_lvl_ ## name, ¶m_ops_log, (void *)&filter, S_IRUGO | S_IWUSR); \
|
|
MODULE_PARM_DESC(slsi_dbg_lvl_ ## name, " Debug levels (0~4) for the " # name " module (0 = off) default=" # default_level)
|
|
|
|
#ifndef CONFIG_SCSC_DEBUG_COMPATIBILITY
|
|
/* Name, Default, Filter */
|
|
ADD_DEBUG_MODULE_PARAM(init_deinit, 3, SLSI_INIT_DEINIT);
|
|
ADD_DEBUG_MODULE_PARAM(netdev, 2, SLSI_NETDEV);
|
|
ADD_DEBUG_MODULE_PARAM(cfg80211, 1, SLSI_CFG80211);
|
|
ADD_DEBUG_MODULE_PARAM(mlme, 2, SLSI_MLME);
|
|
ADD_DEBUG_MODULE_PARAM(summary_frames, 0, SLSI_SUMMARY_FRAMES);
|
|
ADD_DEBUG_MODULE_PARAM(hydra, 0, SLSI_HYDRA);
|
|
ADD_DEBUG_MODULE_PARAM(tx, 0, SLSI_TX);
|
|
ADD_DEBUG_MODULE_PARAM(rx, 0, SLSI_RX);
|
|
ADD_DEBUG_MODULE_PARAM(udi, 2, SLSI_UDI);
|
|
|
|
ADD_DEBUG_MODULE_PARAM(wifi_fcq, 0, SLSI_WIFI_FCQ);
|
|
|
|
ADD_DEBUG_MODULE_PARAM(hip, 0, SLSI_HIP);
|
|
ADD_DEBUG_MODULE_PARAM(hip_init_deinit, 0, SLSI_HIP_INIT_DEINIT);
|
|
ADD_DEBUG_MODULE_PARAM(hip_fw_dl, 0, SLSI_HIP_FW_DL);
|
|
ADD_DEBUG_MODULE_PARAM(hip_sdio_op, 0, SLSI_HIP_SDIO_OP);
|
|
ADD_DEBUG_MODULE_PARAM(hip_ps, 0, SLSI_HIP_PS);
|
|
ADD_DEBUG_MODULE_PARAM(hip_th, 0, SLSI_HIP_TH);
|
|
ADD_DEBUG_MODULE_PARAM(hip_fh, 0, SLSI_HIP_FH);
|
|
ADD_DEBUG_MODULE_PARAM(hip_sig, 0, SLSI_HIP_SIG);
|
|
|
|
ADD_DEBUG_MODULE_PARAM(func_trace, 0, SLSI_FUNC_TRACE);
|
|
ADD_DEBUG_MODULE_PARAM(test, 0, SLSI_TEST);
|
|
ADD_DEBUG_MODULE_PARAM(src_sink, 0, SLSI_SRC_SINK);
|
|
ADD_DEBUG_MODULE_PARAM(fw_test, 0, SLSI_FW_TEST);
|
|
ADD_DEBUG_MODULE_PARAM(rx_ba, 0, SLSI_RX_BA);
|
|
|
|
ADD_DEBUG_MODULE_PARAM(tdls, 2, SLSI_TDLS);
|
|
ADD_DEBUG_MODULE_PARAM(gscan, 3, SLSI_GSCAN);
|
|
ADD_DEBUG_MODULE_PARAM(mbulk, 0, SLSI_MBULK);
|
|
ADD_DEBUG_MODULE_PARAM(flowc, 0, SLSI_FLOWC);
|
|
|
|
int slsi_dbg_lvl_all; /* Override all debug modules */
|
|
|
|
int *slsi_dbg_filters[] = {
|
|
&slsi_dbg_lvl_init_deinit,
|
|
&slsi_dbg_lvl_netdev,
|
|
&slsi_dbg_lvl_cfg80211,
|
|
&slsi_dbg_lvl_mlme,
|
|
&slsi_dbg_lvl_summary_frames,
|
|
&slsi_dbg_lvl_hydra,
|
|
&slsi_dbg_lvl_tx,
|
|
&slsi_dbg_lvl_rx,
|
|
&slsi_dbg_lvl_udi,
|
|
|
|
&slsi_dbg_lvl_wifi_fcq,
|
|
|
|
&slsi_dbg_lvl_hip,
|
|
&slsi_dbg_lvl_hip_init_deinit,
|
|
&slsi_dbg_lvl_hip_fw_dl,
|
|
&slsi_dbg_lvl_hip_sdio_op,
|
|
&slsi_dbg_lvl_hip_ps,
|
|
&slsi_dbg_lvl_hip_th,
|
|
&slsi_dbg_lvl_hip_fh,
|
|
&slsi_dbg_lvl_hip_sig,
|
|
|
|
&slsi_dbg_lvl_func_trace,
|
|
&slsi_dbg_lvl_test,
|
|
&slsi_dbg_lvl_src_sink,
|
|
&slsi_dbg_lvl_fw_test,
|
|
&slsi_dbg_lvl_rx_ba,
|
|
|
|
&slsi_dbg_lvl_tdls,
|
|
&slsi_dbg_lvl_gscan,
|
|
&slsi_dbg_lvl_mbulk,
|
|
&slsi_dbg_lvl_flowc,
|
|
};
|
|
#else
|
|
int slsi_dbg_lvl_compat_all = 0;
|
|
module_param(slsi_dbg_lvl_compat_all, int, S_IRUGO | S_IWUSR);
|
|
|
|
int *slsi_dbg_filters[] = {
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
&slsi_dbg_lvl_compat_all,
|
|
};
|
|
|
|
int slsi_dbg_lvl_all; /* Override all debug modules */
|
|
#endif
|
|
|
|
const int SLSI_DF_MAX = (sizeof(slsi_dbg_filters) / sizeof(slsi_dbg_filters[0]));
|
|
|
|
const int SLSI_OVERRIDE_ALL_FILTER = -1; /* This is not a log module but merely a filter option */
|
|
|
|
/* Convert a string containing a decimal value to an integer */
|
|
static int slsi_decstr_to_int(const char *dec_str, int *res)
|
|
{
|
|
int tmp_res = 0;
|
|
int sign = 0;
|
|
const char *tmp_char = dec_str;
|
|
|
|
sign = (*tmp_char == '-') ? -1 : ((*tmp_char == '+') ? 1 : 0);
|
|
if (sign != 0)
|
|
tmp_char++;
|
|
|
|
while (*tmp_char) {
|
|
if (*tmp_char == '\n')
|
|
break;
|
|
if ((*tmp_char < '0') || (*tmp_char > '9'))
|
|
return -1;
|
|
tmp_res = tmp_res * 10 + (*tmp_char - '0');
|
|
tmp_char++;
|
|
}
|
|
|
|
*res = (sign < 0) ? (-tmp_res) : tmp_res;
|
|
return 0;
|
|
}
|
|
|
|
static int slsi_dbg_set_param_cb(const char *val, const struct kernel_param *kp)
|
|
{
|
|
int new_val;
|
|
int filter;
|
|
|
|
if (slsi_decstr_to_int(val, &new_val) < 0) {
|
|
pr_info("%s: failed to convert %s to int\n", __func__, val);
|
|
return -1;
|
|
}
|
|
filter = *((int *)(kp->arg));
|
|
|
|
if (filter < -1 || filter >= SLSI_DF_MAX) {
|
|
pr_info("%s: filter %d out of range\n", __func__, filter);
|
|
return -1;
|
|
}
|
|
|
|
if (filter == SLSI_OVERRIDE_ALL_FILTER) {
|
|
if (new_val == -1) {
|
|
pr_info("Override does not take effect because slsi_dbg_lvl_all=%d\n", new_val);
|
|
} else {
|
|
int i;
|
|
|
|
pr_info("Setting all debug modules to level %d\n", new_val);
|
|
for (i = 0; i < SLSI_DF_MAX; i++)
|
|
*slsi_dbg_filters[i] = new_val;
|
|
|
|
slsi_dbg_lvl_all = new_val;
|
|
}
|
|
} else {
|
|
pr_info("Setting debug module %d to level %d\n", filter, new_val);
|
|
*slsi_dbg_filters[filter] = new_val;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int slsi_dbg_get_param_cb(char *buffer, const struct kernel_param *kp)
|
|
{
|
|
#define KERN_PARAM_OPS_MAX_BUF_SIZE (4 * 1024)
|
|
int filter;
|
|
int val = 0;
|
|
|
|
filter = *((int *)(kp->arg));
|
|
|
|
if (filter == SLSI_OVERRIDE_ALL_FILTER)
|
|
val = slsi_dbg_lvl_all;
|
|
else if (filter < 0 || filter >= SLSI_DF_MAX)
|
|
pr_info("%s: filter %d out of range\n", __func__, filter);
|
|
else
|
|
val = *slsi_dbg_filters[filter];
|
|
|
|
return snprintf(buffer, KERN_PARAM_OPS_MAX_BUF_SIZE, "%i", val);
|
|
}
|
|
|
|
module_param_cb(slsi_dbg_lvl_all, ¶m_ops_log, (void *)&SLSI_OVERRIDE_ALL_FILTER, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(slsi_dbg_lvl_all, "Override debug level (0~4) for all the log modules (-1 = do not override) default=0");
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_OFFLINE_TRACE
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* The slsi_offline_debug_buffer buffer can be used to debug the HIP behaviour offline
|
|
* i.e. without using the tracing functions that change the timing.
|
|
*
|
|
* Call slsi_offline_dbg_printf() with printf arguments to store a string into the buffer.
|
|
* When unifi_debug_buf_dump() is called, the contents of the buffer are dumped into the trace.
|
|
* The buffer is circular and writing to it when it is full simply means the oldest string(s)
|
|
* are overwritten by the new one.
|
|
* All these functions are intended to be called only from bh context.
|
|
* They are not safe to call from multiple contexts
|
|
* The exception is slsi_offline_dbg_mark() which is intended to be called from another context
|
|
*
|
|
* Implementation:
|
|
* Each string of trace is stored in the buffer as a null terminated string
|
|
* (NB this means the buffer contains the null termination for every string, changed from wifi5)
|
|
* At all times slsi_offline_debug_ptr points at where the next string is about to be written
|
|
* If the wrap flag is set, then the buffer is full and new messages are overwriting older ones
|
|
* The wrap from the top of the buffer back to the start is never done part way through a string.
|
|
* Instead if the string does not fit in the remaining space at the top (highest offset) of the buffer then the whole string
|
|
* is written at the bottom (slsi_offline_debug_buffer[0]) of the buffer.
|
|
* After the first wrap, the slsi_offline_debug_ptr is assumed to point part way through an older partially-overwritten string.
|
|
* So for a dump, we search to the end of this partial string (which is discarded) and start printing strings after it.
|
|
*
|
|
* After we have wrapped for the first time slsi_offline_debug_top is always maintained to point
|
|
* one byte beyond the terminating '\0' of the last string that we wrote before the last time we wrapped.
|
|
* Before the first wrap, contents of slsi_offline_debug_top are undefined.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
#define SLSI_OFFLINE_DEBUG_BUFFER_SIZE 32768
|
|
static char slsi_offline_debug_buffer[SLSI_OFFLINE_DEBUG_BUFFER_SIZE];
|
|
static char *slsi_offline_debug_ptr = slsi_offline_debug_buffer;
|
|
static char *slsi_offline_debug_top; /* marks where we wrapped last time, undefined until slsi_offline_debug_wrap_flag is set */
|
|
static s16 slsi_offline_debug_wrap_flag;
|
|
static char slsi_offline_debug_mark_req;
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Puts "MARK" + the passed id value into the offline trace
|
|
* but only when a trace function is next called in bh context.
|
|
* The idea is this allows another context (other than bh) to put something into the trace so
|
|
* that the relative timing of events can be seen in the log
|
|
*
|
|
* Arguments:
|
|
* id single byte value to identify a point in the log
|
|
* zero is not an allowed value (a request with 0 will do nothing)
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void slsi_offline_dbg_mark(struct slsi_dev *sdev, int level, u8 id)
|
|
{
|
|
if (level > offline_dbg_level)
|
|
return;
|
|
|
|
if (id)
|
|
slsi_offline_debug_mark_req = id;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Puts the contents of the passed buffer into the debug buffer
|
|
* if append_flag is set attempts to append to the previous string
|
|
* (unless too close to end of buffer, in which case, it will be a separate string regardless)
|
|
*
|
|
* Arguments:
|
|
* hex_flag flag set to print hex string, clear to print null terminated string.
|
|
* append if set, attempts to append to last string
|
|
* buff buffer to print as hex (if hex_flag is set)
|
|
* string to print (if hex_flag is clear)
|
|
* length number of hex chars to print (ignored if hex_flag is clear)
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static void slsi_offline_dbg_append(struct slsi_dev *sdev, int level, bool hex, bool append, const char *buff, u16 length)
|
|
{
|
|
u8 *dest;
|
|
u32 string_len = hex ? 2 * length : strlen(buff);
|
|
u32 free_space = sizeof(slsi_offline_debug_buffer) / sizeof(slsi_offline_debug_buffer[0]) - (slsi_offline_debug_ptr - slsi_offline_debug_buffer);
|
|
|
|
if (level > offline_dbg_level)
|
|
return;
|
|
|
|
if (slsi_offline_debug_mark_req) {
|
|
u8 copy = slsi_offline_debug_mark_req;
|
|
|
|
slsi_offline_debug_mark_req = 0;
|
|
slsi_offline_dbg_printf(sdev, level, false, "MARK=%u", copy);
|
|
}
|
|
|
|
if (free_space >= string_len + 2) /* need one extra byte for slsi_offline_debug_ptr to point to, one for final '\0' */
|
|
dest = (slsi_offline_debug_ptr > slsi_offline_debug_buffer && append) ? slsi_offline_debug_ptr - 1 : slsi_offline_debug_ptr;
|
|
else {
|
|
/* there is not enough space for the whole string so instead put it at the bottom of buffer */
|
|
/* need one extra byte for slsi_offline_debug_ptr to point to, one for terminating '\0' */
|
|
if (sizeof(slsi_offline_debug_buffer) > string_len + 2) {
|
|
slsi_offline_debug_wrap_flag = 1;
|
|
slsi_offline_debug_top = slsi_offline_debug_ptr;
|
|
dest = slsi_offline_debug_buffer;
|
|
} else {
|
|
SLSI_ERR_NODEV("ignoring offline trace too large at %d chars\n", string_len);
|
|
return;
|
|
}
|
|
}
|
|
slsi_offline_debug_ptr = dest + string_len + 1; /* advance over string and terminating null */
|
|
|
|
if (hex) {
|
|
for (; length > 0; length--) {
|
|
static const char *hexstring = "0123456789ABCDEF";
|
|
|
|
*dest++ = hexstring[(*buff >> 4) & 0x0f];
|
|
*dest++ = hexstring[*buff++ & 0x0f];
|
|
}
|
|
*dest = '\0';
|
|
} else {
|
|
strcpy(dest, buff);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* puts the contents of the passed string into the debug buffer
|
|
* as a separate string
|
|
* Arguments:
|
|
* str string to print into buffer
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void slsi_offline_dbg_strcpy(struct slsi_dev *sdev, int level, bool append, const char *str)
|
|
{
|
|
if (level > offline_dbg_level)
|
|
return;
|
|
|
|
slsi_spinlock_lock(&sdev->offline_dbg_lock);
|
|
slsi_offline_dbg_append(sdev, level, false, append, str, 0);
|
|
slsi_spinlock_unlock(&sdev->offline_dbg_lock);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* if append_flag is set attempts to append to the previous string
|
|
* (unless too close to end of buffer, in which case, it will be a separate string regardless)
|
|
* on most implementations likely to mean it appears later in trace on the same line rather than a new one
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void slsi_offline_dbg_printf(struct slsi_dev *sdev, int level, bool append, const char *fmt, ...)
|
|
{
|
|
s32 len;
|
|
s32 free_space;
|
|
char *dest;
|
|
va_list args;
|
|
|
|
if (level > offline_dbg_level)
|
|
return;
|
|
|
|
slsi_spinlock_lock(&sdev->offline_dbg_lock);
|
|
|
|
free_space = sizeof(slsi_offline_debug_buffer) / sizeof(slsi_offline_debug_buffer[0]) - (slsi_offline_debug_ptr - slsi_offline_debug_buffer);
|
|
|
|
if (slsi_offline_debug_mark_req) {
|
|
u8 copy = slsi_offline_debug_mark_req;
|
|
|
|
slsi_offline_debug_mark_req = 0;
|
|
slsi_offline_dbg_printf(sdev, level, false, "MARK=%u", copy);
|
|
}
|
|
|
|
/* if appending, point ready to overwrite terminating null of previous string. (ignore beneficial effect on free space) */
|
|
dest = (slsi_offline_debug_ptr > slsi_offline_debug_buffer && append) ? slsi_offline_debug_ptr - 1 : slsi_offline_debug_ptr;
|
|
|
|
va_start(args, fmt);
|
|
len = vsnprintf(dest, free_space, fmt, args);
|
|
va_end(args);
|
|
|
|
/* remember vsnprintf does not report space needed for terminating null */
|
|
/* also need one extra byte for slsi_offline_debug_ptr to point to, one for final '\0' */
|
|
/* following test also copes with non standard vsnprintf, which for example (incorrectly) returns length actually printed or -1 */
|
|
if ((len >= 0) && (free_space >= len + 2)) {
|
|
slsi_offline_debug_ptr = dest + len + 1; /* advance over string and terminating null */
|
|
} else {
|
|
/* there is not enough space for the whole string so instead put it at the bottom of buffer */
|
|
if (len < (s32)sizeof(slsi_offline_debug_buffer) - 1) { /* need one extra byte for slsi_offline_debug_ptr to point to */
|
|
slsi_offline_debug_wrap_flag = 1;
|
|
slsi_offline_debug_top = slsi_offline_debug_ptr;
|
|
va_start(args, fmt);
|
|
len = vsnprintf(slsi_offline_debug_buffer, sizeof(slsi_offline_debug_buffer), fmt, args);
|
|
va_end(args);
|
|
|
|
slsi_offline_debug_ptr = slsi_offline_debug_buffer + len + 1; /* advance over string and terminating null */
|
|
} else {
|
|
SLSI_ERR_NODEV("ignoring offline message because too large\n");
|
|
}
|
|
}
|
|
|
|
slsi_spinlock_unlock(&sdev->offline_dbg_lock);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* puts the contents of the passed buffer into the debug buffer as a hex string
|
|
* attempts to append to the previous string
|
|
* (unless too close to end of buffer in which case, it will be a separate string)
|
|
*
|
|
* Arguments:
|
|
* buff buffer to print as hex
|
|
* length number of chars to print
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void slsi_offline_dbg_hex(struct slsi_dev *sdev, int level, const u8 *buff, u16 length)
|
|
{
|
|
if (level > offline_dbg_level)
|
|
return;
|
|
|
|
slsi_spinlock_lock(&sdev->offline_dbg_lock);
|
|
slsi_offline_dbg_append(sdev, level, true, true, (char *)buff, length);
|
|
slsi_spinlock_unlock(&sdev->offline_dbg_lock);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Writes the offline log data to a procfs seq_file
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void slsi_offline_dbg_dump_to_seq_file(struct slsi_dev *sdev, struct seq_file *m)
|
|
{
|
|
char *point;
|
|
|
|
slsi_spinlock_lock(&sdev->offline_dbg_lock);
|
|
|
|
if (slsi_offline_debug_wrap_flag)
|
|
if (slsi_offline_debug_ptr < slsi_offline_debug_top) {
|
|
point = slsi_offline_debug_ptr + strlen(slsi_offline_debug_ptr) + 1; /* discard first, partially overwritten string */
|
|
while (point < slsi_offline_debug_top) {
|
|
seq_printf(m, "%s\n", point);
|
|
point += (strlen(point) + 1);
|
|
}
|
|
}
|
|
|
|
for (point = slsi_offline_debug_buffer; point < slsi_offline_debug_ptr; point += (strlen(point) + 1))
|
|
seq_printf(m, "%s\n", point);
|
|
|
|
slsi_spinlock_unlock(&sdev->offline_dbg_lock);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Writes the offline log data to the kernel log
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void slsi_offline_dbg_dump_to_klog(struct slsi_dev *sdev)
|
|
{
|
|
char *point;
|
|
|
|
slsi_spinlock_lock(&sdev->offline_dbg_lock);
|
|
|
|
if (slsi_offline_debug_wrap_flag)
|
|
if (slsi_offline_debug_ptr < slsi_offline_debug_top) {
|
|
point = slsi_offline_debug_ptr + strlen(slsi_offline_debug_ptr) + 1; /* discard first, partially overwritten string */
|
|
while (point < slsi_offline_debug_top) {
|
|
SLSI_INFO(sdev, "%s", point);
|
|
point += (strlen(point) + 1);
|
|
}
|
|
}
|
|
|
|
for (point = slsi_offline_debug_buffer; point < slsi_offline_debug_ptr; point += (strlen(point) + 1))
|
|
SLSI_INFO(sdev, "%s", point);
|
|
|
|
slsi_spinlock_unlock(&sdev->offline_dbg_lock);
|
|
}
|
|
|
|
#endif /* CONFIG_SCSC_WLAN_OFFLINE_TRACE */
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_SKB_TRACKING
|
|
struct slsi_skb_tracker {
|
|
struct slsi_spinlock lock;
|
|
u32 device_count;
|
|
struct list_head tracked;
|
|
u32 tracked_count;
|
|
u32 tracked_count_max;
|
|
};
|
|
|
|
struct slsi_tracked_skb {
|
|
struct list_head entry;
|
|
const char *file;
|
|
int line;
|
|
struct sk_buff *skb;
|
|
};
|
|
|
|
static struct slsi_skb_tracker skb_tracker;
|
|
|
|
void slsi_dbg_track_skb_init(void)
|
|
{
|
|
SLSI_DBG4_NODEV(SLSI_TEST, "\n");
|
|
memset(&skb_tracker, 0x00, sizeof(skb_tracker));
|
|
slsi_spinlock_create(&skb_tracker.lock);
|
|
INIT_LIST_HEAD(&skb_tracker.tracked);
|
|
}
|
|
|
|
void slsi_dbg_track_skb_reset(void)
|
|
{
|
|
SLSI_DBG4_NODEV(SLSI_TEST, "\n");
|
|
skb_tracker.device_count = 0;
|
|
skb_tracker.tracked_count = 0;
|
|
skb_tracker.tracked_count_max = 0;
|
|
}
|
|
|
|
bool slsi_dbg_track_skb_marker_f(struct sk_buff *skb, const char *file, int line)
|
|
{
|
|
struct slsi_tracked_skb *t;
|
|
struct list_head *pos, *q;
|
|
bool r = true;
|
|
|
|
if (!skb)
|
|
return r;
|
|
|
|
slsi_spinlock_lock(&skb_tracker.lock);
|
|
list_for_each_safe(pos, q, &skb_tracker.tracked) {
|
|
t = list_entry(pos, struct slsi_tracked_skb, entry);
|
|
if (t->skb == skb) {
|
|
SLSI_DBG4_NODEV(SLSI_TEST, "0x%p: %s:%d", skb, file, line);
|
|
t->file = file;
|
|
t->line = line;
|
|
goto exit;
|
|
}
|
|
}
|
|
WARN_ON(1);
|
|
SLSI_ERR_NODEV("SKB Not Tracked: %p: %s:%d", skb, file, line);
|
|
r = false;
|
|
exit:
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
return r;
|
|
}
|
|
|
|
void slsi_dbg_track_skb_f(struct sk_buff *skb, gfp_t flags, const char *file, int line)
|
|
{
|
|
struct slsi_tracked_skb *t = kmalloc(sizeof(*t), flags);
|
|
|
|
if (!t)
|
|
return;
|
|
|
|
t->file = file;
|
|
t->line = line;
|
|
t->skb = skb_get(skb); /* Add a reference to the skb */
|
|
SLSI_DBG4_NODEV(SLSI_TEST, "0x%p: %s:%d", skb, file, line);
|
|
slsi_spinlock_lock(&skb_tracker.lock);
|
|
list_add(&t->entry, &skb_tracker.tracked);
|
|
skb_tracker.tracked_count++;
|
|
if (skb_tracker.tracked_count > skb_tracker.tracked_count_max)
|
|
skb_tracker.tracked_count_max = skb_tracker.tracked_count;
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
}
|
|
|
|
bool slsi_dbg_untrack_skb_f(struct sk_buff *skb, const char *file, int line)
|
|
{
|
|
struct slsi_tracked_skb *t;
|
|
struct list_head *pos, *q;
|
|
bool r = true;
|
|
|
|
if (!skb)
|
|
return r;
|
|
|
|
slsi_spinlock_lock(&skb_tracker.lock);
|
|
list_for_each_safe(pos, q, &skb_tracker.tracked) {
|
|
t = list_entry(pos, struct slsi_tracked_skb, entry);
|
|
if (t->skb == skb) {
|
|
SLSI_DBG4_NODEV(SLSI_TEST, "0x%p: %s:%d", skb, file, line);
|
|
list_del(pos);
|
|
kfree_skb(t->skb); /* Free the reference we took */
|
|
kfree(t);
|
|
skb_tracker.tracked_count--;
|
|
goto exit;
|
|
}
|
|
}
|
|
WARN_ON(1);
|
|
SLSI_ERR_NODEV("SKB Not Tracked: %p: %s:%d", skb, file, line);
|
|
r = false;
|
|
exit:
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
return r;
|
|
}
|
|
|
|
void slsi_dbg_skb_device_add(void)
|
|
{
|
|
slsi_spinlock_lock(&skb_tracker.lock);
|
|
skb_tracker.device_count++;
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
}
|
|
|
|
void slsi_dbg_skb_device_remove(void)
|
|
{
|
|
slsi_spinlock_lock(&skb_tracker.lock);
|
|
if (skb_tracker.device_count)
|
|
skb_tracker.device_count--;
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
}
|
|
|
|
void slsi_dbg_track_skb_report(void)
|
|
{
|
|
struct slsi_tracked_skb *t;
|
|
struct list_head *pos, *q;
|
|
|
|
slsi_spinlock_lock(&skb_tracker.lock);
|
|
if (skb_tracker.device_count) {
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
return;
|
|
}
|
|
SLSI_INFO_NODEV("Tracked Count Current: %d\n", skb_tracker.tracked_count);
|
|
SLSI_INFO_NODEV("Tracked Count Max : %d\n", skb_tracker.tracked_count_max);
|
|
list_for_each_safe(pos, q, &skb_tracker.tracked) {
|
|
t = list_entry(pos, struct slsi_tracked_skb, entry);
|
|
if (skb_shared(t->skb))
|
|
SLSI_ERR_NODEV("SKB Leak: 0x%p: %s:%d\n", t->skb, t->file, t->line);
|
|
else
|
|
SLSI_ERR_NODEV("SKB Not Untracked: 0x%p: %s:%d\n", t->skb, t->file, t->line);
|
|
list_del(pos);
|
|
kfree_skb(t->skb); /* Free the reference we took */
|
|
kfree(t);
|
|
skb_tracker.tracked_count--;
|
|
}
|
|
INIT_LIST_HEAD(&skb_tracker.tracked);
|
|
slsi_spinlock_unlock(&skb_tracker.lock);
|
|
}
|
|
#endif
|