| /* |
| <: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. |
| |
| :> |
| */ |
| |
| #ifdef ENABLE_LOG |
| |
| #include "bcm_dev_log_task.h" |
| #include "bcm_dev_log_task_internal.h" |
| #include "bcm_dev_log.h" |
| #if defined(LINUX_KERNEL_SPACE) && !defined(__KERNEL__) |
| #include "bcmolt_dev_log_linux.h" |
| #endif |
| |
| #ifdef DEV_LOG_SYSLOG |
| #include <syslog.h> |
| #endif |
| |
| #define USE_ANSI_ESCAPE_CODES |
| |
| #ifdef USE_ANSI_ESCAPE_CODES |
| #define DEV_LOG_STYLE_NORMAL "\033[0m\033#5" |
| #define DEV_LOG_STYLE_BOLD "\033[1m" |
| #define DEV_LOG_STYLE_UNDERLINE "\033[4m" |
| #define DEV_LOG_STYLE_BLINK "\033[5m" |
| #define DEV_LOG_STYLE_REVERSE_VIDEO "\033[7m" |
| #else |
| #define DEV_LOG_STYLE_NORMAL " " |
| #define DEV_LOG_STYLE_BOLD "*** " |
| #define DEV_LOG_STYLE_UNDERLINE "___ " |
| #define DEV_LOG_STYLE_BLINK "o_o " |
| #define DEV_LOG_STYLE_REVERSE_VIDEO "!!! " |
| #endif |
| |
| #define DEV_LOG_MSG_START_CHAR 0x1e /* record separator character */ |
| |
| #define MEM_FILE_HDR_SIZE sizeof(dev_log_mem_file_header) |
| |
| #define LOG_MIN(a,b) (((int)(a) <= (int)(b)) ? (a) : (b)) |
| |
| dev_log_id def_log_id; |
| |
| static bcm_dev_log_style dev_log_level2style[DEV_LOG_LEVEL_NUM_OF] = |
| { |
| [DEV_LOG_LEVEL_FATAL] = BCM_DEV_LOG_STYLE_NORMAL, |
| [DEV_LOG_LEVEL_ERROR] = BCM_DEV_LOG_STYLE_NORMAL, |
| [DEV_LOG_LEVEL_WARNING] = BCM_DEV_LOG_STYLE_NORMAL, |
| [DEV_LOG_LEVEL_INFO] = BCM_DEV_LOG_STYLE_NORMAL, |
| [DEV_LOG_LEVEL_DEBUG] = BCM_DEV_LOG_STYLE_NORMAL, |
| }; |
| |
| static const char *dev_log_style_array[] = |
| { |
| [BCM_DEV_LOG_STYLE_NORMAL] = DEV_LOG_STYLE_NORMAL, |
| [BCM_DEV_LOG_STYLE_BOLD] = DEV_LOG_STYLE_BOLD, |
| [BCM_DEV_LOG_STYLE_UNDERLINE] = DEV_LOG_STYLE_UNDERLINE, |
| [BCM_DEV_LOG_STYLE_BLINK] = DEV_LOG_STYLE_BLINK, |
| [BCM_DEV_LOG_STYLE_REVERSE_VIDEO] = DEV_LOG_STYLE_REVERSE_VIDEO, |
| }; |
| |
| bcm_dev_log dev_log = {{0}}; |
| const char *log_level_str = "?FEWID"; |
| |
| static void bcm_dev_log_shutdown_msg_release(bcmos_msg *m) |
| { |
| (void)m; /* do nothing, the message is statically allocated */ |
| } |
| |
| static bcmos_msg shutdown_msg = { .release = bcm_dev_log_shutdown_msg_release }; |
| |
| static void bcm_dev_log_file_save_msg(bcm_dev_log_file *files, const char *message) |
| { |
| uint32_t i; |
| uint32_t len = strlen(message) + 1; /* including 0 terminator */ |
| |
| for (i = 0; (i < DEV_LOG_MAX_FILES) && (files[i].file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID); i++) |
| { |
| files[i].file_parm.write_cb(&files[i], message, len); |
| } |
| } |
| |
| static void dev_log_save_task_handle_message(bcmos_msg *msg) |
| { |
| static char log_string[MAX_DEV_LOG_STRING_SIZE]; |
| uint32_t length; |
| char time_str[17]; |
| dev_log_queue_msg *receive_msg = msg->data; |
| dev_log_id_parm *log_id = receive_msg->log_id; |
| bcm_dev_log_level msg_level = receive_msg->msg_level; |
| |
| /* Build message header */ |
| *log_string = '\0'; |
| if (!(receive_msg->flags & BCM_LOG_FLAG_NO_HEADER)) |
| { |
| length = sizeof(time_str); |
| dev_log.dev_log_parm.time_to_str_cb(receive_msg->time_stamp, time_str, length); |
| snprintf( |
| log_string, |
| sizeof(log_string), |
| "[%s: %c %-20s] ", |
| time_str, |
| log_level_str[msg_level], |
| log_id->name); |
| } |
| |
| /* Modify the __FILE__ format so it would print the filename only without the path. |
| * If using BCM_LOG_FLAG_CALLER_FMT, it is done in caller context, because filename might be long, taking a lot of |
| * the space of the message itself. */ |
| if ((receive_msg->flags & BCM_LOG_FLAG_FILENAME_IN_FMT) && !(receive_msg->flags & BCM_LOG_FLAG_CALLER_FMT)) |
| receive_msg->u.fmt_args.fmt = dev_log_basename(receive_msg->u.fmt_args.fmt); |
| |
| if (receive_msg->flags & BCM_LOG_FLAG_CALLER_FMT) |
| { |
| /* Copy user string to buffer */ |
| strncat(log_string, receive_msg->u.str, sizeof(log_string) - strlen(log_string) - 1); |
| } |
| else |
| { |
| uint32_t offset = ((long)receive_msg->u.fmt_args.args % 8 == 0) ? 0 : 1; /* start on an 8-byte boundary */ |
| va_list ap; |
| *(void **)&ap = &receive_msg->u.fmt_args.args[offset]; |
| |
| /* Copy user string to buffer */ |
| vsnprintf(log_string + strlen(log_string), |
| sizeof(log_string) - strlen(log_string), |
| receive_msg->u.fmt_args.fmt, |
| ap); |
| } |
| |
| /* Force last char to be end of string */ |
| log_string[MAX_DEV_LOG_STRING_SIZE - 1] = '\0'; |
| |
| /* At this point, if the message is going to be sent to the print task, save the formatted string then forward it. |
| * Otherwise, we can release the message before writing it to RAM. */ |
| if ((log_id->log_type & DEV_LOG_ID_TYPE_PRINT) && |
| (msg_level <= log_id->log_level_print)) |
| { |
| if (!bcm_dev_log_level_is_error(msg_level) && |
| (receive_msg->flags & BCM_LOG_FLAG_DONT_SKIP_PRINT) != 0 && |
| bcm_dev_log_pool_occupancy_percent_get() >= DEV_LOG_SKIP_PRINT_THRESHOLD_PERCENT) |
| { |
| log_id->print_skipped_count++; |
| bcmos_msg_free(msg); |
| } |
| else |
| { |
| strcpy(receive_msg->u.str, log_string); /* so the print task doesn't need to re-format it */ |
| bcmos_msg_send(&dev_log.print_queue, msg, 0); |
| } |
| } |
| else |
| { |
| bcmos_msg_free(msg); |
| } |
| |
| /* Save to file */ |
| if ((log_id->log_type & DEV_LOG_ID_TYPE_SAVE) && |
| (msg_level <= log_id->log_level_save)) |
| { |
| bcm_dev_log_file_save_msg(dev_log.files, log_string); |
| } |
| } |
| |
| static void dev_log_print_task_handle_message(bcmos_msg *msg) |
| { |
| static char log_string[MAX_DEV_LOG_STRING_SIZE]; |
| dev_log_queue_msg *receive_msg = msg->data; |
| dev_log_id_parm *log_id = receive_msg->log_id; |
| bcm_dev_log_level msg_level = receive_msg->msg_level; |
| |
| /* make a local copy of the pre-formatted string (it was formatted in the save task) */ |
| strcpy(log_string, receive_msg->u.str); |
| |
| /* free the message ASAP since printing might take some time */ |
| bcmos_msg_free(msg); |
| |
| if (log_id->style == BCM_DEV_LOG_STYLE_NORMAL && |
| dev_log_level2style[msg_level] == BCM_DEV_LOG_STYLE_NORMAL) |
| { |
| dev_log.dev_log_parm.print_cb(dev_log.dev_log_parm.print_cb_arg, "%s", log_string); |
| } |
| else |
| { |
| /* If style was set per log id, then use it. */ |
| if (log_id->style != BCM_DEV_LOG_STYLE_NORMAL) |
| { |
| dev_log.dev_log_parm.print_cb( |
| dev_log.dev_log_parm.print_cb_arg, |
| "%s%s%s", |
| dev_log_style_array[log_id->style], |
| log_string, |
| dev_log_style_array[BCM_DEV_LOG_STYLE_NORMAL]); |
| } |
| else |
| { |
| /* Otherwise - style was set per log level. */ |
| dev_log.dev_log_parm.print_cb( |
| dev_log.dev_log_parm.print_cb_arg, |
| "%s%s%s", |
| dev_log_style_array[dev_log_level2style[msg_level]], |
| log_string, |
| dev_log_style_array[BCM_DEV_LOG_STYLE_NORMAL]); |
| } |
| } |
| } |
| |
| static int dev_log_print_task(long data) |
| { |
| bcmos_msg *m; |
| bcmos_errno error; |
| const char error_str[MAX_DEV_LOG_STRING_SIZE] = "Error: Can't receive from queue - dev_log print task exit\n"; |
| |
| while (1) |
| { |
| error = bcmos_msg_recv(&dev_log.print_queue, BCMOS_WAIT_FOREVER, &m); |
| if (error != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("%s", error_str); |
| dev_log.dev_log_parm.print_cb(dev_log.dev_log_parm.print_cb_arg, "%s", error_str); |
| bcm_dev_log_file_save_msg(dev_log.files, error_str); |
| return (int)error; |
| } |
| |
| if (m == &shutdown_msg) |
| { |
| bcmos_msg_free(m); |
| break; /* shut down gracefully */ |
| } |
| else |
| { |
| dev_log_print_task_handle_message(m); |
| } |
| } |
| |
| bcmos_sem_post(&dev_log.print_task_is_terminated); |
| |
| return 0; |
| } |
| |
| static int dev_log_save_task(long data) |
| { |
| bcmos_msg *m; |
| bcmos_errno error; |
| const char error_str[MAX_DEV_LOG_STRING_SIZE] = "Error: Can't receive from queue - dev_log save task exit\n"; |
| |
| while (1) |
| { |
| error = bcmos_msg_recv(&dev_log.save_queue, BCMOS_WAIT_FOREVER, &m); |
| if (error != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("%s", error_str); |
| dev_log.dev_log_parm.print_cb(dev_log.dev_log_parm.print_cb_arg, "%s", error_str); |
| bcm_dev_log_file_save_msg(dev_log.files, error_str); |
| return (int)error; |
| } |
| |
| if (m == &shutdown_msg) |
| { |
| bcmos_msg_free(m); |
| break; /* shut down gracefully */ |
| } |
| else |
| { |
| dev_log_save_task_handle_message(m); |
| } |
| } |
| |
| bcmos_sem_post(&dev_log.save_task_is_terminated); |
| |
| return 0; |
| } |
| |
| static int default_time_to_str(uint32_t time_stamp, char *time_str, int time_str_size) |
| { |
| return snprintf(time_str, time_str_size, "%u", time_stamp); |
| } |
| |
| static uint32_t default_get_time(void) |
| { |
| return 0; |
| } |
| |
| static int default_print(void *arg, const char *format, ...) |
| { |
| va_list args; |
| |
| va_start(args, format); |
| bcmos_vprintf(format, args); |
| va_end(args); |
| return 0; |
| } |
| |
| static bcmos_errno default_open_callback_cond_reset(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file, bcmos_bool is_rewind) |
| { |
| bcmos_errno err; |
| |
| if (!file->file_parm.start_addr || file->file_parm.size <= MEM_FILE_HDR_SIZE) |
| return BCM_ERR_PARM; |
| |
| /* Create file mutex */ |
| err = bcmos_mutex_create(&file->u.mem_file.mutex, 0, NULL); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't create mutex (error: %d)\n", (int)err); |
| return err; |
| } |
| |
| /* Check file magic string */ |
| memcpy(&file->u.mem_file.file_header, (uint8_t *)file->file_parm.start_addr, MEM_FILE_HDR_SIZE); |
| if (memcmp(FILE_MAGIC_STR, file->u.mem_file.file_header.file_magic, FILE_MAGIC_STR_SIZE)) |
| { |
| DEV_LOG_INFO_PRINTF("No file magic string - file is empty/corrupt\n"); |
| if (!is_rewind || !file->file_parm.rewind_cb) |
| { |
| bcmos_mutex_destroy(&file->u.mem_file.mutex); |
| return BCM_ERR_NOENT; |
| } |
| return file->file_parm.rewind_cb(file); |
| } |
| |
| DEV_LOG_INFO_PRINTF("Attached to existing file\n"); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno default_open_callback(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file) |
| { |
| return default_open_callback_cond_reset(file_parm, file, BCMOS_TRUE); |
| } |
| |
| static bcmos_errno default_close_callback(bcm_dev_log_file *file) |
| { |
| bcmos_mutex_destroy(&file->u.mem_file.mutex); |
| return BCM_ERR_OK; |
| } |
| |
| /* Look for start of msg character */ |
| static int get_msg_start_offset(bcm_dev_log_file *file, uint32_t offset, uint32_t max_len) |
| { |
| uint8_t *buf, *msg; |
| |
| buf = (uint8_t *)file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE; |
| msg = memchr(buf, DEV_LOG_MSG_START_CHAR, max_len); |
| if (!msg) |
| return -1; |
| return (msg - buf + 1); |
| } |
| |
| /* Look for 0-terminator */ |
| static int get_msg_end_offset(bcm_dev_log_file *file, uint32_t offset, uint32_t max_len) |
| { |
| uint8_t *buf, *end; |
| |
| buf = (uint8_t *)file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE; |
| end = memchr(buf, 0, max_len); |
| if (!end) |
| return -1; |
| return (end - buf + 1); |
| } |
| |
| static int default_read_callback(bcm_dev_log_file *log_file, uint32_t *p_offset, void *buf, uint32_t length) |
| { |
| uint32_t offset = *p_offset; |
| uint32_t real_length = 0; |
| int start, end; |
| |
| /* If file wrapped around, offset starts from write_offset, otherwise, |
| * it starts from the beginning |
| */ |
| if (log_file->u.mem_file.file_header.file_wrap_cnt) |
| { |
| if (offset + length >= log_file->u.mem_file.file_header.data_size) |
| { |
| length = log_file->u.mem_file.file_header.data_size - offset; |
| if (length <= 0) |
| return 0; |
| } |
| offset += log_file->u.mem_file.file_header.write_offset; |
| if (offset >= log_file->u.mem_file.file_header.data_size) |
| offset -= log_file->u.mem_file.file_header.data_size; |
| /* Find beginning of the next message */ |
| start = get_msg_start_offset(log_file, offset, LOG_MIN(log_file->u.mem_file.file_header.data_size - offset, |
| MAX_DEV_LOG_STRING_SIZE)); |
| if (start < 0) |
| { |
| /* Didn't find any up to the end of buffer. Look from the start */ |
| offset = 0; |
| if (!log_file->u.mem_file.file_header.write_offset) |
| return 0; |
| start = get_msg_start_offset(log_file, 0, LOG_MIN(log_file->u.mem_file.file_header.write_offset - 1, |
| MAX_DEV_LOG_STRING_SIZE)); |
| if (start <= 0) |
| return 0; |
| } |
| offset += start; |
| } |
| else |
| { |
| /* Start beginning of message. Without wrap-around it should be right at the offset, |
| * so, it is just a precaution */ |
| start = get_msg_start_offset(log_file, offset, LOG_MIN(log_file->u.mem_file.file_header.data_size - offset, |
| MAX_DEV_LOG_STRING_SIZE)); |
| if (start <= 0) |
| return 0; |
| offset += start; |
| /* Stop reading when reached write_offset */ |
| if (offset + length >= log_file->u.mem_file.file_header.write_offset) |
| { |
| if (offset >= log_file->u.mem_file.file_header.write_offset) |
| return 0; |
| length = log_file->u.mem_file.file_header.write_offset - offset; |
| } |
| } |
| |
| /* We have the beginning. Now find the end of message and copy to the user |
| * buffer if there is enough room */ |
| if (offset + length > log_file->u.mem_file.file_header.data_size) |
| { |
| uint32_t length1 = offset + length - log_file->u.mem_file.file_header.data_size; /* tail length */ |
| length -= length1; |
| end = get_msg_end_offset(log_file, offset, length); |
| if (end >= 0) |
| { |
| memcpy(buf, (uint8_t *)log_file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE, end); |
| *p_offset += end + start; |
| return end; |
| } |
| |
| /* Didn't find end of message in the end of file data buffer. wrap around */ |
| memcpy(buf, (uint8_t *)log_file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE, length); |
| real_length = length; |
| buf = (uint8_t *)buf + length; |
| length = length1; |
| offset = 0; |
| } |
| end = get_msg_end_offset(log_file, offset, MAX_DEV_LOG_STRING_SIZE); |
| if (end <= 0) |
| { |
| /* something is wrong. msg is not terminated */ |
| return 0; |
| } |
| /* If there is no room for the whole message - return overflow */ |
| if (end > length) |
| return (int)BCM_ERR_OVERFLOW; |
| |
| memcpy(buf, (uint8_t *)log_file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE, end); |
| real_length += end; |
| |
| *p_offset += real_length + start; |
| |
| return real_length; |
| } |
| |
| static int get_num_of_overwritten_messages(uint8_t *buf, uint32_t length) |
| { |
| uint8_t *p; |
| int n = 0; |
| |
| do |
| { |
| p = memchr(buf, DEV_LOG_MSG_START_CHAR, length); |
| if (p == NULL) |
| break; |
| ++n; |
| length -= (p + 1 - buf); |
| buf = p + 1; |
| } while(length); |
| |
| return n; |
| } |
| |
| static int default_write_callback(bcm_dev_log_file *file, const void *buf, uint32_t length) |
| { |
| uint32_t offset, next_offset; |
| uint8_t *p; |
| int n_overwritten = 0; |
| |
| if ((file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_STOP_WHEN_FULL) && file->is_full) |
| return 0; |
| |
| if (!length) |
| return 0; |
| |
| bcmos_mutex_lock(&file->u.mem_file.mutex); |
| |
| offset = file->u.mem_file.file_header.write_offset; |
| next_offset = offset + length + 1; /* 1 extra character for record delimiter */ |
| |
| /* Handle overflow */ |
| if (next_offset >= file->u.mem_file.file_header.data_size) |
| { |
| uint32_t tail_length; |
| |
| /* Split buffer in 2 */ |
| tail_length = next_offset - file->u.mem_file.file_header.data_size; /* 2nd segment length */ |
| |
| if ((file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_STOP_WHEN_FULL) && tail_length) |
| { |
| file->is_full = BCMOS_TRUE; |
| bcmos_mutex_unlock(&file->u.mem_file.mutex); |
| return 0; |
| } |
| |
| length -= tail_length; |
| /* "if (next_offset >= file->u.mem_file.file_header.data_size)" condition |
| * guarantees that there is room for at least 1 character in the end of file */ |
| p = (uint8_t *)file->file_parm.start_addr + offset + MEM_FILE_HDR_SIZE; |
| if (file->u.mem_file.file_header.file_wrap_cnt) |
| n_overwritten += get_num_of_overwritten_messages(p, length + 1); |
| *p = DEV_LOG_MSG_START_CHAR; |
| if (length) |
| { |
| memcpy(p + 1, buf, length); |
| buf = (const uint8_t *)buf + length; |
| } |
| if (tail_length) |
| { |
| p = (uint8_t *)file->file_parm.start_addr + MEM_FILE_HDR_SIZE; |
| if (file->u.mem_file.file_header.file_wrap_cnt) |
| n_overwritten += get_num_of_overwritten_messages(p, tail_length); |
| memcpy(p, buf, tail_length); |
| ++file->u.mem_file.file_header.file_wrap_cnt; |
| } |
| next_offset = tail_length; |
| } |
| else |
| { |
| p = (uint8_t *)file->file_parm.start_addr + MEM_FILE_HDR_SIZE + offset; |
| if (file->u.mem_file.file_header.file_wrap_cnt) |
| n_overwritten += get_num_of_overwritten_messages(p, length + 1); |
| *(p++) = DEV_LOG_MSG_START_CHAR; |
| memcpy(p, buf, length); |
| } |
| file->u.mem_file.file_header.write_offset = next_offset; |
| file->u.mem_file.file_header.num_msgs += (1 - n_overwritten); |
| |
| /* update the header */ |
| memcpy((uint8_t *)file->file_parm.start_addr, &file->u.mem_file.file_header, MEM_FILE_HDR_SIZE); |
| |
| /* send almost full indication if necessary */ |
| if (file->almost_full.send_ind_cb && |
| !file->almost_full.ind_sent && |
| file->u.mem_file.file_header.write_offset > file->almost_full.threshold) |
| { |
| file->almost_full.ind_sent = (file->almost_full.send_ind_cb(file->almost_full.priv) == BCM_ERR_OK); |
| } |
| |
| bcmos_mutex_unlock(&file->u.mem_file.mutex); |
| |
| return length; |
| } |
| |
| static bcmos_errno default_rewind_callback(bcm_dev_log_file *file) |
| { |
| bcmos_mutex_lock(&file->u.mem_file.mutex); |
| |
| DEV_LOG_INFO_PRINTF("Clear file\n"); |
| file->u.mem_file.file_header.file_wrap_cnt = 0; |
| file->u.mem_file.file_header.write_offset = 0; |
| file->u.mem_file.file_header.data_size = file->file_parm.size - MEM_FILE_HDR_SIZE; |
| file->u.mem_file.file_header.num_msgs = 0; |
| strcpy(file->u.mem_file.file_header.file_magic, FILE_MAGIC_STR); |
| memcpy((uint8_t *)file->file_parm.start_addr, &file->u.mem_file.file_header, MEM_FILE_HDR_SIZE); |
| file->almost_full.ind_sent = BCMOS_FALSE; |
| |
| bcmos_mutex_unlock(&file->u.mem_file.mutex); |
| |
| return BCM_ERR_OK; |
| } |
| |
| |
| static void set_default_file_callbacks(bcm_dev_log_file *file) |
| { |
| file->file_parm.open_cb = default_open_callback; |
| file->file_parm.close_cb = default_close_callback; |
| file->file_parm.read_cb = default_read_callback; |
| file->file_parm.write_cb = default_write_callback; |
| file->file_parm.rewind_cb = default_rewind_callback; |
| } |
| |
| static inline bcmos_bool bcm_dev_log_is_memory_file(bcm_dev_log_file *file) |
| { |
| return (file->file_parm.open_cb == default_open_callback); |
| } |
| |
| bcmos_errno bcm_dev_log_file_clear(bcm_dev_log_file *file) |
| { |
| if (!file || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID)) |
| return BCM_ERR_PARM; |
| |
| if (!file->file_parm.rewind_cb) |
| return BCM_ERR_NOT_SUPPORTED; |
| |
| return file->file_parm.rewind_cb(file); |
| } |
| |
| /* File index to file handle */ |
| bcm_dev_log_file *bcm_dev_log_file_get(uint32_t file_index) |
| { |
| bcm_dev_log_file *file = &dev_log.files[file_index]; |
| |
| if ((file_index >= DEV_LOG_MAX_FILES) || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID)) |
| return NULL; |
| return file; |
| } |
| |
| /* Read from file */ |
| int bcm_dev_log_file_read(bcm_dev_log_file *file, uint32_t *offset, char *buf, uint32_t buf_len) |
| { |
| int len; |
| if (!file || !buf || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID)) |
| return (int)BCM_ERR_PARM; |
| |
| len = file->file_parm.read_cb(file, offset, buf, buf_len); |
| /* If nothing more to read and CLEAR_AFTER_READ mode, read again under lock and clear if no new records */ |
| if (!len && bcm_dev_log_is_memory_file(file) && (file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_CLEAR_AFTER_READ)) |
| { |
| bcmos_mutex_lock(&file->u.mem_file.mutex); |
| len = file->file_parm.read_cb(file, offset, buf, buf_len); |
| if (!len) |
| file->file_parm.rewind_cb(file); |
| bcmos_mutex_unlock(&file->u.mem_file.mutex); |
| } |
| return len; |
| } |
| |
| /* Attach file to memory buffer */ |
| bcmos_errno bcm_dev_log_file_attach(void *buf, uint32_t buf_len, bcm_dev_log_file *file) |
| { |
| bcmos_errno rc; |
| dev_log_mem_file_header *hdr = (dev_log_mem_file_header *)buf; |
| |
| if (!buf || !file || buf_len <= MEM_FILE_HDR_SIZE) |
| return BCM_ERR_PARM; |
| |
| if (memcmp(FILE_MAGIC_STR, hdr->file_magic, FILE_MAGIC_STR_SIZE)) |
| return BCM_ERR_NOENT; |
| |
| #if DEV_LOG_ENDIAN != BCM_CPU_ENDIAN |
| hdr->file_wrap_cnt = bcmos_endian_swap_u32(hdr->file_wrap_cnt); |
| hdr->write_offset = bcmos_endian_swap_u32(hdr->write_offset); |
| hdr->data_size = bcmos_endian_swap_u32(hdr->data_size); |
| hdr->num_msgs = bcmos_endian_swap_u32(hdr->num_msgs); |
| #endif |
| |
| memset(file, 0, sizeof(*file)); |
| file->file_parm.start_addr = buf; |
| file->file_parm.size = buf_len; |
| file->file_parm.flags = BCM_DEV_LOG_FILE_FLAG_VALID; |
| set_default_file_callbacks(file); |
| rc = default_open_callback_cond_reset(&file->file_parm, file, BCMOS_FALSE); |
| if (rc) |
| return rc; |
| |
| if (!file->u.mem_file.file_header.num_msgs) |
| return BCM_ERR_NOENT; |
| |
| return BCM_ERR_OK; |
| } |
| |
| /* Detach file handle from memory buffer */ |
| bcmos_errno bcm_dev_log_file_detach(bcm_dev_log_file *file) |
| { |
| if (!file || !(file->file_parm.flags & BCM_DEV_LOG_FILE_FLAG_VALID)) |
| return BCM_ERR_PARM; |
| file->file_parm.flags = BCM_DEV_LOG_FILE_FLAG_VALID; |
| bcmos_mutex_destroy(&file->u.mem_file.mutex); |
| return BCM_ERR_OK; |
| } |
| |
| |
| #ifdef BCM_OS_POSIX |
| /* Regular file callbacks */ |
| |
| /****************************************************************************************/ |
| /* OPEN CALLBACK: open memory/file */ |
| /* file_parm - file parameters */ |
| /* file - file */ |
| /****************************************************************************************/ |
| static bcmos_errno _dev_log_reg_file_open(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file) |
| { |
| bcmos_errno err = BCM_ERR_OK; |
| |
| file->file_parm = *file_parm; |
| file->u.reg_file_handle = fopen((char *)file_parm->udef_parms, "w+"); |
| if (!file->u.reg_file_handle) |
| { |
| bcmos_printf("DEV_LOG: can't open file %s for writing\n", (char *)file_parm->udef_parms); |
| err = BCM_ERR_IO; |
| } |
| return err; |
| } |
| |
| /****************************************************************************************/ |
| /* CLOSE CALLBACK: close memory/file */ |
| /* file - file handle */ |
| /****************************************************************************************/ |
| static bcmos_errno _dev_log_reg_file_close(bcm_dev_log_file *file) |
| { |
| if (file->u.reg_file_handle) |
| { |
| fclose(file->u.reg_file_handle); |
| file->u.reg_file_handle = NULL; |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /****************************************************************************************/ |
| /* REWIND CALLBACK: clears memory/file */ |
| /* file - file handle */ |
| /****************************************************************************************/ |
| static bcmos_errno _dev_log_reg_file_rewind(bcm_dev_log_file *file) |
| { |
| bcmos_errno err = BCM_ERR_OK; |
| FILE *f; |
| |
| if (file->u.reg_file_handle) |
| { |
| f = freopen((const char *)file->file_parm.udef_parms, "w+", file->u.reg_file_handle); |
| if (NULL != f) |
| { |
| file->u.reg_file_handle = f; |
| } |
| else |
| { |
| err = BCM_ERR_IO; |
| bcmos_printf("DEV_LOG: can't open file %s for writing\n", (char *)file->file_parm.udef_parms); |
| } |
| |
| } |
| return err; |
| } |
| |
| /****************************************************************************************/ |
| /* READ_CALLBACK: read form memory/file */ |
| /* offset - the offset in bytes to read from, output */ |
| /* buf - Where to put the result */ |
| /* length - Buffer length */ |
| /* This function should return the number of bytes actually read from file or 0 if EOF */ |
| /****************************************************************************************/ |
| static int _dev_log_reg_file_read(bcm_dev_log_file *file, uint32_t *offset, void *buf, uint32_t length) |
| { |
| int n = 0; |
| if (file->u.reg_file_handle) |
| { |
| if (!fseek(file->u.reg_file_handle, SEEK_SET, *offset)) |
| n = fread(buf, 1, length, file->u.reg_file_handle); |
| *offset = ftell(file->u.reg_file_handle); |
| } |
| return (n < 0) ? 0 : n; |
| } |
| |
| /****************************************************************************************/ |
| /* WRITE_CALLBACK: write form memory/file */ |
| /* buf - The buffer that should be written */ |
| /* length - The number of bytes to write */ |
| /* This function should return the number of bytes actually written to file or 0 if EOF */ |
| /****************************************************************************************/ |
| static int _dev_log_reg_file_write(bcm_dev_log_file *file, const void *buf, uint32_t length) |
| { |
| const char *cbuf = (const char *)buf; |
| int n = 0; |
| if (file->u.reg_file_handle) |
| { |
| /* Remove 0 terminator */ |
| if (length && !cbuf[length-1]) |
| --length; |
| n = fwrite(buf, 1, length, file->u.reg_file_handle); |
| fflush(file->u.reg_file_handle); |
| } |
| return n; |
| } |
| |
| static void set_regular_file_callbacks(bcm_dev_log_file *file) |
| { |
| file->file_parm.open_cb = _dev_log_reg_file_open; |
| file->file_parm.close_cb = _dev_log_reg_file_close; |
| file->file_parm.read_cb = _dev_log_reg_file_read; |
| file->file_parm.write_cb = _dev_log_reg_file_write; |
| file->file_parm.rewind_cb = _dev_log_reg_file_rewind; |
| } |
| |
| #endif /* #ifdef BCM_OS_POSIX */ |
| |
| #ifdef DEV_LOG_SYSLOG |
| /* linux syslog integration */ |
| /****************************************************************************************/ |
| /* OPEN CALLBACK: open syslog interface */ |
| /* file_parm->udef_parm contains optional log ident */ |
| /****************************************************************************************/ |
| static bcmos_errno _dev_log_syslog_file_open(const bcm_dev_log_file_parm *file_parm, bcm_dev_log_file *file) |
| { |
| file->file_parm = *file_parm; |
| openlog((const char *)file_parm->udef_parms, 0, LOG_USER); |
| return BCM_ERR_OK; |
| } |
| |
| /****************************************************************************************/ |
| /* CLOSE CALLBACK: close syslog interface */ |
| /****************************************************************************************/ |
| static bcmos_errno _dev_log_syslog_file_close(bcm_dev_log_file *file) |
| { |
| closelog(); |
| return BCM_ERR_OK; |
| } |
| |
| /****************************************************************************************/ |
| /* REWIND CALLBACK: not supported by syslog. Return OK to prevent request failure */ |
| /****************************************************************************************/ |
| static bcmos_errno _dev_log_syslog_file_rewind(bcm_dev_log_file *file) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| /****************************************************************************************/ |
| /* READ_CALLBACK: reading from syslog is not supported */ |
| /****************************************************************************************/ |
| static int _dev_log_syslog_file_read(bcm_dev_log_file *file, uint32_t *offset, void *buf, uint32_t length) |
| { |
| return 0; |
| } |
| |
| /****************************************************************************************/ |
| /* WRITE_CALLBACK: write to syslog */ |
| /* buf - The buffer that should be written */ |
| /* length - The number of bytes to write */ |
| /* This function should return the number of bytes actually written to file or 0 if EOF */ |
| /****************************************************************************************/ |
| static int _dev_log_syslog_file_write(bcm_dev_log_file *file, const void *buf, uint32_t length) |
| { |
| const char *cbuf = (const char *)buf; |
| static int log_priority = LOG_DEBUG; |
| |
| /* Identify log lovel */ |
| if (cbuf && cbuf[0] == '[') |
| { |
| const char *clevel = strchr(cbuf, ' '); |
| if (clevel) |
| { |
| switch (*(clevel + 1)) |
| { |
| case 'F': |
| log_priority = LOG_CRIT; |
| break; |
| case 'E': |
| log_priority = LOG_ERR; |
| break; |
| case 'W': |
| log_priority = LOG_WARNING; |
| break; |
| case 'I': |
| log_priority = LOG_INFO; |
| break; |
| case 'D': |
| log_priority = LOG_DEBUG; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| syslog(log_priority, "%s", cbuf); |
| return length; |
| } |
| |
| static void set_syslog_file_callbacks(bcm_dev_log_file *file) |
| { |
| file->file_parm.open_cb = _dev_log_syslog_file_open; |
| file->file_parm.close_cb = _dev_log_syslog_file_close; |
| file->file_parm.read_cb = _dev_log_syslog_file_read; |
| file->file_parm.write_cb = _dev_log_syslog_file_write; |
| file->file_parm.rewind_cb = _dev_log_syslog_file_rewind; |
| } |
| |
| #endif /* #ifdef DEV_LOG_SYSLOG */ |
| |
| static bcmos_errno bcm_dev_log_create( |
| const bcm_dev_log_parm *dev_log_parm, |
| const bcmos_task_parm *save_task_parm, |
| const bcmos_task_parm *print_task_parm, |
| const bcmos_msg_queue_parm *save_queue_parm, |
| const bcmos_msg_queue_parm *print_queue_parm, |
| const bcmos_msg_pool_parm *pool_parm) |
| { |
| bcmos_errno err; |
| int i; |
| |
| if (!dev_log_parm) |
| return BCM_ERR_PARM; |
| |
| if (dev_log.state != BCM_DEV_LOG_STATE_UNINITIALIZED) |
| return BCM_ERR_ALREADY; |
| |
| /* Set user callbacks */ |
| dev_log.dev_log_parm = *dev_log_parm; |
| if (!dev_log.dev_log_parm.print_cb) |
| dev_log.dev_log_parm.print_cb = default_print; |
| if (!dev_log.dev_log_parm.get_time_cb) |
| dev_log.dev_log_parm.get_time_cb = default_get_time; |
| if (!dev_log.dev_log_parm.time_to_str_cb) |
| dev_log.dev_log_parm.time_to_str_cb = default_time_to_str; |
| |
| for (i = 0; (i < DEV_LOG_MAX_FILES) && (dev_log_parm->log_file[i].flags & BCM_DEV_LOG_FILE_FLAG_VALID); i++) |
| { |
| /* Copy log files */ |
| dev_log.files[i].file_parm = dev_log_parm->log_file[i]; |
| if (!dev_log.files[i].file_parm.open_cb) |
| { |
| if (dev_log.files[i].file_parm.type == BCM_DEV_LOG_FILE_MEMORY) |
| set_default_file_callbacks(&dev_log.files[i]); |
| #ifdef BCM_OS_POSIX |
| else if (dev_log.files[i].file_parm.type == BCM_DEV_LOG_FILE_REGULAR) |
| set_regular_file_callbacks(&dev_log.files[i]); |
| #endif |
| #ifdef DEV_LOG_SYSLOG |
| else if (dev_log.files[i].file_parm.type == BCM_DEV_LOG_FILE_SYSLOG) |
| set_syslog_file_callbacks(&dev_log.files[i]); |
| #endif |
| else |
| { |
| DEV_LOG_ERROR_PRINTF("file%d: open_cb must be set for user-defined file\n\n", i); |
| continue; |
| } |
| } |
| err = dev_log.files[i].file_parm.open_cb(&dev_log.files[i].file_parm, &dev_log.files[i]); |
| if (err) |
| { |
| DEV_LOG_ERROR_PRINTF("file%d: open failed: %s (%d)\n\n", i, bcmos_strerror(err), err ); |
| continue; |
| } |
| DEV_LOG_INFO_PRINTF("file: start_addr: 0x%p, size: %u, max msg size %u\n", |
| dev_log.files[i].file_parm.start_addr, dev_log.files[i].file_parm.size, MAX_DEV_LOG_STRING_SIZE); |
| } |
| |
| /* Create pool */ |
| err = bcmos_msg_pool_create(&dev_log.pool, pool_parm); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't create pool (error: %d)\n", (int)err); |
| return err; |
| } |
| |
| /* Create message queues */ |
| err = bcmos_msg_queue_create(&dev_log.save_queue, save_queue_parm); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("(%s) Error: Can't create save queue (error: %d)\n", __FUNCTION__, (int)err); |
| return err; |
| } |
| err = bcmos_msg_queue_create(&dev_log.print_queue, print_queue_parm); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("(%s) Error: Can't create print queue (error: %d)\n", __FUNCTION__, (int)err); |
| return err; |
| } |
| |
| /* Create tasks */ |
| err = bcmos_task_create(&dev_log.save_task, save_task_parm); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't spawn save task (error: %d)\n", (int)err); |
| return err; |
| } |
| err = bcmos_task_create(&dev_log.print_task, print_task_parm); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't spawn print task (error: %d)\n", (int)err); |
| return err; |
| } |
| |
| bcmos_sem_create(&dev_log.save_task_is_terminated, 0, 0, "save_task_sem"); |
| bcmos_sem_create(&dev_log.print_task_is_terminated, 0, 0, "print_task_sem"); |
| |
| dev_log.state = BCM_DEV_LOG_STATE_ENABLED; |
| |
| return BCM_ERR_OK; |
| } |
| |
| void bcm_dev_log_destroy(void) |
| { |
| bcmos_errno err; |
| uint32_t i; |
| bcmos_msg_send_flags msg_flags; |
| |
| if (dev_log.state == BCM_DEV_LOG_STATE_UNINITIALIZED) |
| { |
| return; |
| } |
| |
| /* Send a shutdown message to each task. When received by the main loops, it will tear down the tasks gracefully. |
| * If the flag is set to tear down immediately, we'll send an URGENT teardown message, so it'll be handled before |
| * all oustanding log messages (the oustanding log messages will be freed during bcmos_msg_queue_destroy). */ |
| msg_flags = BCMOS_MSG_SEND_NOLIMIT; |
| if ((dev_log.flags & BCM_DEV_LOG_FLAG_DESTROY_IMMEDIATELY) != 0) |
| msg_flags |= BCMOS_MSG_SEND_URGENT; |
| |
| /* The save task depends on the print task, so we terminate the save task first. */ |
| bcmos_msg_send(&dev_log.save_queue, &shutdown_msg, msg_flags); |
| bcmos_sem_wait(&dev_log.save_task_is_terminated, BCMOS_WAIT_FOREVER); |
| |
| bcmos_msg_send(&dev_log.print_queue, &shutdown_msg, msg_flags); |
| bcmos_sem_wait(&dev_log.print_task_is_terminated, BCMOS_WAIT_FOREVER); |
| |
| bcmos_sem_destroy(&dev_log.save_task_is_terminated); |
| bcmos_sem_destroy(&dev_log.print_task_is_terminated); |
| |
| err = bcmos_task_destroy(&dev_log.save_task); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't destroy dev_log save task (error: %d)\n", (int)err); |
| } |
| |
| err = bcmos_task_destroy(&dev_log.print_task); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't destroy dev_log print task (error: %d)\n", (int)err); |
| } |
| |
| err = bcmos_msg_queue_destroy(&dev_log.save_queue); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't destroy save queue (error: %d)\n", (int)err); |
| } |
| |
| err = bcmos_msg_queue_destroy(&dev_log.print_queue); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't destroy print queue (error: %d)\n", (int)err); |
| } |
| |
| err = bcmos_msg_pool_destroy(&dev_log.pool); |
| if (err != BCM_ERR_OK) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: Can't destroy pool (error: %d)\n", (int)err); |
| } |
| |
| for (i = 0; (i < DEV_LOG_MAX_FILES) && (dev_log.dev_log_parm.log_file[i].flags & BCM_DEV_LOG_FILE_FLAG_VALID); i++) |
| { |
| dev_log.files[i].file_parm.close_cb(&dev_log.files[i]); |
| } |
| |
| dev_log.state = BCM_DEV_LOG_STATE_UNINITIALIZED; |
| } |
| |
| typedef struct |
| { |
| bcmos_msg msg; |
| dev_log_queue_msg log_queue_msg; |
| bcmos_bool lock; |
| bcmos_fastlock fastlock; |
| } bcm_dev_log_drop_msg; |
| |
| typedef struct |
| { |
| bcm_dev_log_drop_msg msg; |
| uint32_t drops; |
| uint32_t reported_drops; |
| bcmos_timer timer; |
| uint32_t last_report_timestamp; |
| } bcm_dev_log_drop; |
| |
| static bcm_dev_log_drop dev_log_drop; |
| |
| static void bcm_dev_log_log_message_release_drop(bcmos_msg *m) |
| { |
| unsigned long flags; |
| |
| flags = bcmos_fastlock_lock(&dev_log_drop.msg.fastlock); |
| dev_log_drop.msg.lock = BCMOS_FALSE; |
| bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags); |
| |
| /* Schedule a timer to report extra drops, because we may not meet DEV_LOG_DROP_REPORT_DROP_THRESHOLD soon. */ |
| bcmos_timer_start(&dev_log_drop.timer, DEV_LOG_DROP_REPORT_RATE_US); |
| } |
| |
| static void _bcm_dev_log_drop_report(void) |
| { |
| bcmos_msg *msg; |
| dev_log_queue_msg *log_queue_msg; |
| |
| msg = &dev_log_drop.msg.msg; |
| msg->release = bcm_dev_log_log_message_release_drop; |
| log_queue_msg = &dev_log_drop.msg.log_queue_msg; |
| log_queue_msg->flags = BCM_LOG_FLAG_DONT_SKIP_PRINT; |
| log_queue_msg->time_stamp = dev_log.dev_log_parm.get_time_cb(); |
| log_queue_msg->msg_level = DEV_LOG_LEVEL_ERROR; |
| log_queue_msg->u.fmt_args.fmt = "Log message pool exhausted, dropped message count=%u\n"; |
| log_queue_msg->u.fmt_args.args[0] = (const void *)(unsigned long)(dev_log_drop.drops - dev_log_drop.reported_drops); |
| log_queue_msg->u.fmt_args.args[1] = |
| log_queue_msg->u.fmt_args.args[0]; /* args[0] may or may not be skipped depending on alignment */ |
| log_queue_msg->log_id = (dev_log_id_parm *)def_log_id; |
| log_queue_msg->time_stamp = dev_log.dev_log_parm.get_time_cb(); |
| dev_log_drop.reported_drops = dev_log_drop.drops; |
| dev_log_drop.last_report_timestamp = bcmos_timestamp(); |
| |
| msg->data = log_queue_msg; |
| msg->size = sizeof(*log_queue_msg); |
| bcmos_msg_send(&dev_log.save_queue, msg, 0); |
| } |
| |
| void bcm_dev_log_drop_report(void) |
| { |
| unsigned long flags; |
| |
| dev_log_drop.drops++; |
| |
| flags = bcmos_fastlock_lock(&dev_log_drop.msg.fastlock); |
| /* 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 |
| * DEV_LOG_DROP_REPORT_RATE_US. */ |
| if (dev_log_drop.msg.lock || |
| ((dev_log_drop.drops - dev_log_drop.reported_drops < DEV_LOG_DROP_REPORT_DROP_THRESHOLD) && |
| (bcmos_timestamp() - dev_log_drop.last_report_timestamp < DEV_LOG_DROP_REPORT_RATE_US))) |
| { |
| bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags); |
| return; |
| } |
| |
| dev_log_drop.msg.lock = BCMOS_TRUE; |
| bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags); |
| |
| /* Do the actual report. */ |
| _bcm_dev_log_drop_report(); |
| } |
| |
| static bcmos_timer_rc bcm_dev_log_drop_timer_cb(bcmos_timer *timer, long data) |
| { |
| unsigned long flags; |
| |
| flags = bcmos_fastlock_lock(&dev_log_drop.msg.fastlock); |
| if (dev_log_drop.msg.lock) |
| { |
| bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags); |
| /* Logger task hasn't released the lock yet (the drop message hasn't been reported yet) -> reschedule the timer. */ |
| bcmos_timer_start(&dev_log_drop.timer, DEV_LOG_DROP_REPORT_RATE_US); |
| } |
| else |
| { |
| if (dev_log_drop.drops == dev_log_drop.reported_drops) |
| { |
| /* No new drops to report. */ |
| bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags); |
| return BCMOS_TIMER_OK; |
| } |
| dev_log_drop.msg.lock = BCMOS_TRUE; |
| bcmos_fastlock_unlock(&dev_log_drop.msg.fastlock, flags); |
| |
| /* Do the actual report. */ |
| _bcm_dev_log_drop_report(); |
| } |
| |
| return BCMOS_TIMER_OK; |
| } |
| |
| bcmos_errno bcm_dev_log_init_default_logger_ext( |
| bcm_dev_log_parm *dev_log_parm, |
| uint32_t num_files, |
| uint32_t stack_size, |
| bcmos_task_priority priority, |
| uint32_t pool_size) |
| { |
| bcmos_errno err; |
| bcmos_task_parm save_task_parm = {0}; |
| bcmos_task_parm print_task_parm = {0}; |
| bcmos_msg_queue_parm print_queue_parm = {0}; |
| bcmos_msg_queue_parm save_queue_parm = {0}; |
| bcmos_msg_pool_parm pool_parm = {0}; |
| bcmos_timer_parm timer_params = |
| { |
| .name = "dev_log_drop_timer", |
| .owner = BCMOS_MODULE_ID_NONE, /* Will be called in ISR context */ |
| .handler = bcm_dev_log_drop_timer_cb, |
| }; |
| |
| if (!dev_log_parm) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: dev_log_parm must be set\n"); |
| return BCM_ERR_PARM; |
| } |
| |
| save_task_parm.priority = priority; |
| save_task_parm.stack_size = stack_size; |
| save_task_parm.name = "dev_log_save"; |
| save_task_parm.handler = dev_log_save_task; |
| save_task_parm.data = 0; |
| |
| print_task_parm.priority = priority; |
| print_task_parm.stack_size = stack_size; |
| print_task_parm.name = "dev_log_print"; |
| print_task_parm.handler = dev_log_print_task; |
| print_task_parm.data = 0; |
| |
| /* It is important to avoid terminating logger task during an exception, as logger task is low priority and might have backlog of unhandled messages. |
| * 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 |
| * CLI. */ |
| save_task_parm.flags = BCMOS_TASK_FLAG_NO_SUSPEND_ON_OOPS; |
| print_task_parm.flags = BCMOS_TASK_FLAG_NO_SUSPEND_ON_OOPS; |
| |
| save_queue_parm.name = "dev_log_save"; |
| save_queue_parm.size = 0; /* unlimited */ |
| print_queue_parm.name = "dev_log_print"; |
| print_queue_parm.size = 0; /* unlimited */ |
| |
| pool_parm.name = "dev_log"; |
| pool_parm.size = pool_size; |
| pool_parm.data_size = sizeof(dev_log_queue_msg); |
| |
| def_log_id = bcm_dev_log_id_register("default", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH); |
| err = bcmos_timer_create(&dev_log_drop.timer, &timer_params); |
| BCMOS_TRACE_CHECK_RETURN(err, err, "bcmos_timer_create"); |
| bcmos_fastlock_init(&dev_log_drop.msg.fastlock, 0); |
| |
| if (!dev_log_parm->get_time_cb) |
| dev_log_parm->get_time_cb = bcmos_timestamp; |
| |
| err = bcm_dev_log_create( |
| dev_log_parm, |
| &save_task_parm, |
| &print_task_parm, |
| &save_queue_parm, |
| &print_queue_parm, |
| &pool_parm); |
| BCMOS_TRACE_CHECK_RETURN(err, err, "bcm_dev_log_create"); |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_dev_log_init_default_logger(void **start_addresses, |
| uint32_t *sizes, |
| bcm_dev_log_file_flags *flags, |
| uint32_t num_files, |
| uint32_t stack_size, |
| bcmos_task_priority priority, |
| uint32_t pool_size) |
| { |
| bcm_dev_log_parm dev_log_parm = {0}; |
| uint32_t i; |
| |
| for (i = 0; i < num_files; i++) |
| { |
| dev_log_parm.log_file[i].start_addr = start_addresses[i]; |
| dev_log_parm.log_file[i].size = sizes[i]; |
| /* size[i] might be 0 in simulation build for sram buffer. */ |
| dev_log_parm.log_file[i].flags = (sizes[i] ? BCM_DEV_LOG_FILE_FLAG_VALID : BCM_DEV_LOG_FILE_FLAG_NONE) | |
| (flags ? flags[i] : BCM_DEV_LOG_FILE_FLAG_WRAP_AROUND); |
| } |
| |
| return bcm_dev_log_init_default_logger_ext(&dev_log_parm, num_files, stack_size, priority, |
| pool_size); |
| } |
| |
| log_name_table logs_names[DEV_LOG_MAX_IDS]; |
| uint8_t log_name_table_index = 0; |
| |
| static void dev_log_add_log_name_to_table(const char *xi_name) |
| { |
| char name[MAX_DEV_LOG_ID_NAME + 1] = {}; |
| char *p = name; |
| char *p_end; |
| int i = 0; |
| long val = -1; |
| |
| strncpy(name, xi_name, MAX_DEV_LOG_ID_NAME); |
| while (*p) |
| { |
| /* While there are more characters to process... */ |
| if (isdigit(*p)) |
| { |
| /* Upon finding a digit, ... */ |
| val = strtol(p, &p_end, 10); /* Read a number, ... */ |
| if ((name + strlen(name)) != p_end) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: dev_log_add_log_name_to_table %s)\n", xi_name); |
| } |
| *p = '\0'; |
| break; |
| |
| } |
| else |
| { |
| /* Otherwise, move on to the next character. */ |
| p++; |
| } |
| } |
| for (i = 0; i < log_name_table_index; i++) |
| { |
| if (strcmp(name, logs_names[i].name) == 0) |
| { |
| if (val < logs_names[i].first_instance) |
| { |
| logs_names[i].first_instance = val; |
| } |
| if (val > logs_names[i].last_instance) |
| { |
| logs_names[i].last_instance = val; |
| } |
| break; |
| } |
| } |
| if ((i == log_name_table_index) && (i < DEV_LOG_MAX_IDS)) |
| { |
| strncpy(logs_names[i].name, name, MAX_DEV_LOG_ID_NAME); |
| if (val == -1) |
| { |
| val = LOG_NAME_NO_INSTANCE; |
| } |
| logs_names[i].last_instance = val; |
| logs_names[i].first_instance = val; |
| log_name_table_index++; |
| } |
| } |
| |
| void dev_log_get_log_name_table(char *buffer, uint32_t buf_len) |
| { |
| uint32_t i = 0; |
| uint32_t buf_len_free = buf_len; |
| |
| buffer[0] = '\0'; |
| for (i = 0; i < log_name_table_index; i++) |
| { |
| if (logs_names[i].first_instance == LOG_NAME_NO_INSTANCE) |
| { |
| snprintf(&buffer[strlen(buffer)], buf_len_free, "%s\n", logs_names[i].name); |
| } |
| else |
| { |
| 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); |
| } |
| buffer[buf_len - 1] = '\0'; |
| buf_len_free = buf_len - strlen(buffer); |
| if (buf_len_free <= 1) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: dev_log_get_log_name_table too long\n"); |
| break; |
| } |
| } |
| } |
| |
| dev_log_id bcm_dev_log_id_register(const char *xi_name, |
| bcm_dev_log_level xi_default_log_level, |
| bcm_dev_log_id_type xi_default_log_type) |
| { |
| dev_log_id_parm *new_id; |
| |
| /* Check that we have room for one more ID */ |
| if (dev_log.num_ids == BCM_SIZEOFARRAY(dev_log.ids)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: wrong parameters\n"); |
| return DEV_LOG_INVALID_ID; |
| } |
| |
| /* Get next free log_id in array and increment next_id. |
| * The new_id structure is clean because the whole dev_log array is in BSS |
| */ |
| new_id = &dev_log.ids[dev_log.num_ids++]; |
| |
| /* Add prefix for kernel log_ids in order to avoid clash with similarly-names logs in the user space */ |
| #ifdef __KERNEL__ |
| strcpy(new_id->name, "k_"); |
| #endif |
| |
| /* Set log_id parameters */ |
| strncat(new_id->name, xi_name, sizeof(new_id->name)-strlen(new_id->name)); |
| new_id->name[MAX_DEV_LOG_ID_NAME-1] = '\0'; |
| |
| new_id->default_log_type = xi_default_log_type; |
| new_id->default_log_level = xi_default_log_level; |
| |
| dev_log_add_log_name_to_table(new_id->name); |
| |
| bcm_dev_log_id_set_levels_and_type_to_default((dev_log_id)new_id); |
| new_id->style = BCM_DEV_LOG_STYLE_NORMAL; |
| |
| return (dev_log_id)new_id; |
| } |
| |
| bcmos_errno bcm_dev_log_id_set_type(dev_log_id xi_id, bcm_dev_log_id_type xi_log_type) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| |
| /* In linux kernel we only support save */ |
| #ifdef __KERNEL__ |
| if (xi_log_type & DEV_LOG_ID_TYPE_BOTH) |
| xi_log_type = DEV_LOG_ID_TYPE_SAVE; |
| #endif |
| |
| DEV_LOG_INFO_PRINTF("ID: 0x%lx, New log type: %d\n", xi_id, (int)xi_log_type); |
| |
| id->log_type = xi_log_type; |
| |
| /* In linux user space we need to notify kernel integration code */ |
| #if defined(LINUX_KERNEL_SPACE) && !defined(__KERNEL__) |
| bcm_dev_log_linux_id_set_type(xi_id, xi_log_type); |
| #endif |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_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) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| |
| if ((xi_log_level_print >= DEV_LOG_LEVEL_NUM_OF) || (xi_log_level_save >= DEV_LOG_LEVEL_NUM_OF)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: wrong parameters\n"); |
| return BCM_ERR_PARM; |
| } |
| |
| DEV_LOG_INFO_PRINTF("ID: 0x%p, New: xi_log_level_print=%u, xi_log_level_save=%u\n", |
| (void *)id, |
| xi_log_level_print, |
| xi_log_level_save); |
| |
| id->log_level_print = xi_log_level_print; |
| id->log_level_save = xi_log_level_save; |
| |
| /* In linux user space we need to notify kernel integration code */ |
| #if defined(LINUX_KERNEL_SPACE) && !defined(__KERNEL__) |
| bcm_dev_log_linux_id_set_level(xi_id, xi_log_level_print, xi_log_level_save); |
| #endif |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_dev_log_id_set_levels_and_type_to_default(dev_log_id xi_id) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| |
| id->log_level_print = id->default_log_level; |
| id->log_level_save = id->default_log_level; |
| id->log_type = id->default_log_type; |
| |
| #ifdef TRIGGER_LOGGER_FEATURE |
| memset(&id->throttle,0, sizeof(dev_log_id_throttle)); |
| memset(&id->trigger, 0, sizeof(dev_log_id_trigger)); |
| id->throttle_log_level = DEV_LOG_LEVEL_NO_LOG; |
| id->trigger_log_level = DEV_LOG_LEVEL_NO_LOG; |
| #endif |
| |
| /* In linux kernel space we don't support PRINT, only SAVE */ |
| #ifdef __KERNEL__ |
| if (id->log_type & DEV_LOG_ID_TYPE_BOTH) |
| id->log_type = DEV_LOG_ID_TYPE_SAVE; |
| #endif |
| |
| return BCM_ERR_OK; |
| } |
| |
| void bcm_dev_log_level_set_style(bcm_dev_log_level level, bcm_dev_log_style xi_style) |
| { |
| dev_log_level2style[level] = xi_style; |
| } |
| |
| bcmos_errno bcm_dev_log_id_set_style(dev_log_id xi_id, bcm_dev_log_style xi_style) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| id->style = xi_style; |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_dev_log_id_clear_counters(dev_log_id xi_id) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| memset(id->counters, 0, sizeof(id->counters)); |
| id->lost_msg_cnt = 0; |
| id->print_skipped_count = 0; |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_dev_log_id_get(dev_log_id xi_id, dev_log_id_parm *xo_id) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| |
| *xo_id = *id; |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_dev_log_id_set(dev_log_id xi_id, dev_log_id_parm *parms) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| |
| *id = *parms; |
| |
| return BCM_ERR_OK; |
| } |
| |
| dev_log_id bcm_dev_log_id_get_by_index(uint32_t xi_index) |
| { |
| if (xi_index >= dev_log.num_ids) |
| { |
| return DEV_LOG_INVALID_ID; |
| } |
| return (dev_log_id)&(dev_log.ids[xi_index]); |
| } |
| |
| uint32_t bcm_dev_log_get_index_by_id(dev_log_id xi_id) |
| { |
| uint32_t idx = (dev_log_id_parm *)xi_id - &dev_log.ids[0]; |
| if (idx >= dev_log.num_ids) |
| idx = DEV_LOG_INVALID_INDEX; |
| return idx; |
| } |
| |
| dev_log_id bcm_dev_log_id_get_by_name(const char *xi_name) |
| { |
| uint32_t i; |
| |
| if (xi_name == NULL) |
| { |
| return DEV_LOG_INVALID_ID; |
| } |
| |
| for (i = 0; i<dev_log.num_ids; i++) |
| { |
| if (!strcmp(dev_log.ids[i].name, xi_name)) |
| { |
| return (dev_log_id)&(dev_log.ids[i]); |
| } |
| } |
| |
| DEV_LOG_ERROR_PRINTF("Error: can't find name\n"); |
| return DEV_LOG_INVALID_ID; |
| } |
| |
| uint32_t bcm_dev_log_get_num_of_entries(void) |
| { |
| return dev_log.num_ids; |
| } |
| |
| bcmos_bool bcm_dev_log_get_control(void) |
| { |
| return dev_log.state == BCM_DEV_LOG_STATE_ENABLED; |
| } |
| |
| uint32_t bcm_dev_log_get_num_of_messages(bcm_dev_log_file *file) |
| { |
| return file->u.mem_file.file_header.num_msgs; |
| } |
| |
| void bcm_dev_log_set_control(bcmos_bool control) |
| { |
| if (control && dev_log.state == BCM_DEV_LOG_STATE_DISABLED) |
| dev_log.state = BCM_DEV_LOG_STATE_ENABLED; |
| else if (!control && dev_log.state == BCM_DEV_LOG_STATE_ENABLED) |
| dev_log.state = BCM_DEV_LOG_STATE_DISABLED; |
| } |
| |
| bcm_dev_log_file_flags bcm_dev_log_get_file_flags(uint32_t file_id) |
| { |
| return dev_log.files[file_id].file_parm.flags; |
| } |
| |
| void bcm_dev_log_set_file_flags(uint32_t file_id, bcm_dev_log_file_flags flags) |
| { |
| dev_log.files[file_id].file_parm.flags = flags; |
| } |
| |
| bcm_dev_log_flags bcm_dev_log_get_flags(void) |
| { |
| return dev_log.flags; |
| } |
| |
| void bcm_dev_log_set_flags(bcm_dev_log_flags flags) |
| { |
| dev_log.flags = flags; |
| } |
| |
| void bcm_dev_log_set_print_cb(bcm_dev_log_print_cb cb) |
| { |
| dev_log.dev_log_parm.print_cb = cb; |
| } |
| |
| void bcm_dev_log_set_get_time_cb(bcm_dev_log_get_time_cb cb) |
| { |
| dev_log.dev_log_parm.get_time_cb = cb; |
| } |
| |
| void bcm_dev_log_set_time_to_str_cb(bcm_dev_log_time_to_str_cb cb) |
| { |
| dev_log.dev_log_parm.time_to_str_cb = cb; |
| } |
| |
| #ifdef TRIGGER_LOGGER_FEATURE |
| /********************************************************************************************/ |
| /* */ |
| /* Name: bcm_dev_log_set_throttle */ |
| /* */ |
| /* Abstract: Set throttle level for the specific log is and its level */ |
| /* */ |
| /* Arguments: */ |
| /* - xi_id - The ID in the Dev log (what we got form bcm_dev_log_id_register) */ |
| /* - xi_log_level - log level */ |
| /* - xi_throttle - throttle number, 0 - no throttle */ |
| /* */ |
| /* Return Value: */ |
| /* bcmos_errno - Success code (BCM_ERR_OK) or Error code (see bcmos_errno.h) */ |
| /* */ |
| /********************************************************************************************/ |
| bcmos_errno bcm_dev_log_set_throttle(dev_log_id xi_id, bcm_dev_log_level xi_log_level, uint32_t xi_throttle) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| id->throttle_log_level = xi_log_level; |
| id->throttle.threshold = xi_throttle; |
| id->throttle.counter = 0; |
| |
| return BCM_ERR_OK; |
| } |
| |
| /********************************************************************************************/ |
| /* */ |
| /* Name: bcm_dev_log_set_trigger */ |
| /* */ |
| /* Abstract: Set trigger counters for the specific log is and its level */ |
| /* */ |
| /* Arguments: */ |
| /* - xi_id - The ID in the Dev log (what we got form bcm_dev_log_id_register) */ |
| /* - xi_log_level - log level */ |
| /* - xi_start - start printing after this number, 0 - no trigger */ |
| /* - xi_stop - stop printing after this number, 0 - do not stop */ |
| /* - xi_repeat - start printing after this number, 0 - no repeat, -1 always */ |
| /* */ |
| /* Return Value: */ |
| /* bcmos_errno - Success code (BCM_ERR_OK) or Error code (see bcmos_errno.h) */ |
| /* */ |
| /********************************************************************************************/ |
| bcmos_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) |
| { |
| dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| |
| if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| { |
| DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| return BCM_ERR_PARM; |
| } |
| id->trigger_log_level = xi_log_level; |
| id->trigger.start_threshold = xi_start; |
| id->trigger.stop_threshold = xi_stop; |
| id->trigger.repeat_threshold = xi_repeat; |
| id->trigger.counter = 0; |
| id->trigger.repeat = 0; |
| |
| return BCM_ERR_OK; |
| } |
| #endif /* TRIGGER_LOGGER_FEATURE */ |
| |
| /* Get file info: max data size, used bytes */ |
| bcmos_errno bcm_dev_log_get_file_info(bcm_dev_log_file *file, uint32_t *file_size, uint32_t *used_bytes) |
| { |
| if (!file || !file_size || !used_bytes) |
| return BCM_ERR_PARM; |
| /* Only supported for memory files */ |
| if (!bcm_dev_log_is_memory_file(file)) |
| return BCM_ERR_NOT_SUPPORTED; |
| *file_size = file->u.mem_file.file_header.data_size; |
| *used_bytes = (file->u.mem_file.file_header.file_wrap_cnt || file->is_full) ? |
| file->u.mem_file.file_header.data_size : file->u.mem_file.file_header.write_offset; |
| return BCM_ERR_OK; |
| } |
| |
| /* Register indication to be sent when file utilization crosses threshold */ |
| bcmos_errno bcm_dev_log_almost_full_ind_register(bcm_dev_log_file *file, uint32_t used_bytes_threshold, |
| F_dev_log_file_almost_full send_ind_cb, long ind_cb_priv) |
| { |
| if (!file || (used_bytes_threshold && !send_ind_cb)) |
| return BCM_ERR_PARM; |
| /* Only supported for memory files */ |
| if (!bcm_dev_log_is_memory_file(file)) |
| return BCM_ERR_NOT_SUPPORTED; |
| |
| bcmos_mutex_lock(&file->u.mem_file.mutex); |
| file->almost_full.threshold = used_bytes_threshold; |
| file->almost_full.send_ind_cb = send_ind_cb; |
| file->almost_full.priv = ind_cb_priv; |
| file->almost_full.ind_sent = BCMOS_FALSE; |
| bcmos_mutex_unlock(&file->u.mem_file.mutex); |
| |
| return BCM_ERR_OK; |
| } |
| |
| #endif /* ENABLE_LOG */ |