| /* |
| <: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; |
| } |