blob: 35a2ad573aa8b7a06a4432a557c45d65daf16f6a [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30/*
31 * bcm_dev_log_linux.c
32 *
33 * Linux-specific code. User space
34 */
35
36#include <bcmolt_dev_log_linux.h>
37#include <kernel/bcmolt_dev_log_ioctl.h>
38
39#include <stdio.h>
40#include <sys/ioctl.h>
41#include <sys/types.h>
42#include <fcntl.h>
43
44#define BCM_DEV_LOG_FILE_NAME "/dev/bcm_log"
45#define BCM_DEV_LOG_POLL_INTERVAL_US 10000
46
47static int dev_log_file;
48static bcmos_task dev_log_read_task;
49static int max_kernel_id_index;
50/*
51 * Internal helpers
52 */
53
54/* read kernel DB */
55static bcmos_errno _dev_log_kernel_db_read(void)
56{
57 dev_log_io_param io_param;
58 int i;
59
60 if (ioctl(dev_log_file, DEV_LOG_CHRDEV_DB_READ, &io_param) == -1)
61 {
62 BCMOS_TRACE_RETURN(BCM_ERR_IO, "Can't read kernel data base. Error %s\n", strerror(errno));
63 }
64
65 /* Register kernel log ids */
66 for (i=0; i<io_param.db_read.num_ids; i++)
67 {
68 bcm_dev_log_id_register(io_param.db_read.ids[i].name,
69 io_param.db_read.ids[i].default_level,
70 io_param.db_read.ids[i].default_type);
71 }
72 max_kernel_id_index = i;
73
74 return BCM_ERR_OK;
75}
76
77/* identify log level by level char */
78static bcm_dev_log_level _dev_log_level_by_char(char clevel)
79{
80 bcm_dev_log_level level;
81
82 switch (clevel)
83 {
84 case 'F': level = DEV_LOG_LEVEL_FATAL; break;
85 case 'E': level = DEV_LOG_LEVEL_ERROR; break;
86 case 'W': level = DEV_LOG_LEVEL_WARNING; break;
87 case 'I': level = DEV_LOG_LEVEL_INFO; break;
88 case 'D': level = DEV_LOG_LEVEL_DEBUG; break;
89 default: level = DEV_LOG_LEVEL_NO_LOG; break;
90 }
91
92 return level;
93}
94
95/* read_buf task handler */
96static int _dev_log_read_kernel_buf_handler(long data)
97{
98 dev_log_io_param io_param = {};
99 unsigned long log_time;
100 char level_char;
101 char log_name[MAX_DEV_LOG_ID_NAME];
102 int n;
103 dev_log_id id;
104 bcm_dev_log_level level;
105
106 while (dev_log_file)
107 {
108 if (ioctl(dev_log_file, DEV_LOG_CHRDEV_MSG_READ, &io_param) < 0)
109 {
110 /* If no entries - sleep ant retry. Otherwise - stop */
111 if (errno == EAGAIN)
112 {
113 bcmos_usleep(BCM_DEV_LOG_POLL_INTERVAL_US);
114 continue;
115 }
116 BCMOS_TRACE_ERR("ioctl()->%d (%s)\n", errno, strerror(errno));
117 break;
118 }
119
120 /* parse kernel message */
121 n = sscanf(io_param.msg_read.msg, "[%lu: %c %20s]", &log_time, &level_char, log_name);
122 if (n < 3)
123 {
124 BCMOS_TRACE_ERR("Can't parse kernel log: %s\n", io_param.msg_read.msg);
125 continue;
126 }
127
128 /* Identify log_id */
129 id = bcm_dev_log_id_get_by_name(log_name);
130 if (id == DEV_LOG_INVALID_ID)
131 {
132 BCMOS_TRACE_ERR("Can't identify log id: %s\n", io_param.msg_read.msg);
133 continue;
134 }
135
136 /* Identify log_level */
137 level = _dev_log_level_by_char(level_char);
138 if (level == DEV_LOG_LEVEL_NO_LOG)
139 {
140 BCMOS_TRACE_ERR("Can't identify log level: %s\n", io_param.msg_read.msg);
141 continue;
142 }
143
144 /* log kernel message in user-space logger.
145 * Do not generate header. It is already in the message
146 */
147 bcm_dev_log_log(id, level, BCM_LOG_FLAG_NO_HEADER | BCM_LOG_FLAG_CALLER_FMT, "%s", io_param.msg_read.msg);
148 }
149 BCMOS_TRACE_INFO("Kernel logger daemon terminated\n");
150
151 return 0;
152}
153
154/** Linux-specific init */
155bcmos_errno bcm_dev_log_linux_init(void)
156{
157 static char *task_name = "kernel_log";
158 bcmos_task_parm taskp =
159 {
160 .name = task_name,
161 .priority = TASK_PRIORITY_DEV_LOG_KERNEL,
162 .handler = _dev_log_read_kernel_buf_handler
163 };
164 bcmos_errno rc;
165
166 if (dev_log_file)
167 return BCM_ERR_ALREADY;
168
169 /* Read kernel registrations */
170 dev_log_file = open(BCM_DEV_LOG_FILE_NAME, O_RDWR);
171 if (dev_log_file < 0)
172 {
173 dev_log_file = 0;
174 BCMOS_TRACE_RETURN(BCM_ERR_NOENT, "Can't open %s for read/write. Error %s\n",
175 BCM_DEV_LOG_FILE_NAME, strerror(errno));
176 }
177
178 /* Read kernel data base */
179 rc = _dev_log_kernel_db_read();
180 if (rc)
181 {
182 close(dev_log_file);
183 dev_log_file = 0;
184 return rc;
185 }
186
187 /* Create a task that will poll kernel buffer */
188 rc = bcmos_task_create(&dev_log_read_task, &taskp);
189 if (rc)
190 {
191 close(dev_log_file);
192 dev_log_file = 0;
193 BCMOS_TRACE_RETURN(rc, "Can't create read_kernel_buf task\n");
194 }
195
196 return BCM_ERR_OK;
197}
198
199/** Linux-specific cleanup */
200void bcm_dev_log_linux_exit(void)
201{
202 int f = dev_log_file;
203 if (!dev_log_file)
204 return;
205 dev_log_file = 0;
206 close(f); /* will cause pending ioctl to unlock */
207 bcmos_task_destroy(&dev_log_read_task);
208}
209
210/* notify dev_log in kernel space about log_type change */
211bcmos_errno bcm_dev_log_linux_id_set_type(dev_log_id log_id, bcm_dev_log_id_type log_type)
212{
213 dev_log_io_param io_param;
214 uint32_t idx;
215
216 if (!dev_log_file)
217 return BCM_ERR_STATE;
218
219 /* map to index. id in kernel space is different */
220 idx = bcm_dev_log_get_index_by_id(log_id);
221 if (idx == DEV_LOG_INVALID_INDEX)
222 {
223 BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't map log_id to index\n");
224 }
225
226 /* Ignore the request if log_id is not in th ekernel */
227 if (idx > max_kernel_id_index)
228 return BCM_ERR_OK;
229
230 io_param.type_set.index = idx;
231 io_param.type_set.type = log_type;
232 if (ioctl(dev_log_file, DEV_LOG_CHRDEV_TYPE_SET, &io_param) == -1)
233 {
234 BCMOS_TRACE_RETURN(BCM_ERR_IO, "Can't set type for kernel log id %lu. Error %s\n", log_id, strerror(errno));
235 }
236
237 return BCM_ERR_OK;
238}
239
240/* notify dev_log in kernel space about log_level change */
241bcmos_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)
242{
243 uint32_t idx;
244 dev_log_io_param io_param;
245
246 if (!dev_log_file)
247 return BCM_ERR_STATE;
248
249 /* map to index. id in kernel space is different */
250 idx = bcm_dev_log_get_index_by_id(log_id);
251 if (idx == DEV_LOG_INVALID_INDEX)
252 {
253 BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't map log_id to index\n");
254 }
255
256 /* Ignore the request if log_id is not in the kernel */
257 if (idx > max_kernel_id_index)
258 return BCM_ERR_OK;
259
260 io_param.level_set.index = idx;
261 io_param.level_set.level_print = log_level_print;
262 io_param.level_set.level_save = log_level_save;
263
264 if (ioctl(dev_log_file, DEV_LOG_CHRDEV_LEVEL_SET, &io_param) == -1)
265 {
266 BCMOS_TRACE_RETURN(BCM_ERR_IO, "Can't set level for kernel log id %lu. Error %s\n", log_id, strerror(errno));
267 }
268
269 return BCM_ERR_OK;
270}