blob: 666558845f8e9b43a5dddf0a78086dba3d130f23 [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#ifdef ENABLE_LOG
31
32#include "bcm_dev_log_task.h"
33#include "bcm_dev_log_task_internal.h"
34#include "bcm_dev_log.h"
35#if defined(LINUX_KERNEL_SPACE) && !defined(__KERNEL__)
36#include "bcmolt_dev_log_linux.h"
37#endif
38
39#ifdef DEV_LOG_SYSLOG
40#include <syslog.h>
41#endif
42
43#define USE_ANSI_ESCAPE_CODES
44
45#ifdef USE_ANSI_ESCAPE_CODES
46#define DEV_LOG_STYLE_NORMAL "\033[0m\033#5"
47#define DEV_LOG_STYLE_BOLD "\033[1m"
48#define DEV_LOG_STYLE_UNDERLINE "\033[4m"
49#define DEV_LOG_STYLE_BLINK "\033[5m"
50#define DEV_LOG_STYLE_REVERSE_VIDEO "\033[7m"
51#else
52#define DEV_LOG_STYLE_NORMAL " "
53#define DEV_LOG_STYLE_BOLD "*** "
54#define DEV_LOG_STYLE_UNDERLINE "___ "
55#define DEV_LOG_STYLE_BLINK "o_o "
56#define DEV_LOG_STYLE_REVERSE_VIDEO "!!! "
57#endif
58
59#define DEV_LOG_MSG_START_CHAR 0x1e /* record separator character */
60
61#define MEM_FILE_HDR_SIZE sizeof(dev_log_mem_file_header)
62
63#define LOG_MIN(a,b) (((int)(a) <= (int)(b)) ? (a) : (b))
64
65dev_log_id def_log_id;
66
67static bcm_dev_log_style dev_log_level2style[DEV_LOG_LEVEL_NUM_OF] =
68{
69 [DEV_LOG_LEVEL_FATAL] = BCM_DEV_LOG_STYLE_NORMAL,
70 [DEV_LOG_LEVEL_ERROR] = BCM_DEV_LOG_STYLE_NORMAL,
71 [DEV_LOG_LEVEL_WARNING] = BCM_DEV_LOG_STYLE_NORMAL,
72 [DEV_LOG_LEVEL_INFO] = BCM_DEV_LOG_STYLE_NORMAL,
73 [DEV_LOG_LEVEL_DEBUG] = BCM_DEV_LOG_STYLE_NORMAL,
74};
75
76static const char *dev_log_style_array[] =
77{
78 [BCM_DEV_LOG_STYLE_NORMAL] = DEV_LOG_STYLE_NORMAL,
79 [BCM_DEV_LOG_STYLE_BOLD] = DEV_LOG_STYLE_BOLD,
80 [BCM_DEV_LOG_STYLE_UNDERLINE] = DEV_LOG_STYLE_UNDERLINE,
81 [BCM_DEV_LOG_STYLE_BLINK] = DEV_LOG_STYLE_BLINK,
82 [BCM_DEV_LOG_STYLE_REVERSE_VIDEO] = DEV_LOG_STYLE_REVERSE_VIDEO,
83};
84
85bcm_dev_log dev_log = {{0}};
86const char *log_level_str = "?FEWID";
87
88static void bcm_dev_log_shutdown_msg_release(bcmos_msg *m)
89{
90 (void)m; /* do nothing, the message is statically allocated */
91}
92
93static bcmos_msg shutdown_msg = { .release = bcm_dev_log_shutdown_msg_release };
94
95static void bcm_dev_log_file_save_msg(bcm_dev_log_file *files, const char *message)
96{
97 uint32_t i;
98 uint32_t len = strlen(message) + 1; /* including 0 terminator */
99
100 for (i = 0; (i < DEV_LOG_MAX_FILES) && (files[i].file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID); i++)
101 {
102 files[i].file_parm.write_cb(&files[i], message, len);
103 }
104}
105
106static void dev_log_save_task_handle_message(bcmos_msg *msg)
107{
108 static char log_string[MAX_DEV_LOG_STRING_SIZE];
109 uint32_t length;
110 char time_str[17];
111 dev_log_queue_msg *receive_msg = msg->data;
112 dev_log_id_parm *log_id = receive_msg->log_id;
113 bcm_dev_log_level msg_level = receive_msg->msg_level;
114
115 /* Build message header */
116 *log_string = '\0';
117 if (!(receive_msg->flags & BCM_LOG_FLAG_NO_HEADER))
118 {
119 length = sizeof(time_str);
120 dev_log.dev_log_parm.time_to_str_cb(receive_msg->time_stamp, time_str, length);
121 snprintf(
122 log_string,
123 sizeof(log_string),
124 "[%s: %c %-20s] ",
125 time_str,
126 log_level_str[msg_level],
127 log_id->name);
128 }
129
130 /* Modify the __FILE__ format so it would print the filename only without the path.
131 * If using BCM_LOG_FLAG_CALLER_FMT, it is done in caller context, because filename might be long, taking a lot of
132 * the space of the message itself. */
133 if ((receive_msg->flags & BCM_LOG_FLAG_FILENAME_IN_FMT) && !(receive_msg->flags & BCM_LOG_FLAG_CALLER_FMT))
134 receive_msg->u.fmt_args.fmt = dev_log_basename(receive_msg->u.fmt_args.fmt);
135
136 if (receive_msg->flags & BCM_LOG_FLAG_CALLER_FMT)
137 {
138 /* Copy user string to buffer */
139 strncat(log_string, receive_msg->u.str, sizeof(log_string) - strlen(log_string) - 1);
140 }
141 else
142 {
143 uint32_t offset = ((long)receive_msg->u.fmt_args.args % 8 == 0) ? 0 : 1; /* start on an 8-byte boundary */
144 va_list ap;
145 *(void **)&ap = &receive_msg->u.fmt_args.args[offset];
146
147 /* Copy user string to buffer */
148 vsnprintf(log_string + strlen(log_string),
149 sizeof(log_string) - strlen(log_string),
150 receive_msg->u.fmt_args.fmt,
151 ap);
152 }
153
154 /* Force last char to be end of string */
155 log_string[MAX_DEV_LOG_STRING_SIZE - 1] = '\0';
156
157 /* At this point, if the message is going to be sent to the print task, save the formatted string then forward it.
158 * Otherwise, we can release the message before writing it to RAM. */
159 if ((log_id->log_type & DEV_LOG_ID_TYPE_PRINT) &&
160 (msg_level <= log_id->log_level_print))
161 {
162 if (!bcm_dev_log_level_is_error(msg_level) &&
163 (receive_msg->flags & BCM_LOG_FLAG_DONT_SKIP_PRINT) != 0 &&
164 bcm_dev_log_pool_occupancy_percent_get() >= DEV_LOG_SKIP_PRINT_THRESHOLD_PERCENT)
165 {
166 log_id->print_skipped_count++;
167 bcmos_msg_free(msg);
168 }
169 else
170 {
171 strcpy(receive_msg->u.str, log_string); /* so the print task doesn't need to re-format it */
172 bcmos_msg_send(&dev_log.print_queue, msg, 0);
173 }
174 }
175 else
176 {
177 bcmos_msg_free(msg);
178 }
179
180 /* Save to file */
181 if ((log_id->log_type & DEV_LOG_ID_TYPE_SAVE) &&
182 (msg_level <= log_id->log_level_save))
183 {
184 bcm_dev_log_file_save_msg(dev_log.files, log_string);
185 }
186}
187
188static void dev_log_print_task_handle_message(bcmos_msg *msg)
189{
190 static char log_string[MAX_DEV_LOG_STRING_SIZE];
191 dev_log_queue_msg *receive_msg = msg->data;
192 dev_log_id_parm *log_id = receive_msg->log_id;
193 bcm_dev_log_level msg_level = receive_msg->msg_level;
194
195 /* make a local copy of the pre-formatted string (it was formatted in the save task) */
196 strcpy(log_string, receive_msg->u.str);
197
198 /* free the message ASAP since printing might take some time */
199 bcmos_msg_free(msg);
200
201 if (log_id->style == BCM_DEV_LOG_STYLE_NORMAL &&
202 dev_log_level2style[msg_level] == BCM_DEV_LOG_STYLE_NORMAL)
203 {
204 dev_log.dev_log_parm.print_cb(dev_log.dev_log_parm.print_cb_arg, "%s", log_string);
205 }
206 else
207 {
208 /* If style was set per log id, then use it. */
209 if (log_id->style != BCM_DEV_LOG_STYLE_NORMAL)
210 {
211 dev_log.dev_log_parm.print_cb(
212 dev_log.dev_log_parm.print_cb_arg,
213 "%s%s%s",
214 dev_log_style_array[log_id->style],
215 log_string,
216 dev_log_style_array[BCM_DEV_LOG_STYLE_NORMAL]);
217 }
218 else
219 {
220 /* Otherwise - style was set per log level. */
221 dev_log.dev_log_parm.print_cb(
222 dev_log.dev_log_parm.print_cb_arg,
223 "%s%s%s",
224 dev_log_style_array[dev_log_level2style[msg_level]],
225 log_string,
226 dev_log_style_array[BCM_DEV_LOG_STYLE_NORMAL]);
227 }
228 }
229}
230
231static int dev_log_print_task(long data)
232{
233 bcmos_msg *m;
234 bcmos_errno error;
235 const char error_str[MAX_DEV_LOG_STRING_SIZE] = "Error: Can't receive from queue - dev_log print task exit\n";
236
237 while (1)
238 {
239 error = bcmos_msg_recv(&dev_log.print_queue, BCMOS_WAIT_FOREVER, &m);
240 if (error != BCM_ERR_OK)
241 {
242 DEV_LOG_ERROR_PRINTF("%s", error_str);
243 dev_log.dev_log_parm.print_cb(dev_log.dev_log_parm.print_cb_arg, "%s", error_str);
244 bcm_dev_log_file_save_msg(dev_log.files, error_str);
245 return (int)error;
246 }
247
248 if (m == &shutdown_msg)
249 {
250 bcmos_msg_free(m);
251 break; /* shut down gracefully */
252 }
253 else
254 {
255 dev_log_print_task_handle_message(m);
256 }
257 }
258
259 bcmos_sem_post(&dev_log.print_task_is_terminated);
260
261 return 0;
262}
263
264static int dev_log_save_task(long data)
265{
266 bcmos_msg *m;
267 bcmos_errno error;
268 const char error_str[MAX_DEV_LOG_STRING_SIZE] = "Error: Can't receive from queue - dev_log save task exit\n";
269
270 while (1)
271 {
272 error = bcmos_msg_recv(&dev_log.save_queue, BCMOS_WAIT_FOREVER, &m);
273 if (error != BCM_ERR_OK)
274 {
275 DEV_LOG_ERROR_PRINTF("%s", error_str);
276 dev_log.dev_log_parm.print_cb(dev_log.dev_log_parm.print_cb_arg, "%s", error_str);
277 bcm_dev_log_file_save_msg(dev_log.files, error_str);
278 return (int)error;
279 }
280
281 if (m == &shutdown_msg)
282 {
283 bcmos_msg_free(m);
284 break; /* shut down gracefully */
285 }
286 else
287 {
288 dev_log_save_task_handle_message(m);
289 }
290 }
291
292 bcmos_sem_post(&dev_log.save_task_is_terminated);
293
294 return 0;
295}
296
297static int default_time_to_str(uint32_t time_stamp, char *time_str, int time_str_size)
298{
299 return snprintf(time_str, time_str_size, "%u", time_stamp);
300}
301
302static uint32_t default_get_time(void)
303{
304 return 0;
305}
306
307static int default_print(void *arg, const char *format, ...)
308{
309 va_list args;
310
311 va_start(args, format);
312 bcmos_vprintf(format, args);
313 va_end(args);
314 return 0;
315}
316
317static bcmos_errno default_open_callback_cond_reset(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file, bcmos_bool is_rewind)
318{
319 bcmos_errno err;
320
321 if (!file->file_parm.start_addr || file->file_parm.size <= MEM_FILE_HDR_SIZE)
322 return BCM_ERR_PARM;
323
324 /* Create file mutex */
325 err = bcmos_mutex_create(&file->u.mem_file.mutex, 0, NULL);
326 if (err != BCM_ERR_OK)
327 {
328 DEV_LOG_ERROR_PRINTF("Error: Can't create mutex (error: %d)\n", (int)err);
329 return err;
330 }
331
332 /* Check file magic string */
333 memcpy(&file->u.mem_file.file_header, (uint8_t *)file->file_parm.start_addr, MEM_FILE_HDR_SIZE);
334 if (memcmp(FILE_MAGIC_STR, file->u.mem_file.file_header.file_magic, FILE_MAGIC_STR_SIZE))
335 {
336 DEV_LOG_INFO_PRINTF("No file magic string - file is empty/corrupt\n");
337 if (!is_rewind || !file->file_parm.rewind_cb)
338 {
339 bcmos_mutex_destroy(&file->u.mem_file.mutex);
340 return BCM_ERR_NOENT;
341 }
342 return file->file_parm.rewind_cb(file);
343 }
344
345 DEV_LOG_INFO_PRINTF("Attached to existing file\n");
346
347 return BCM_ERR_OK;
348}
349
350static bcmos_errno default_open_callback(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file)
351{
352 return default_open_callback_cond_reset(file_parm, file, BCMOS_TRUE);
353}
354
355static bcmos_errno default_close_callback(bcm_dev_log_file *file)
356{
357 bcmos_mutex_destroy(&file->u.mem_file.mutex);
358 return BCM_ERR_OK;
359}
360
361/* Look for start of msg character */
362static int get_msg_start_offset(bcm_dev_log_file *file, uint32_t offset, uint32_t max_len)
363{
364 uint8_t *buf, *msg;
365
366 buf = (uint8_t *)file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE;
367 msg = memchr(buf, DEV_LOG_MSG_START_CHAR, max_len);
368 if (!msg)
369 return -1;
370 return (msg - buf + 1);
371}
372
373/* Look for 0-terminator */
374static int get_msg_end_offset(bcm_dev_log_file *file, uint32_t offset, uint32_t max_len)
375{
376 uint8_t *buf, *end;
377
378 buf = (uint8_t *)file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE;
379 end = memchr(buf, 0, max_len);
380 if (!end)
381 return -1;
382 return (end - buf + 1);
383}
384
385static int default_read_callback(bcm_dev_log_file *log_file, uint32_t *p_offset, void *buf, uint32_t length)
386{
387 uint32_t offset = *p_offset;
388 uint32_t real_length = 0;
389 int start, end;
390
391 /* If file wrapped around, offset starts from write_offset, otherwise,
392 * it starts from the beginning
393 */
394 if (log_file->u.mem_file.file_header.file_wrap_cnt)
395 {
396 if (offset + length >= log_file->u.mem_file.file_header.data_size)
397 {
398 length = log_file->u.mem_file.file_header.data_size - offset;
399 if (length <= 0)
400 return 0;
401 }
402 offset += log_file->u.mem_file.file_header.write_offset;
403 if (offset >= log_file->u.mem_file.file_header.data_size)
404 offset -= log_file->u.mem_file.file_header.data_size;
405 /* Find beginning of the next message */
406 start = get_msg_start_offset(log_file, offset, LOG_MIN(log_file->u.mem_file.file_header.data_size - offset,
407 MAX_DEV_LOG_STRING_SIZE));
408 if (start < 0)
409 {
410 /* Didn't find any up to the end of buffer. Look from the start */
411 offset = 0;
412 if (!log_file->u.mem_file.file_header.write_offset)
413 return 0;
414 start = get_msg_start_offset(log_file, 0, LOG_MIN(log_file->u.mem_file.file_header.write_offset - 1,
415 MAX_DEV_LOG_STRING_SIZE));
416 if (start <= 0)
417 return 0;
418 }
419 offset += start;
420 }
421 else
422 {
423 /* Start beginning of message. Without wrap-around it should be right at the offset,
424 * so, it is just a precaution */
425 start = get_msg_start_offset(log_file, offset, LOG_MIN(log_file->u.mem_file.file_header.data_size - offset,
426 MAX_DEV_LOG_STRING_SIZE));
427 if (start <= 0)
428 return 0;
429 offset += start;
430 /* Stop reading when reached write_offset */
431 if (offset + length >= log_file->u.mem_file.file_header.write_offset)
432 {
433 if (offset >= log_file->u.mem_file.file_header.write_offset)
434 return 0;
435 length = log_file->u.mem_file.file_header.write_offset - offset;
436 }
437 }
438
439 /* We have the beginning. Now find the end of message and copy to the user
440 * buffer if there is enough room */
441 if (offset + length > log_file->u.mem_file.file_header.data_size)
442 {
443 uint32_t length1 = offset + length - log_file->u.mem_file.file_header.data_size; /* tail length */
444 length -= length1;
445 end = get_msg_end_offset(log_file, offset, length);
446 if (end >= 0)
447 {
448 memcpy(buf, (uint8_t *)log_file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE, end);
449 *p_offset += end + start;
450 return end;
451 }
452
453 /* Didn't find end of message in the end of file data buffer. wrap around */
454 memcpy(buf, (uint8_t *)log_file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE, length);
455 real_length = length;
456 buf = (uint8_t *)buf + length;
457 length = length1;
458 offset = 0;
459 }
460 end = get_msg_end_offset(log_file, offset, MAX_DEV_LOG_STRING_SIZE);
461 if (end <= 0)
462 {
463 /* something is wrong. msg is not terminated */
464 return 0;
465 }
466 /* If there is no room for the whole message - return overflow */
467 if (end > length)
468 return (int)BCM_ERR_OVERFLOW;
469
470 memcpy(buf, (uint8_t *)log_file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE, end);
471 real_length += end;
472
473 *p_offset += real_length + start;
474
475 return real_length;
476}
477
478static int get_num_of_overwritten_messages(uint8_t *buf, uint32_t length)
479{
480 uint8_t *p;
481 int n = 0;
482
483 do
484 {
485 p = memchr(buf, DEV_LOG_MSG_START_CHAR, length);
486 if (p == NULL)
487 break;
488 ++n;
489 length -= (p + 1 - buf);
490 buf = p + 1;
491 } while(length);
492
493 return n;
494}
495
496static int default_write_callback(bcm_dev_log_file *file, const void *buf, uint32_t length)
497{
498 uint32_t offset, next_offset;
499 uint8_t *p;
500 int n_overwritten = 0;
501
502 if ((file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_STOP_WHEN_FULL) && file->is_full)
503 return 0;
504
505 if (!length)
506 return 0;
507
508 bcmos_mutex_lock(&file->u.mem_file.mutex);
509
510 offset = file->u.mem_file.file_header.write_offset;
511 next_offset = offset + length + 1; /* 1 extra character for record delimiter */
512
513 /* Handle overflow */
514 if (next_offset >= file->u.mem_file.file_header.data_size)
515 {
516 uint32_t tail_length;
517
518 /* Split buffer in 2 */
519 tail_length = next_offset - file->u.mem_file.file_header.data_size; /* 2nd segment length */
520
521 if ((file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_STOP_WHEN_FULL) && tail_length)
522 {
523 file->is_full = BCMOS_TRUE;
524 bcmos_mutex_unlock(&file->u.mem_file.mutex);
525 return 0;
526 }
527
528 length -= tail_length;
529 /* "if (next_offset >= file->u.mem_file.file_header.data_size)" condition
530 * guarantees that there is room for at least 1 character in the end of file */
531 p = (uint8_t *)file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE;
532 if (file->u.mem_file.file_header.file_wrap_cnt)
533 n_overwritten += get_num_of_overwritten_messages(p, length + 1);
534 *p = DEV_LOG_MSG_START_CHAR;
535 if (length)
536 {
537 memcpy(p + 1, buf, length);
538 buf = (const uint8_t *)buf + length;
539 }
540 if (tail_length)
541 {
542 p = (uint8_t *)file->file_parm.start_addr + MEM_FILE_HDR_SIZE;
543 if (file->u.mem_file.file_header.file_wrap_cnt)
544 n_overwritten += get_num_of_overwritten_messages(p, tail_length);
545 memcpy(p, buf, tail_length);
546 ++file->u.mem_file.file_header.file_wrap_cnt;
547 }
548 next_offset = tail_length;
549 }
550 else
551 {
552 p = (uint8_t *)file->file_parm.start_addr + MEM_FILE_HDR_SIZE + offset;
553 if (file->u.mem_file.file_header.file_wrap_cnt)
554 n_overwritten += get_num_of_overwritten_messages(p, length + 1);
555 *(p++) = DEV_LOG_MSG_START_CHAR;
556 memcpy(p, buf, length);
557 }
558 file->u.mem_file.file_header.write_offset = next_offset;
559 file->u.mem_file.file_header.num_msgs += (1 - n_overwritten);
560
561 /* update the header */
562 memcpy((uint8_t *)file->file_parm.start_addr, &file->u.mem_file.file_header, MEM_FILE_HDR_SIZE);
563
564 /* send almost full indication if necessary */
565 if (file->almost_full.send_ind_cb &&
566 !file->almost_full.ind_sent &&
567 file->u.mem_file.file_header.write_offset > file->almost_full.threshold)
568 {
569 file->almost_full.ind_sent = (file->almost_full.send_ind_cb(file->almost_full.priv) == BCM_ERR_OK);
570 }
571
572 bcmos_mutex_unlock(&file->u.mem_file.mutex);
573
574 return length;
575}
576
577static bcmos_errno default_rewind_callback(bcm_dev_log_file *file)
578{
579 bcmos_mutex_lock(&file->u.mem_file.mutex);
580
581 DEV_LOG_INFO_PRINTF("Clear file\n");
582 file->u.mem_file.file_header.file_wrap_cnt = 0;
583 file->u.mem_file.file_header.write_offset = 0;
584 file->u.mem_file.file_header.data_size = file->file_parm.size - MEM_FILE_HDR_SIZE;
585 file->u.mem_file.file_header.num_msgs = 0;
586 strcpy(file->u.mem_file.file_header.file_magic, FILE_MAGIC_STR);
587 memcpy((uint8_t *)file->file_parm.start_addr, &file->u.mem_file.file_header, MEM_FILE_HDR_SIZE);
588 file->almost_full.ind_sent = BCMOS_FALSE;
589
590 bcmos_mutex_unlock(&file->u.mem_file.mutex);
591
592 return BCM_ERR_OK;
593}
594
595
596static void set_default_file_callbacks(bcm_dev_log_file *file)
597{
598 file->file_parm.open_cb = default_open_callback;
599 file->file_parm.close_cb = default_close_callback;
600 file->file_parm.read_cb = default_read_callback;
601 file->file_parm.write_cb = default_write_callback;
602 file->file_parm.rewind_cb = default_rewind_callback;
603}
604
605static inline bcmos_bool bcm_dev_log_is_memory_file(bcm_dev_log_file *file)
606{
607 return (file->file_parm.open_cb == default_open_callback);
608}
609
610bcmos_errno bcm_dev_log_file_clear(bcm_dev_log_file *file)
611{
612 if (!file || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID))
613 return BCM_ERR_PARM;
614
615 if (!file->file_parm.rewind_cb)
616 return BCM_ERR_NOT_SUPPORTED;
617
618 return file->file_parm.rewind_cb(file);
619}
620
621/* File index to file handle */
622bcm_dev_log_file *bcm_dev_log_file_get(uint32_t file_index)
623{
624 bcm_dev_log_file *file = &dev_log.files[file_index];
625
626 if ((file_index >= DEV_LOG_MAX_FILES) || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID))
627 return NULL;
628 return file;
629}
630
631/* Read from file */
632int bcm_dev_log_file_read(bcm_dev_log_file *file, uint32_t *offset, char *buf, uint32_t buf_len)
633{
634 int len;
635 if (!file || !buf || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID))
636 return (int)BCM_ERR_PARM;
637
638 len = file->file_parm.read_cb(file, offset, buf, buf_len);
639 /* If nothing more to read and CLEAR_AFTER_READ mode, read again under lock and clear if no new records */
640 if (!len && bcm_dev_log_is_memory_file(file) && (file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_CLEAR_AFTER_READ))
641 {
642 bcmos_mutex_lock(&file->u.mem_file.mutex);
643 len = file->file_parm.read_cb(file, offset, buf, buf_len);
644 if (!len)
645 file->file_parm.rewind_cb(file);
646 bcmos_mutex_unlock(&file->u.mem_file.mutex);
647 }
648 return len;
649}
650
651/* Attach file to memory buffer */
652bcmos_errno bcm_dev_log_file_attach(void *buf, uint32_t buf_len, bcm_dev_log_file *file)
653{
654 bcmos_errno rc;
655 dev_log_mem_file_header *hdr = (dev_log_mem_file_header *)buf;
656
657 if (!buf || !file || buf_len <= MEM_FILE_HDR_SIZE)
658 return BCM_ERR_PARM;
659
660 if (memcmp(FILE_MAGIC_STR, hdr->file_magic, FILE_MAGIC_STR_SIZE))
661 return BCM_ERR_NOENT;
662
663#if DEV_LOG_ENDIAN != BCM_CPU_ENDIAN
664 hdr->file_wrap_cnt = bcmos_endian_swap_u32(hdr->file_wrap_cnt);
665 hdr->write_offset = bcmos_endian_swap_u32(hdr->write_offset);
666 hdr->data_size = bcmos_endian_swap_u32(hdr->data_size);
667 hdr->num_msgs = bcmos_endian_swap_u32(hdr->num_msgs);
668#endif
669
670 memset(file, 0, sizeof(*file));
671 file->file_parm.start_addr = buf;
672 file->file_parm.size = buf_len;
673 file->file_parm.flags = BCM_DEV_LOG_FILE_FLAG_VALID;
674 set_default_file_callbacks(file);
675 rc = default_open_callback_cond_reset(&file->file_parm, file, BCMOS_FALSE);
676 if (rc)
677 return rc;
678
679 if (!file->u.mem_file.file_header.num_msgs)
680 return BCM_ERR_NOENT;
681
682 return BCM_ERR_OK;
683}
684
685/* Detach file handle from memory buffer */
686bcmos_errno bcm_dev_log_file_detach(bcm_dev_log_file *file)
687{
688 if (!file || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID))
689 return BCM_ERR_PARM;
690 file->file_parm.flags = BCM_DEV_LOG_FILE_FLAG_VALID;
691 bcmos_mutex_destroy(&file->u.mem_file.mutex);
692 return BCM_ERR_OK;
693}
694
695
696#ifdef BCM_OS_POSIX
697 /* Regular file callbacks */
698
699/****************************************************************************************/
700/* OPEN CALLBACK: open memory/file */
701/* file_parm - file parameters */
702/* file - file */
703/****************************************************************************************/
704static bcmos_errno _dev_log_reg_file_open(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file)
705{
706 bcmos_errno err = BCM_ERR_OK;
707
708 file->file_parm = *file_parm;
709 file->u.reg_file_handle = fopen((char *)file_parm->udef_parms, "w+");
710 if (!file->u.reg_file_handle)
711 {
712 bcmos_printf("DEV_LOG: can't open file %s for writing\n", (char *)file_parm->udef_parms);
713 err = BCM_ERR_IO;
714 }
715 return err;
716}
717
718/****************************************************************************************/
719/* CLOSE CALLBACK: close memory/file */
720/* file - file handle */
721/****************************************************************************************/
722static bcmos_errno _dev_log_reg_file_close(bcm_dev_log_file *file)
723{
724 if (file->u.reg_file_handle)
725 {
726 fclose(file->u.reg_file_handle);
727 file->u.reg_file_handle = NULL;
728 }
729 return BCM_ERR_OK;
730}
731
732/****************************************************************************************/
733/* REWIND CALLBACK: clears memory/file */
734/* file - file handle */
735/****************************************************************************************/
736static bcmos_errno _dev_log_reg_file_rewind(bcm_dev_log_file *file)
737{
738 bcmos_errno err = BCM_ERR_OK;
739 FILE *f;
740
741 if (file->u.reg_file_handle)
742 {
743 f = freopen((const char *)file->file_parm.udef_parms, "w+", file->u.reg_file_handle);
744 if (NULL != f)
745 {
746 file->u.reg_file_handle = f;
747 }
748 else
749 {
750 err = BCM_ERR_IO;
751 bcmos_printf("DEV_LOG: can't open file %s for writing\n", (char *)file->file_parm.udef_parms);
752 }
753
754 }
755 return err;
756}
757
758/****************************************************************************************/
759/* READ_CALLBACK: read form memory/file */
760/* offset - the offset in bytes to read from, output */
761/* buf - Where to put the result */
762/* length - Buffer length */
763/* This function should return the number of bytes actually read from file or 0 if EOF */
764/****************************************************************************************/
765static int _dev_log_reg_file_read(bcm_dev_log_file *file, uint32_t *offset, void *buf, uint32_t length)
766{
767 int n = 0;
768 if (file->u.reg_file_handle)
769 {
770 if (!fseek(file->u.reg_file_handle, SEEK_SET, *offset))
771 n = fread(buf, 1, length, file->u.reg_file_handle);
772 *offset = ftell(file->u.reg_file_handle);
773 }
774 return (n < 0) ? 0 : n;
775}
776
777/****************************************************************************************/
778/* WRITE_CALLBACK: write form memory/file */
779/* buf - The buffer that should be written */
780/* length - The number of bytes to write */
781/* This function should return the number of bytes actually written to file or 0 if EOF */
782/****************************************************************************************/
783static int _dev_log_reg_file_write(bcm_dev_log_file *file, const void *buf, uint32_t length)
784{
785 const char *cbuf = (const char *)buf;
786 int n = 0;
787 if (file->u.reg_file_handle)
788 {
789 /* Remove 0 terminator */
790 if (length && !cbuf[length-1])
791 --length;
792 n = fwrite(buf, 1, length, file->u.reg_file_handle);
793 fflush(file->u.reg_file_handle);
794 }
795 return n;
796}
797
798static void set_regular_file_callbacks(bcm_dev_log_file *file)
799{
800 file->file_parm.open_cb = _dev_log_reg_file_open;
801 file->file_parm.close_cb = _dev_log_reg_file_close;
802 file->file_parm.read_cb = _dev_log_reg_file_read;
803 file->file_parm.write_cb = _dev_log_reg_file_write;
804 file->file_parm.rewind_cb = _dev_log_reg_file_rewind;
805}
806
807#endif /* #ifdef BCM_OS_POSIX */
808
809#ifdef DEV_LOG_SYSLOG
810 /* linux syslog integration */
811/****************************************************************************************/
812/* OPEN CALLBACK: open syslog interface */
813/* file_parm->udef_parm contains optional log ident */
814/****************************************************************************************/
815static bcmos_errno _dev_log_syslog_file_open(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file)
816{
817 file->file_parm = *file_parm;
818 openlog((const char *)file_parm->udef_parms, 0, LOG_USER);
819 return BCM_ERR_OK;
820}
821
822/****************************************************************************************/
823/* CLOSE CALLBACK: close syslog interface */
824/****************************************************************************************/
825static bcmos_errno _dev_log_syslog_file_close(bcm_dev_log_file *file)
826{
827 closelog();
828 return BCM_ERR_OK;
829}
830
831/****************************************************************************************/
832/* REWIND CALLBACK: not supported by syslog. Return OK to prevent request failure */
833/****************************************************************************************/
834static bcmos_errno _dev_log_syslog_file_rewind(bcm_dev_log_file *file)
835{
836 return BCM_ERR_OK;
837}
838
839/****************************************************************************************/
840/* READ_CALLBACK: reading from syslog is not supported */
841/****************************************************************************************/
842static int _dev_log_syslog_file_read(bcm_dev_log_file *file, uint32_t *offset, void *buf, uint32_t length)
843{
844 return 0;
845}
846
847/****************************************************************************************/
848/* WRITE_CALLBACK: write to syslog */
849/* buf - The buffer that should be written */
850/* length - The number of bytes to write */
851/* This function should return the number of bytes actually written to file or 0 if EOF */
852/****************************************************************************************/
853static int _dev_log_syslog_file_write(bcm_dev_log_file *file, const void *buf, uint32_t length)
854{
855 const char *cbuf = (const char *)buf;
856 static int log_priority = LOG_DEBUG;
857
858 /* Identify log lovel */
859 if (cbuf && cbuf[0] == '[')
860 {
861 const char *clevel = strchr(cbuf, ' ');
862 if (clevel)
863 {
864 switch (*(clevel + 1))
865 {
866 case 'F':
867 log_priority = LOG_CRIT;
868 break;
869 case 'E':
870 log_priority = LOG_ERR;
871 break;
872 case 'W':
873 log_priority = LOG_WARNING;
874 break;
875 case 'I':
876 log_priority = LOG_INFO;
877 break;
878 case 'D':
879 log_priority = LOG_DEBUG;
880 break;
881 default:
882 break;
883 }
884 }
885 }
886 syslog(log_priority, "%s", cbuf);
887 return length;
888}
889
890static void set_syslog_file_callbacks(bcm_dev_log_file *file)
891{
892 file->file_parm.open_cb = _dev_log_syslog_file_open;
893 file->file_parm.close_cb = _dev_log_syslog_file_close;
894 file->file_parm.read_cb = _dev_log_syslog_file_read;
895 file->file_parm.write_cb = _dev_log_syslog_file_write;
896 file->file_parm.rewind_cb = _dev_log_syslog_file_rewind;
897}
898
899#endif /* #ifdef DEV_LOG_SYSLOG */
900
901static bcmos_errno bcm_dev_log_create(
902 const bcm_dev_log_parm *dev_log_parm,
903 const bcmos_task_parm *save_task_parm,
904 const bcmos_task_parm *print_task_parm,
905 const bcmos_msg_queue_parm *save_queue_parm,
906 const bcmos_msg_queue_parm *print_queue_parm,
907 const bcmos_msg_pool_parm *pool_parm)
908{
909 bcmos_errno err;
910 int i;
911
912 if (!dev_log_parm)
913 return BCM_ERR_PARM;
914
915 if (dev_log.state != BCM_DEV_LOG_STATE_UNINITIALIZED)
916 return BCM_ERR_ALREADY;
917
918 /* Set user callbacks */
919 dev_log.dev_log_parm = *dev_log_parm;
920 if (!dev_log.dev_log_parm.print_cb)
921 dev_log.dev_log_parm.print_cb = default_print;
922 if (!dev_log.dev_log_parm.get_time_cb)
923 dev_log.dev_log_parm.get_time_cb = default_get_time;
924 if (!dev_log.dev_log_parm.time_to_str_cb)
925 dev_log.dev_log_parm.time_to_str_cb = default_time_to_str;
926
927 for (i = 0; (i < DEV_LOG_MAX_FILES) && (dev_log_parm->log_file[i].flags & BCM_DEV_LOG_FILE_FLAG_VALID); i++)
928 {
929 /* Copy log files */
930 dev_log.files[i].file_parm = dev_log_parm->log_file[i];
931 if (!dev_log.files[i].file_parm.open_cb)
932 {
933 if (dev_log.files[i].file_parm.type == BCM_DEV_LOG_FILE_MEMORY)
934 set_default_file_callbacks(&dev_log.files[i]);
935#ifdef BCM_OS_POSIX
936 else if (dev_log.files[i].file_parm.type == BCM_DEV_LOG_FILE_REGULAR)
937 set_regular_file_callbacks(&dev_log.files[i]);
938#endif
939#ifdef DEV_LOG_SYSLOG
940 else if (dev_log.files[i].file_parm.type == BCM_DEV_LOG_FILE_SYSLOG)
941 set_syslog_file_callbacks(&dev_log.files[i]);
942#endif
943 else
944 {
945 DEV_LOG_ERROR_PRINTF("file%d: open_cb must be set for user-defined file\n\n", i);
946 continue;
947 }
948 }
949 err = dev_log.files[i].file_parm.open_cb(&dev_log.files[i].file_parm, &dev_log.files[i]);
950 if (err)
951 {
952 DEV_LOG_ERROR_PRINTF("file%d: open failed: %s (%d)\n\n", i, bcmos_strerror(err), err );
953 continue;
954 }
955 DEV_LOG_INFO_PRINTF("file: start_addr: 0x%p, size: %u, max msg size %u\n",
956 dev_log.files[i].file_parm.start_addr, dev_log.files[i].file_parm.size, MAX_DEV_LOG_STRING_SIZE);
957 }
958
959 /* Create pool */
960 err = bcmos_msg_pool_create(&dev_log.pool, pool_parm);
961 if (err != BCM_ERR_OK)
962 {
963 DEV_LOG_ERROR_PRINTF("Error: Can't create pool (error: %d)\n", (int)err);
964 return err;
965 }
966
967 /* Create message queues */
968 err = bcmos_msg_queue_create(&dev_log.save_queue, save_queue_parm);
969 if (err != BCM_ERR_OK)
970 {
971 DEV_LOG_ERROR_PRINTF("(%s) Error: Can't create save queue (error: %d)\n", __FUNCTION__, (int)err);
972 return err;
973 }
974 err = bcmos_msg_queue_create(&dev_log.print_queue, print_queue_parm);
975 if (err != BCM_ERR_OK)
976 {
977 DEV_LOG_ERROR_PRINTF("(%s) Error: Can't create print queue (error: %d)\n", __FUNCTION__, (int)err);
978 return err;
979 }
980
981 /* Create tasks */
982 err = bcmos_task_create(&dev_log.save_task, save_task_parm);
983 if (err != BCM_ERR_OK)
984 {
985 DEV_LOG_ERROR_PRINTF("Error: Can't spawn save task (error: %d)\n", (int)err);
986 return err;
987 }
988 err = bcmos_task_create(&dev_log.print_task, print_task_parm);
989 if (err != BCM_ERR_OK)
990 {
991 DEV_LOG_ERROR_PRINTF("Error: Can't spawn print task (error: %d)\n", (int)err);
992 return err;
993 }
994
995 bcmos_sem_create(&dev_log.save_task_is_terminated, 0, 0, "save_task_sem");
996 bcmos_sem_create(&dev_log.print_task_is_terminated, 0, 0, "print_task_sem");
997
998 dev_log.state = BCM_DEV_LOG_STATE_ENABLED;
999
1000 return BCM_ERR_OK;
1001}
1002
1003void bcm_dev_log_destroy(void)
1004{
1005 bcmos_errno err;
1006 uint32_t i;
1007 bcmos_msg_send_flags msg_flags;
1008
1009 if (dev_log.state == BCM_DEV_LOG_STATE_UNINITIALIZED)
1010 {
1011 return;
1012 }
1013
1014 /* Send a shutdown message to each task. When received by the main loops, it will tear down the tasks gracefully.
1015 * If the flag is set to tear down immediately, we'll send an URGENT teardown message, so it'll be handled before
1016 * all oustanding log messages (the oustanding log messages will be freed during bcmos_msg_queue_destroy). */
1017 msg_flags = BCMOS_MSG_SEND_NOLIMIT;
1018 if ((dev_log.flags & BCM_DEV_LOG_FLAG_DESTROY_IMMEDIATELY) != 0)
1019 msg_flags |= BCMOS_MSG_SEND_URGENT;
1020
1021 /* The save task depends on the print task, so we terminate the save task first. */
1022 bcmos_msg_send(&dev_log.save_queue, &shutdown_msg, msg_flags);
1023 bcmos_sem_wait(&dev_log.save_task_is_terminated, BCMOS_WAIT_FOREVER);
1024
1025 bcmos_msg_send(&dev_log.print_queue, &shutdown_msg, msg_flags);
1026 bcmos_sem_wait(&dev_log.print_task_is_terminated, BCMOS_WAIT_FOREVER);
1027
1028 bcmos_sem_destroy(&dev_log.save_task_is_terminated);
1029 bcmos_sem_destroy(&dev_log.print_task_is_terminated);
1030
1031 err = bcmos_task_destroy(&dev_log.save_task);
1032 if (err != BCM_ERR_OK)
1033 {
1034 DEV_LOG_ERROR_PRINTF("Error: Can't destroy dev_log save task (error: %d)\n", (int)err);
1035 }
1036
1037 err = bcmos_task_destroy(&dev_log.print_task);
1038 if (err != BCM_ERR_OK)
1039 {
1040 DEV_LOG_ERROR_PRINTF("Error: Can't destroy dev_log print task (error: %d)\n", (int)err);
1041 }
1042
1043 err = bcmos_msg_queue_destroy(&dev_log.save_queue);
1044 if (err != BCM_ERR_OK)
1045 {
1046 DEV_LOG_ERROR_PRINTF("Error: Can't destroy save queue (error: %d)\n", (int)err);
1047 }
1048
1049 err = bcmos_msg_queue_destroy(&dev_log.print_queue);
1050 if (err != BCM_ERR_OK)
1051 {
1052 DEV_LOG_ERROR_PRINTF("Error: Can't destroy print queue (error: %d)\n", (int)err);
1053 }
1054
1055 err = bcmos_msg_pool_destroy(&dev_log.pool);
1056 if (err != BCM_ERR_OK)
1057 {
1058 DEV_LOG_ERROR_PRINTF("Error: Can't destroy pool (error: %d)\n", (int)err);
1059 }
1060
1061 for (i = 0; (i < DEV_LOG_MAX_FILES) && (dev_log.dev_log_parm.log_file[i].flags & BCM_DEV_LOG_FILE_FLAG_VALID); i++)
1062 {
1063 dev_log.files[i].file_parm.close_cb(&dev_log.files[i]);
1064 }
1065
1066 dev_log.state = BCM_DEV_LOG_STATE_UNINITIALIZED;
1067}
1068
1069typedef struct
1070{
1071 bcmos_msg msg;
1072 dev_log_queue_msg log_queue_msg;
1073 bcmos_bool lock;
1074 bcmos_fastlock fastlock;
1075} bcm_dev_log_drop_msg;
1076
1077typedef struct
1078{
1079 bcm_dev_log_drop_msg msg;
1080 uint32_t drops;
1081 uint32_t reported_drops;
1082 bcmos_timer timer;
1083 uint32_t last_report_timestamp;
1084} bcm_dev_log_drop;
1085
1086static bcm_dev_log_drop dev_log_drop;
1087
1088static void bcm_dev_log_log_message_release_drop(bcmos_msg *m)
1089{
1090 unsigned long flags;
1091
1092 flags = bcmos_fastlock_lock(&dev_log_drop.msg.fastlock);
1093 dev_log_drop.msg.lock = BCMOS_FALSE;
1094 bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags);
1095
1096 /* Schedule a timer to report extra drops, because we may not meet DEV_LOG_DROP_REPORT_DROP_THRESHOLD soon. */
1097 bcmos_timer_start(&dev_log_drop.timer, DEV_LOG_DROP_REPORT_RATE_US);
1098}
1099
1100static void _bcm_dev_log_drop_report(void)
1101{
1102 bcmos_msg *msg;
1103 dev_log_queue_msg *log_queue_msg;
1104
1105 msg = &dev_log_drop.msg.msg;
1106 msg->release = bcm_dev_log_log_message_release_drop;
1107 log_queue_msg = &dev_log_drop.msg.log_queue_msg;
1108 log_queue_msg->flags = BCM_LOG_FLAG_DONT_SKIP_PRINT;
1109 log_queue_msg->time_stamp = dev_log.dev_log_parm.get_time_cb();
1110 log_queue_msg->msg_level = DEV_LOG_LEVEL_ERROR;
1111 log_queue_msg->u.fmt_args.fmt = "Log message pool exhausted, dropped message count=%u\n";
1112 log_queue_msg->u.fmt_args.args[0] = (const void *)(unsigned long)(dev_log_drop.drops - dev_log_drop.reported_drops);
1113 log_queue_msg->u.fmt_args.args[1] =
1114 log_queue_msg->u.fmt_args.args[0]; /* args[0] may or may not be skipped depending on alignment */
1115 log_queue_msg->log_id = (dev_log_id_parm *)def_log_id;
1116 log_queue_msg->time_stamp = dev_log.dev_log_parm.get_time_cb();
1117 dev_log_drop.reported_drops = dev_log_drop.drops;
1118 dev_log_drop.last_report_timestamp = bcmos_timestamp();
1119
1120 msg->data = log_queue_msg;
1121 msg->size = sizeof(*log_queue_msg);
1122 bcmos_msg_send(&dev_log.save_queue, msg, 0);
1123}
1124
1125void bcm_dev_log_drop_report(void)
1126{
1127 unsigned long flags;
1128
1129 dev_log_drop.drops++;
1130
1131 flags = bcmos_fastlock_lock(&dev_log_drop.msg.fastlock);
1132 /* The 1st drop report will be done immediately, because although DEV_LOG_DROP_REPORT_DROP_THRESHOLD isn't reached, the time difference is greater than
1133 * DEV_LOG_DROP_REPORT_RATE_US. */
1134 if (dev_log_drop.msg.lock ||
1135 ((dev_log_drop.drops - dev_log_drop.reported_drops < DEV_LOG_DROP_REPORT_DROP_THRESHOLD) &&
1136 (bcmos_timestamp() - dev_log_drop.last_report_timestamp < DEV_LOG_DROP_REPORT_RATE_US)))
1137 {
1138 bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags);
1139 return;
1140 }
1141
1142 dev_log_drop.msg.lock = BCMOS_TRUE;
1143 bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags);
1144
1145 /* Do the actual report. */
1146 _bcm_dev_log_drop_report();
1147}
1148
1149static bcmos_timer_rc bcm_dev_log_drop_timer_cb(bcmos_timer *timer, long data)
1150{
1151 unsigned long flags;
1152
1153 flags = bcmos_fastlock_lock(&dev_log_drop.msg.fastlock);
1154 if (dev_log_drop.msg.lock)
1155 {
1156 bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags);
1157 /* Logger task hasn't released the lock yet (the drop message hasn't been reported yet) -> reschedule the timer. */
1158 bcmos_timer_start(&dev_log_drop.timer, DEV_LOG_DROP_REPORT_RATE_US);
1159 }
1160 else
1161 {
1162 if (dev_log_drop.drops == dev_log_drop.reported_drops)
1163 {
1164 /* No new drops to report. */
1165 bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags);
1166 return BCMOS_TIMER_OK;
1167 }
1168 dev_log_drop.msg.lock = BCMOS_TRUE;
1169 bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags);
1170
1171 /* Do the actual report. */
1172 _bcm_dev_log_drop_report();
1173 }
1174
1175 return BCMOS_TIMER_OK;
1176}
1177
1178bcmos_errno bcm_dev_log_init_default_logger_ext(
1179 bcm_dev_log_parm *dev_log_parm,
1180 uint32_t num_files,
1181 uint32_t stack_size,
1182 bcmos_task_priority priority,
1183 uint32_t pool_size)
1184{
1185 bcmos_errno err;
1186 bcmos_task_parm save_task_parm = {0};
1187 bcmos_task_parm print_task_parm = {0};
1188 bcmos_msg_queue_parm print_queue_parm = {0};
1189 bcmos_msg_queue_parm save_queue_parm = {0};
1190 bcmos_msg_pool_parm pool_parm = {0};
1191 bcmos_timer_parm timer_params =
1192 {
1193 .name = "dev_log_drop_timer",
1194 .owner = BCMOS_MODULE_ID_NONE, /* Will be called in ISR context */
1195 .handler = bcm_dev_log_drop_timer_cb,
1196 };
1197
1198 if (!dev_log_parm)
1199 {
1200 DEV_LOG_ERROR_PRINTF("Error: dev_log_parm must be set\n");
1201 return BCM_ERR_PARM;
1202 }
1203
1204 save_task_parm.priority = priority;
1205 save_task_parm.stack_size = stack_size;
1206 save_task_parm.name = "dev_log_save";
1207 save_task_parm.handler = dev_log_save_task;
1208 save_task_parm.data = 0;
1209
1210 print_task_parm.priority = priority;
1211 print_task_parm.stack_size = stack_size;
1212 print_task_parm.name = "dev_log_print";
1213 print_task_parm.handler = dev_log_print_task;
1214 print_task_parm.data = 0;
1215
1216 /* It is important to avoid terminating logger task during an exception, as logger task is low priority and might have backlog of unhandled messages.
1217 * Even if as a result of the exception the logger task will be in a deadlock waiting for a resource (semaphore/mutex/timer), it is in lower priority than CLI and thus should not block
1218 * CLI. */
1219 save_task_parm.flags = BCMOS_TASK_FLAG_NO_SUSPEND_ON_OOPS;
1220 print_task_parm.flags = BCMOS_TASK_FLAG_NO_SUSPEND_ON_OOPS;
1221
1222 save_queue_parm.name = "dev_log_save";
1223 save_queue_parm.size = 0; /* unlimited */
1224 print_queue_parm.name = "dev_log_print";
1225 print_queue_parm.size = 0; /* unlimited */
1226
1227 pool_parm.name = "dev_log";
1228 pool_parm.size = pool_size;
1229 pool_parm.data_size = sizeof(dev_log_queue_msg);
1230
1231 def_log_id = bcm_dev_log_id_register("default", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
1232 err = bcmos_timer_create(&dev_log_drop.timer, &timer_params);
1233 BCMOS_TRACE_CHECK_RETURN(err, err, "bcmos_timer_create");
1234 bcmos_fastlock_init(&dev_log_drop.msg.fastlock, 0);
1235
1236 if (!dev_log_parm->get_time_cb)
1237 dev_log_parm->get_time_cb = bcmos_timestamp;
1238
1239 err = bcm_dev_log_create(
1240 dev_log_parm,
1241 &save_task_parm,
1242 &print_task_parm,
1243 &save_queue_parm,
1244 &print_queue_parm,
1245 &pool_parm);
1246 BCMOS_TRACE_CHECK_RETURN(err, err, "bcm_dev_log_create");
1247 return BCM_ERR_OK;
1248}
1249
1250bcmos_errno bcm_dev_log_init_default_logger(void **start_addresses,
1251 uint32_t *sizes,
1252 bcm_dev_log_file_flags *flags,
1253 uint32_t num_files,
1254 uint32_t stack_size,
1255 bcmos_task_priority priority,
1256 uint32_t pool_size)
1257{
1258 bcm_dev_log_parm dev_log_parm = {0};
1259 uint32_t i;
1260
1261 for (i = 0; i < num_files; i++)
1262 {
1263 dev_log_parm.log_file[i].start_addr = start_addresses[i];
1264 dev_log_parm.log_file[i].size = sizes[i];
1265 /* size[i] might be 0 in simulation build for sram buffer. */
1266 dev_log_parm.log_file[i].flags = (sizes[i] ? BCM_DEV_LOG_FILE_FLAG_VALID : BCM_DEV_LOG_FILE_FLAG_NONE) |
1267 (flags ? flags[i] : BCM_DEV_LOG_FILE_FLAG_WRAP_AROUND);
1268 }
1269
1270 return bcm_dev_log_init_default_logger_ext(&dev_log_parm, num_files, stack_size, priority,
1271 pool_size);
1272}
1273
1274log_name_table logs_names[DEV_LOG_MAX_IDS];
1275uint8_t log_name_table_index = 0;
1276
1277static void dev_log_add_log_name_to_table(const char *xi_name)
1278{
1279 char name[MAX_DEV_LOG_ID_NAME + 1] = {};
1280 char *p = name;
1281 char *p_end;
1282 int i = 0;
1283 long val = -1;
1284
1285 strncpy(name, xi_name, MAX_DEV_LOG_ID_NAME);
1286 while (*p)
1287 {
1288 /* While there are more characters to process... */
1289 if (isdigit(*p))
1290 {
1291 /* Upon finding a digit, ... */
1292 val = strtol(p, &p_end, 10); /* Read a number, ... */
1293 if ((name + strlen(name)) != p_end)
1294 {
1295 DEV_LOG_ERROR_PRINTF("Error: dev_log_add_log_name_to_table %s)\n", xi_name);
1296 }
1297 *p = '\0';
1298 break;
1299
1300 }
1301 else
1302 {
1303 /* Otherwise, move on to the next character. */
1304 p++;
1305 }
1306 }
1307 for (i = 0; i < log_name_table_index; i++)
1308 {
1309 if (strcmp(name, logs_names[i].name) == 0)
1310 {
1311 if (val < logs_names[i].first_instance)
1312 {
1313 logs_names[i].first_instance = val;
1314 }
1315 if (val > logs_names[i].last_instance)
1316 {
1317 logs_names[i].last_instance = val;
1318 }
1319 break;
1320 }
1321 }
1322 if ((i == log_name_table_index) && (i < DEV_LOG_MAX_IDS))
1323 {
1324 strncpy(logs_names[i].name, name, MAX_DEV_LOG_ID_NAME);
1325 if (val == -1)
1326 {
1327 val = LOG_NAME_NO_INSTANCE;
1328 }
1329 logs_names[i].last_instance = val;
1330 logs_names[i].first_instance = val;
1331 log_name_table_index++;
1332 }
1333}
1334
1335void dev_log_get_log_name_table(char *buffer, uint32_t buf_len)
1336{
1337 uint32_t i = 0;
1338 uint32_t buf_len_free = buf_len;
1339
1340 buffer[0] = '\0';
1341 for (i = 0; i < log_name_table_index; i++)
1342 {
1343 if (logs_names[i].first_instance == LOG_NAME_NO_INSTANCE)
1344 {
1345 snprintf(&buffer[strlen(buffer)], buf_len_free, "%s\n", logs_names[i].name);
1346 }
1347 else
1348 {
1349 snprintf(&buffer[strlen(buffer)], buf_len_free, "%s %d - %d\n", logs_names[i].name, logs_names[i].first_instance, logs_names[i].last_instance);
1350 }
1351 buffer[buf_len - 1] = '\0';
1352 buf_len_free = buf_len - strlen(buffer);
1353 if (buf_len_free <= 1)
1354 {
1355 DEV_LOG_ERROR_PRINTF("Error: dev_log_get_log_name_table too long\n");
1356 break;
1357 }
1358 }
1359}
1360
1361dev_log_id bcm_dev_log_id_register(const char *xi_name,
1362 bcm_dev_log_level xi_default_log_level,
1363 bcm_dev_log_id_type xi_default_log_type)
1364{
1365 dev_log_id_parm *new_id;
1366
1367 /* Check that we have room for one more ID */
1368 if (dev_log.num_ids == BCM_SIZEOFARRAY(dev_log.ids))
1369 {
1370 DEV_LOG_ERROR_PRINTF("Error: wrong parameters\n");
1371 return DEV_LOG_INVALID_ID;
1372 }
1373
1374 /* Get next free log_id in array and increment next_id.
1375 * The new_id structure is clean because the whole dev_log array is in BSS
1376 */
1377 new_id = &dev_log.ids[dev_log.num_ids++];
1378
1379 /* Add prefix for kernel log_ids in order to avoid clash with similarly-names logs in the user space */
1380#ifdef __KERNEL__
1381 strcpy(new_id->name, "k_");
1382#endif
1383
1384 /* Set log_id parameters */
1385 strncat(new_id->name, xi_name, sizeof(new_id->name)-strlen(new_id->name));
1386 new_id->name[MAX_DEV_LOG_ID_NAME-1] = '\0';
1387
1388 new_id->default_log_type = xi_default_log_type;
1389 new_id->default_log_level = xi_default_log_level;
1390
1391 dev_log_add_log_name_to_table(new_id->name);
1392
1393 bcm_dev_log_id_set_levels_and_type_to_default((dev_log_id)new_id);
1394 new_id->style = BCM_DEV_LOG_STYLE_NORMAL;
1395
1396 return (dev_log_id)new_id;
1397}
1398
1399bcmos_errno bcm_dev_log_id_set_type(dev_log_id xi_id, bcm_dev_log_id_type xi_log_type)
1400{
1401 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1402
1403 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1404 {
1405 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1406 return BCM_ERR_PARM;
1407 }
1408
1409 /* In linux kernel we only support save */
1410#ifdef __KERNEL__
1411 if (xi_log_type & DEV_LOG_ID_TYPE_BOTH)
1412 xi_log_type = DEV_LOG_ID_TYPE_SAVE;
1413#endif
1414
1415 DEV_LOG_INFO_PRINTF("ID: 0x%lx, New log type: %d\n", xi_id, (int)xi_log_type);
1416
1417 id->log_type = xi_log_type;
1418
1419 /* In linux user space we need to notify kernel integration code */
1420#if defined(LINUX_KERNEL_SPACE) && !defined(__KERNEL__)
1421 bcm_dev_log_linux_id_set_type(xi_id, xi_log_type);
1422#endif
1423
1424 return BCM_ERR_OK;
1425}
1426
1427bcmos_errno bcm_dev_log_id_set_level(dev_log_id xi_id, bcm_dev_log_level xi_log_level_print, bcm_dev_log_level xi_log_level_save)
1428{
1429 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1430
1431 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1432 {
1433 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1434 return BCM_ERR_PARM;
1435 }
1436
1437 if ((xi_log_level_print >= DEV_LOG_LEVEL_NUM_OF) || (xi_log_level_save >= DEV_LOG_LEVEL_NUM_OF))
1438 {
1439 DEV_LOG_ERROR_PRINTF("Error: wrong parameters\n");
1440 return BCM_ERR_PARM;
1441 }
1442
1443 DEV_LOG_INFO_PRINTF("ID: 0x%p, New: xi_log_level_print=%u, xi_log_level_save=%u\n",
1444 (void *)id,
1445 xi_log_level_print,
1446 xi_log_level_save);
1447
1448 id->log_level_print = xi_log_level_print;
1449 id->log_level_save = xi_log_level_save;
1450
1451 /* In linux user space we need to notify kernel integration code */
1452#if defined(LINUX_KERNEL_SPACE) && !defined(__KERNEL__)
1453 bcm_dev_log_linux_id_set_level(xi_id, xi_log_level_print, xi_log_level_save);
1454#endif
1455
1456 return BCM_ERR_OK;
1457}
1458
1459bcmos_errno bcm_dev_log_id_set_levels_and_type_to_default(dev_log_id xi_id)
1460{
1461 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1462
1463 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1464 {
1465 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1466 return BCM_ERR_PARM;
1467 }
1468
1469 id->log_level_print = id->default_log_level;
1470 id->log_level_save = id->default_log_level;
1471 id->log_type = id->default_log_type;
1472
1473#ifdef TRIGGER_LOGGER_FEATURE
1474 memset(&id->throttle,0, sizeof(dev_log_id_throttle));
1475 memset(&id->trigger, 0, sizeof(dev_log_id_trigger));
1476 id->throttle_log_level = DEV_LOG_LEVEL_NO_LOG;
1477 id->trigger_log_level = DEV_LOG_LEVEL_NO_LOG;
1478#endif
1479
1480 /* In linux kernel space we don't support PRINT, only SAVE */
1481#ifdef __KERNEL__
1482 if (id->log_type & DEV_LOG_ID_TYPE_BOTH)
1483 id->log_type = DEV_LOG_ID_TYPE_SAVE;
1484#endif
1485
1486 return BCM_ERR_OK;
1487}
1488
1489void bcm_dev_log_level_set_style(bcm_dev_log_level level, bcm_dev_log_style xi_style)
1490{
1491 dev_log_level2style[level] = xi_style;
1492}
1493
1494bcmos_errno bcm_dev_log_id_set_style(dev_log_id xi_id, bcm_dev_log_style xi_style)
1495{
1496 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1497
1498 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1499 {
1500 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1501 return BCM_ERR_PARM;
1502 }
1503 id->style = xi_style;
1504 return BCM_ERR_OK;
1505}
1506
1507bcmos_errno bcm_dev_log_id_clear_counters(dev_log_id xi_id)
1508{
1509 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1510
1511 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1512 {
1513 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1514 return BCM_ERR_PARM;
1515 }
1516 memset(id->counters, 0, sizeof(id->counters));
1517 id->lost_msg_cnt = 0;
1518 id->print_skipped_count = 0;
1519
1520 return BCM_ERR_OK;
1521}
1522
1523bcmos_errno bcm_dev_log_id_get(dev_log_id xi_id, dev_log_id_parm *xo_id)
1524{
1525 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1526
1527 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1528 {
1529 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1530 return BCM_ERR_PARM;
1531 }
1532
1533 *xo_id = *id;
1534
1535 return BCM_ERR_OK;
1536}
1537
1538bcmos_errno bcm_dev_log_id_set(dev_log_id xi_id, dev_log_id_parm *parms)
1539{
1540 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1541
1542 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1543 {
1544 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1545 return BCM_ERR_PARM;
1546 }
1547
1548 *id = *parms;
1549
1550 return BCM_ERR_OK;
1551}
1552
1553dev_log_id bcm_dev_log_id_get_by_index(uint32_t xi_index)
1554{
1555 if (xi_index >= dev_log.num_ids)
1556 {
1557 return DEV_LOG_INVALID_ID;
1558 }
1559 return (dev_log_id)&(dev_log.ids[xi_index]);
1560}
1561
1562uint32_t bcm_dev_log_get_index_by_id(dev_log_id xi_id)
1563{
1564 uint32_t idx = (dev_log_id_parm *)xi_id - &dev_log.ids[0];
1565 if (idx >= dev_log.num_ids)
1566 idx = DEV_LOG_INVALID_INDEX;
1567 return idx;
1568}
1569
1570dev_log_id bcm_dev_log_id_get_by_name(const char *xi_name)
1571{
1572 uint32_t i;
1573
1574 if (xi_name == NULL)
1575 {
1576 return DEV_LOG_INVALID_ID;
1577 }
1578
1579 for (i = 0; i<dev_log.num_ids; i++)
1580 {
1581 if (!strcmp(dev_log.ids[i].name, xi_name))
1582 {
1583 return (dev_log_id)&(dev_log.ids[i]);
1584 }
1585 }
1586
1587 DEV_LOG_ERROR_PRINTF("Error: can't find name\n");
1588 return DEV_LOG_INVALID_ID;
1589}
1590
1591uint32_t bcm_dev_log_get_num_of_entries(void)
1592{
1593 return dev_log.num_ids;
1594}
1595
1596bcmos_bool bcm_dev_log_get_control(void)
1597{
1598 return dev_log.state == BCM_DEV_LOG_STATE_ENABLED;
1599}
1600
1601uint32_t bcm_dev_log_get_num_of_messages(bcm_dev_log_file *file)
1602{
1603 return file->u.mem_file.file_header.num_msgs;
1604}
1605
1606void bcm_dev_log_set_control(bcmos_bool control)
1607{
1608 if (control && dev_log.state == BCM_DEV_LOG_STATE_DISABLED)
1609 dev_log.state = BCM_DEV_LOG_STATE_ENABLED;
1610 else if (!control && dev_log.state == BCM_DEV_LOG_STATE_ENABLED)
1611 dev_log.state = BCM_DEV_LOG_STATE_DISABLED;
1612}
1613
1614bcm_dev_log_file_flags bcm_dev_log_get_file_flags(uint32_t file_id)
1615{
1616 return dev_log.files[file_id].file_parm.flags;
1617}
1618
1619void bcm_dev_log_set_file_flags(uint32_t file_id, bcm_dev_log_file_flags flags)
1620{
1621 dev_log.files[file_id].file_parm.flags = flags;
1622}
1623
1624bcm_dev_log_flags bcm_dev_log_get_flags(void)
1625{
1626 return dev_log.flags;
1627}
1628
1629void bcm_dev_log_set_flags(bcm_dev_log_flags flags)
1630{
1631 dev_log.flags = flags;
1632}
1633
1634void bcm_dev_log_set_print_cb(bcm_dev_log_print_cb cb)
1635{
1636 dev_log.dev_log_parm.print_cb = cb;
1637}
1638
1639void bcm_dev_log_set_get_time_cb(bcm_dev_log_get_time_cb cb)
1640{
1641 dev_log.dev_log_parm.get_time_cb = cb;
1642}
1643
1644void bcm_dev_log_set_time_to_str_cb(bcm_dev_log_time_to_str_cb cb)
1645{
1646 dev_log.dev_log_parm.time_to_str_cb = cb;
1647}
1648
1649#ifdef TRIGGER_LOGGER_FEATURE
1650/********************************************************************************************/
1651/* */
1652/* Name: bcm_dev_log_set_throttle */
1653/* */
1654/* Abstract: Set throttle level for the specific log is and its level */
1655/* */
1656/* Arguments: */
1657/* - xi_id - The ID in the Dev log (what we got form bcm_dev_log_id_register) */
1658/* - xi_log_level - log level */
1659/* - xi_throttle - throttle number, 0 - no throttle */
1660/* */
1661/* Return Value: */
1662/* bcmos_errno - Success code (BCM_ERR_OK) or Error code (see bcmos_errno.h) */
1663/* */
1664/********************************************************************************************/
1665bcmos_errno bcm_dev_log_set_throttle(dev_log_id xi_id, bcm_dev_log_level xi_log_level, uint32_t xi_throttle)
1666{
1667 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1668
1669 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1670 {
1671 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1672 return BCM_ERR_PARM;
1673 }
1674 id->throttle_log_level = xi_log_level;
1675 id->throttle.threshold = xi_throttle;
1676 id->throttle.counter = 0;
1677
1678 return BCM_ERR_OK;
1679}
1680
1681/********************************************************************************************/
1682/* */
1683/* Name: bcm_dev_log_set_trigger */
1684/* */
1685/* Abstract: Set trigger counters for the specific log is and its level */
1686/* */
1687/* Arguments: */
1688/* - xi_id - The ID in the Dev log (what we got form bcm_dev_log_id_register) */
1689/* - xi_log_level - log level */
1690/* - xi_start - start printing after this number, 0 - no trigger */
1691/* - xi_stop - stop printing after this number, 0 - do not stop */
1692/* - xi_repeat - start printing after this number, 0 - no repeat, -1 always */
1693/* */
1694/* Return Value: */
1695/* bcmos_errno - Success code (BCM_ERR_OK) or Error code (see bcmos_errno.h) */
1696/* */
1697/********************************************************************************************/
1698bcmos_errno bcm_dev_log_set_trigger(dev_log_id xi_id, bcm_dev_log_level xi_log_level, uint32_t xi_start, uint32_t xi_stop, int32_t xi_repeat)
1699{
1700 dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
1701
1702 if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
1703 {
1704 DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
1705 return BCM_ERR_PARM;
1706 }
1707 id->trigger_log_level = xi_log_level;
1708 id->trigger.start_threshold = xi_start;
1709 id->trigger.stop_threshold = xi_stop;
1710 id->trigger.repeat_threshold = xi_repeat;
1711 id->trigger.counter = 0;
1712 id->trigger.repeat = 0;
1713
1714 return BCM_ERR_OK;
1715}
1716#endif /* TRIGGER_LOGGER_FEATURE */
1717
1718/* Get file info: max data size, used bytes */
1719bcmos_errno bcm_dev_log_get_file_info(bcm_dev_log_file *file, uint32_t *file_size, uint32_t *used_bytes)
1720{
1721 if (!file || !file_size || !used_bytes)
1722 return BCM_ERR_PARM;
1723 /* Only supported for memory files */
1724 if (!bcm_dev_log_is_memory_file(file))
1725 return BCM_ERR_NOT_SUPPORTED;
1726 *file_size = file->u.mem_file.file_header.data_size;
1727 *used_bytes = (file->u.mem_file.file_header.file_wrap_cnt || file->is_full) ?
1728 file->u.mem_file.file_header.data_size : file->u.mem_file.file_header.write_offset;
1729 return BCM_ERR_OK;
1730}
1731
1732/* Register indication to be sent when file utilization crosses threshold */
1733bcmos_errno bcm_dev_log_almost_full_ind_register(bcm_dev_log_file *file, uint32_t used_bytes_threshold,
1734 F_dev_log_file_almost_full send_ind_cb, long ind_cb_priv)
1735{
1736 if (!file || (used_bytes_threshold && !send_ind_cb))
1737 return BCM_ERR_PARM;
1738 /* Only supported for memory files */
1739 if (!bcm_dev_log_is_memory_file(file))
1740 return BCM_ERR_NOT_SUPPORTED;
1741
1742 bcmos_mutex_lock(&file->u.mem_file.mutex);
1743 file->almost_full.threshold = used_bytes_threshold;
1744 file->almost_full.send_ind_cb = send_ind_cb;
1745 file->almost_full.priv = ind_cb_priv;
1746 file->almost_full.ind_sent = BCMOS_FALSE;
1747 bcmos_mutex_unlock(&file->u.mem_file.mutex);
1748
1749 return BCM_ERR_OK;
1750}
1751
1752#endif /* ENABLE_LOG */