blob: 35a2ad573aa8b7a06a4432a557c45d65daf16f6a [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
/*
* bcm_dev_log_linux.c
*
* Linux-specific code. User space
*/
#include <bcmolt_dev_log_linux.h>
#include <kernel/bcmolt_dev_log_ioctl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#define BCM_DEV_LOG_FILE_NAME "/dev/bcm_log"
#define BCM_DEV_LOG_POLL_INTERVAL_US 10000
static int dev_log_file;
static bcmos_task dev_log_read_task;
static int max_kernel_id_index;
/*
* Internal helpers
*/
/* read kernel DB */
static bcmos_errno _dev_log_kernel_db_read(void)
{
dev_log_io_param io_param;
int i;
if (ioctl(dev_log_file, DEV_LOG_CHRDEV_DB_READ, &io_param) == -1)
{
BCMOS_TRACE_RETURN(BCM_ERR_IO, "Can't read kernel data base. Error %s\n", strerror(errno));
}
/* Register kernel log ids */
for (i=0; i<io_param.db_read.num_ids; i++)
{
bcm_dev_log_id_register(io_param.db_read.ids[i].name,
io_param.db_read.ids[i].default_level,
io_param.db_read.ids[i].default_type);
}
max_kernel_id_index = i;
return BCM_ERR_OK;
}
/* identify log level by level char */
static bcm_dev_log_level _dev_log_level_by_char(char clevel)
{
bcm_dev_log_level level;
switch (clevel)
{
case 'F': level = DEV_LOG_LEVEL_FATAL; break;
case 'E': level = DEV_LOG_LEVEL_ERROR; break;
case 'W': level = DEV_LOG_LEVEL_WARNING; break;
case 'I': level = DEV_LOG_LEVEL_INFO; break;
case 'D': level = DEV_LOG_LEVEL_DEBUG; break;
default: level = DEV_LOG_LEVEL_NO_LOG; break;
}
return level;
}
/* read_buf task handler */
static int _dev_log_read_kernel_buf_handler(long data)
{
dev_log_io_param io_param = {};
unsigned long log_time;
char level_char;
char log_name[MAX_DEV_LOG_ID_NAME];
int n;
dev_log_id id;
bcm_dev_log_level level;
while (dev_log_file)
{
if (ioctl(dev_log_file, DEV_LOG_CHRDEV_MSG_READ, &io_param) < 0)
{
/* If no entries - sleep ant retry. Otherwise - stop */
if (errno == EAGAIN)
{
bcmos_usleep(BCM_DEV_LOG_POLL_INTERVAL_US);
continue;
}
BCMOS_TRACE_ERR("ioctl()->%d (%s)\n", errno, strerror(errno));
break;
}
/* parse kernel message */
n = sscanf(io_param.msg_read.msg, "[%lu: %c %20s]", &log_time, &level_char, log_name);
if (n < 3)
{
BCMOS_TRACE_ERR("Can't parse kernel log: %s\n", io_param.msg_read.msg);
continue;
}
/* Identify log_id */
id = bcm_dev_log_id_get_by_name(log_name);
if (id == DEV_LOG_INVALID_ID)
{
BCMOS_TRACE_ERR("Can't identify log id: %s\n", io_param.msg_read.msg);
continue;
}
/* Identify log_level */
level = _dev_log_level_by_char(level_char);
if (level == DEV_LOG_LEVEL_NO_LOG)
{
BCMOS_TRACE_ERR("Can't identify log level: %s\n", io_param.msg_read.msg);
continue;
}
/* log kernel message in user-space logger.
* Do not generate header. It is already in the message
*/
bcm_dev_log_log(id, level, BCM_LOG_FLAG_NO_HEADER | BCM_LOG_FLAG_CALLER_FMT, "%s", io_param.msg_read.msg);
}
BCMOS_TRACE_INFO("Kernel logger daemon terminated\n");
return 0;
}
/** Linux-specific init */
bcmos_errno bcm_dev_log_linux_init(void)
{
static char *task_name = "kernel_log";
bcmos_task_parm taskp =
{
.name = task_name,
.priority = TASK_PRIORITY_DEV_LOG_KERNEL,
.handler = _dev_log_read_kernel_buf_handler
};
bcmos_errno rc;
if (dev_log_file)
return BCM_ERR_ALREADY;
/* Read kernel registrations */
dev_log_file = open(BCM_DEV_LOG_FILE_NAME, O_RDWR);
if (dev_log_file < 0)
{
dev_log_file = 0;
BCMOS_TRACE_RETURN(BCM_ERR_NOENT, "Can't open %s for read/write. Error %s\n",
BCM_DEV_LOG_FILE_NAME, strerror(errno));
}
/* Read kernel data base */
rc = _dev_log_kernel_db_read();
if (rc)
{
close(dev_log_file);
dev_log_file = 0;
return rc;
}
/* Create a task that will poll kernel buffer */
rc = bcmos_task_create(&dev_log_read_task, &taskp);
if (rc)
{
close(dev_log_file);
dev_log_file = 0;
BCMOS_TRACE_RETURN(rc, "Can't create read_kernel_buf task\n");
}
return BCM_ERR_OK;
}
/** Linux-specific cleanup */
void bcm_dev_log_linux_exit(void)
{
int f = dev_log_file;
if (!dev_log_file)
return;
dev_log_file = 0;
close(f); /* will cause pending ioctl to unlock */
bcmos_task_destroy(&dev_log_read_task);
}
/* notify dev_log in kernel space about log_type change */
bcmos_errno bcm_dev_log_linux_id_set_type(dev_log_id log_id, bcm_dev_log_id_type log_type)
{
dev_log_io_param io_param;
uint32_t idx;
if (!dev_log_file)
return BCM_ERR_STATE;
/* map to index. id in kernel space is different */
idx = bcm_dev_log_get_index_by_id(log_id);
if (idx == DEV_LOG_INVALID_INDEX)
{
BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't map log_id to index\n");
}
/* Ignore the request if log_id is not in th ekernel */
if (idx > max_kernel_id_index)
return BCM_ERR_OK;
io_param.type_set.index = idx;
io_param.type_set.type = log_type;
if (ioctl(dev_log_file, DEV_LOG_CHRDEV_TYPE_SET, &io_param) == -1)
{
BCMOS_TRACE_RETURN(BCM_ERR_IO, "Can't set type for kernel log id %lu. Error %s\n", log_id, strerror(errno));
}
return BCM_ERR_OK;
}
/* notify dev_log in kernel space about log_level change */
bcmos_errno bcm_dev_log_linux_id_set_level(dev_log_id log_id, bcm_dev_log_level log_level_print, bcm_dev_log_level log_level_save)
{
uint32_t idx;
dev_log_io_param io_param;
if (!dev_log_file)
return BCM_ERR_STATE;
/* map to index. id in kernel space is different */
idx = bcm_dev_log_get_index_by_id(log_id);
if (idx == DEV_LOG_INVALID_INDEX)
{
BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't map log_id to index\n");
}
/* Ignore the request if log_id is not in the kernel */
if (idx > max_kernel_id_index)
return BCM_ERR_OK;
io_param.level_set.index = idx;
io_param.level_set.level_print = log_level_print;
io_param.level_set.level_save = log_level_save;
if (ioctl(dev_log_file, DEV_LOG_CHRDEV_LEVEL_SET, &io_param) == -1)
{
BCMOS_TRACE_RETURN(BCM_ERR_IO, "Can't set level for kernel log id %lu. Error %s\n", log_id, strerror(errno));
}
return BCM_ERR_OK;
}