lineage_kernel_xcoverpro/drivers/net/wireless/scsc/scsc_wifilogger_internal.c

235 lines
5.5 KiB
C
Raw Normal View History

2023-06-18 22:53:49 +00:00
/******************************************************************************
*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
*
******************************************************************************/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <scsc/scsc_logring.h>
#include <scsc/scsc_mx.h>
#include "scsc_wifilogger_internal.h"
static struct scsc_wifi_logger *wifi_logger;
struct scsc_wifi_logger *scsc_wifilogger_get_handle(void)
{
return wifi_logger;
}
bool scsc_wifilogger_init(void)
{
struct scsc_wifi_logger *wl = NULL;
wl = kzalloc(sizeof(*wl), GFP_KERNEL);
if (!wl) {
SCSC_TAG_ERR(WLOG,
"Failed to allocate scsc_wifilogger data. Abort.\n");
return false;
}
mutex_init(&wl->lock);
wl->initialized = true;
wifi_logger = wl;
return true;
}
static int scsc_wifilogger_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
int ret = NOTIFY_DONE;
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG, "WiFi Logger NOT initialized !\n");
return NOTIFY_BAD;
}
switch (event) {
case SCSC_FW_EVENT_FAILURE:
break;
case SCSC_FW_EVENT_MOREDUMP_COMPLETE:
{
char *panic_record_dump = data;
SCSC_TAG_INFO(WLOG, "Notification received: MOREDUMP COMPLETED.\n");
SCSC_TAG_DEBUG(WLOG, "PANIC DUMP RX\n-------------------------\n\n%s\n",
panic_record_dump);
if (wl->on_alert_cb) {
wl->on_alert_cb(panic_record_dump,
PANIC_RECORD_DUMP_BUFFER_SZ, 0, wl->on_alert_ctx);
SCSC_TAG_DEBUG(WLOG, "Alert handler -- processed %d bytes @%p\n",
PANIC_RECORD_DUMP_BUFFER_SZ, panic_record_dump);
}
ret = NOTIFY_OK;
break;
}
default:
ret = NOTIFY_BAD;
break;
}
return ret;
}
static struct notifier_block firmware_nb = {
.notifier_call = scsc_wifilogger_notifier,
};
bool scsc_wifilogger_fw_alert_init(void)
{
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG, "WiFi Logger NOT initialized !\n");
return false;
}
wl->features_mask |= WIFI_LOGGER_MEMORY_DUMP_SUPPORTED | WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
if (!mxman_register_firmware_notifier(&firmware_nb))
wl->features_mask |= WIFI_LOGGER_WATCHDOG_TIMER_SUPPORTED;
return true;
}
unsigned int scsc_wifilogger_get_features(void)
{
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG, "WiFi Logger NOT initialized !\n");
return 0;
}
return wl->features_mask;
}
bool scsc_wifilogger_get_rings_status(u32 *num_rings,
struct scsc_wifi_ring_buffer_status *status)
{
int i, j = 0;
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG, "WiFi Logger NOT initialized !\n");
*num_rings = 0;
return false;
}
for (i = 0; i < *num_rings && i < MAX_WIFI_LOGGER_RINGS; i++)
if (wl->rings[i] && wl->rings[i]->registered)
scsc_wlog_get_ring_status(wl->rings[i], &status[j++]);
*num_rings = j;
return true;
}
struct scsc_wlog_ring *scsc_wifilogger_get_ring_from_name(char *name)
{
int i;
struct scsc_wlog_ring *r = NULL;
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG,
"WiFi Logger NOT initialized..cannot find ring !\n");
return r;
}
mutex_lock(&wl->lock);
for (i = 0; i < MAX_WIFI_LOGGER_RINGS; i++) {
if (wl->rings[i] &&
!strncmp(name, wl->rings[i]->st.name, RING_NAME_SZ)) {
if (wl->rings[i]->initialized &&
wl->rings[i]->registered)
r = wl->rings[i];
break;
}
}
mutex_unlock(&wl->lock);
return r;
}
bool scsc_wifilogger_register_ring(struct scsc_wlog_ring *r)
{
int pos;
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG,
"WiFi Logger NOT initialized..cannot register ring !\n");
return false;
}
/**
* Calculate ring position in array from unique ring_id:
* there can be multiple distinct rings supporting the same
* feature....like pkt_fate_tx/rx
*/
pos = r->st.ring_id % MAX_WIFI_LOGGER_RINGS;
mutex_lock(&wl->lock);
if (wl->rings[pos]) {
SCSC_TAG_ERR(WLOG,
"Ring %s already registered on position %d. Abort\n",
wl->rings[pos]->st.name, pos);
mutex_unlock(&wl->lock);
return false;
}
SCSC_TAG_DEBUG(WLOG, "Registering ring %s as position %d\n",
r->st.name, pos);
wl->rings[pos] = r;
wl->features_mask |= r->features_mask;
r->wl = wl;
r->registered = true;
mutex_unlock(&wl->lock);
SCSC_TAG_INFO(WLOG, "Ring '%s' registered\n", r->st.name);
return true;
}
void scsc_wifilogger_destroy(void)
{
int i;
struct scsc_wlog_ring *r = NULL;
struct scsc_wifi_logger *wl = NULL;
wl = scsc_wifilogger_get_handle();
if (!wl || !wl->initialized) {
SCSC_TAG_ERR(WLOG,
"WiFi Logger NOT initialized..cannot destroy!\n");
return;
}
mxman_unregister_firmware_notifier(&firmware_nb);
/* Remove DebufgFS hooks at first... */
#ifdef CONFIG_SCSC_WIFILOGGER_DEBUGFS
scsc_wifilogger_debugfs_remove_top_dir_recursive();
#endif
mutex_lock(&wl->lock);
for (i = 0; i < MAX_WIFI_LOGGER_RINGS; i++) {
if (wl->rings[i]) {
r = wl->rings[i];
scsc_wlog_ring_change_verbosity(r, WLOG_NONE);
r->registered = false;
wl->rings[i] = NULL;
if (r->initialized)
scsc_wlog_ring_destroy(r);
}
}
wl->features_mask = 0;
mutex_unlock(&wl->lock);
kfree(wl);
wifi_logger = NULL;
}