| /* |
| <: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. |
| |
| :> |
| */ |
| |
| #include <bcmos_system.h> |
| #include <bcmtr_debug.h> |
| #include <bcmcli_session.h> |
| #include <bcm_api_cli_helpers.h> |
| #include <bcmolt_math.h> |
| |
| #ifdef ENABLE_LOG |
| #include <bcm_dev_log.h> |
| #endif |
| |
| #ifdef ENABLE_CLI |
| #include <bcmtr_debug_cli.h> |
| #endif |
| |
| /* Messages are recorded in the following format: |
| * uint32_t - event type |
| * uint32_t - timestamp |
| * uint32_t - message data size |
| * <message data> - padded to the nearest 4 bytes |
| * uint32_t - total size - total capture entry size, including control info |
| */ |
| |
| /* Overhead size: entry header + uint32_t suffix */ |
| #define BCMTR_CAPTURE_OVERHEAD_SIZE (sizeof(bcmtr_capture_entry) + sizeof(uint32_t)) |
| |
| #define BCMTR_CAPTURE_ENTRY_FIELDS (sizeof(bcmtr_capture_entry) / sizeof(uint32_t)) |
| |
| /* Maximum total number of characters for a message dump */ |
| #define BCMTR_MAX_MSG_DUMP_STR_SIZE 4096 |
| |
| /* Capture control block */ |
| typedef struct |
| { |
| bcmtr_capture_parm parm; /* Capture configuration */ |
| bcmos_bool active; |
| |
| uint8_t *start; |
| uint8_t *end; |
| uint8_t *cur; |
| uint32_t size; /* Buffer size */ |
| uint32_t used; /* Bytes used */ |
| uint32_t wa; /* Number of times buffer wrapped around */ |
| uint32_t events; /* Number of capture events */ |
| } bcmtr_capture_buf; |
| |
| static bcmtr_capture_buf capture_buf[BCMTR_MAX_OLTS]; |
| |
| /* CLI session where to dump */ |
| static bcmcli_session *bcmtr_cld_session; |
| |
| #ifdef ENABLE_LOG |
| /* Logger used for BCMTR_CLD_LOG */ |
| static dev_log_id bcmtr_cld_log_id; |
| #endif |
| |
| /* Global variable: per msg_id CLD level */ |
| bcmtr_cld_type bcmtr_cld_active_level[BCMTR_MAX_OLTS][BCMOLT_GROUP_ID__NUM_OF]; |
| |
| /* Create a dummy CLI session so we can print to a buffer internally before printing to the real CLI. |
| * This way, the output can't be interrupted by another print. */ |
| static char bcmtr_cld_scratch_buf[BCMTR_MAX_MSG_DUMP_STR_SIZE]; |
| static uint32_t bcmtr_cld_scratch_pos = 0; |
| static bcmcli_session *bcmtr_cld_scratch_session; |
| |
| /* |
| * Internal functions |
| */ |
| |
| /* CLI session print callback for the scratch buffer */ |
| static int bcmtr_log_cli_print(bcmcli_session *session, const char *buf, uint32_t size) |
| { |
| size = MIN(size, BCMTR_MAX_MSG_DUMP_STR_SIZE - bcmtr_cld_scratch_pos); |
| if (size > 0) |
| { |
| memcpy(&bcmtr_cld_scratch_buf[bcmtr_cld_scratch_pos], buf, size); |
| } |
| bcmtr_cld_scratch_pos += size; |
| return size; |
| } |
| |
| /* Get message event name */ |
| static inline const char *bcmtr_cld_event_name(bcmtr_cld_event_type ev) |
| { |
| static const char *ev_name[] = { |
| [BCMTR_CLD_EV_SEND] = "tx", |
| [BCMTR_CLD_EV_RESEND] = "re-tx", |
| [BCMTR_CLD_EV_RECV] = "rx", |
| [BCMTR_CLD_EV_RECV_DISCARD] = "rx-discard", |
| [BCMTR_CLD_EV_TIMEOUT] = "timeout" |
| }; |
| return ev_name[ev]; |
| } |
| |
| /* Store data in capture buffer */ |
| static inline void _bcmtr_capture_store(bcmtr_capture_buf *tb, const void *data, uint32_t size) |
| { |
| int32_t left = (int32_t)(tb->end - tb->cur); |
| if (left >= (int32_t)size) |
| { |
| memcpy(tb->cur, data, size); |
| tb->cur += size; |
| } |
| else |
| { |
| memcpy(tb->cur, data, left); |
| memcpy(tb->start, (const uint8_t *)data + left, size - left); |
| tb->cur = tb->start + size - left; |
| ++tb->wa; |
| } |
| tb->used += size; |
| if (tb->used > tb->size) |
| tb->used = tb->size; |
| } |
| |
| |
| /* Get capture entry size and start pointer given pointer right after the entry */ |
| static void _bcmtr_capture_get_prev(bcmtr_capture_buf *tb, uint8_t *ptr, uint32_t *prev_size, uint8_t **prev_ptr) |
| { |
| uint32_t size; |
| uint8_t *prev; |
| |
| if (ptr == tb->start) |
| ptr = tb->end; |
| size = *(((uint32_t *)(long)ptr) - 1); |
| BUG_ON(!size || size > 0xffff || (size & 0x3)); |
| prev = ptr - size; |
| if (prev < tb->start) |
| prev = tb->end - (size - (ptr - tb->start)); |
| *prev_size = size; |
| *prev_ptr = prev; |
| } |
| |
| /* Get number of complete messages stored in capture buffer */ |
| static uint32_t _bcmtr_capture_nmsgs(bcmtr_capture_buf *tb) |
| { |
| uint32_t n = 0; |
| uint32_t prev_length; |
| uint8_t *prev_start = tb->cur; |
| |
| if (!tb->used) |
| return 0; |
| |
| /* Unwind capture buffer backward */ |
| while (n < tb->events) |
| { |
| uint8_t *prev = prev_start; |
| _bcmtr_capture_get_prev(tb, prev, &prev_length, &prev_start); |
| if (prev_start >= prev) |
| break; |
| ++n; |
| } |
| |
| /* If buffer has wrapped around - continue unwinding */ |
| if (tb->wa) |
| { |
| while (prev_start >= tb->cur) |
| { |
| _bcmtr_capture_get_prev(tb, prev_start, &prev_length, &prev_start); |
| ++n; |
| } |
| } |
| |
| return n; |
| } |
| |
| static inline void _bcmtr_capture_wrap(uint8_t **cur, bcmtr_capture_buf *tb) |
| { |
| if (*cur > tb->end) |
| { |
| *cur = tb->start + (*cur - tb->end); |
| } |
| } |
| static void _bcmtr_capture_unwind(bcmtr_capture_buf *tb, uint8_t **start, uint32_t *count) |
| { |
| uint32_t prev_length; |
| uint8_t *prev_start; |
| uint32_t n = 0; |
| uint8_t *cur_hdr = NULL; |
| |
| prev_start = tb->cur; |
| while (n < tb->events) |
| { |
| uint8_t *prev = prev_start; |
| _bcmtr_capture_get_prev(tb, prev, &prev_length, &prev_start); |
| if (prev_start >= prev) |
| break; |
| ++n; |
| cur_hdr = prev_start; |
| } |
| |
| /* If buffer has wrapped around - continue unwinding */ |
| if (tb->wa) |
| { |
| while (prev_start >= tb->cur) |
| { |
| cur_hdr = prev_start; |
| _bcmtr_capture_get_prev(tb, prev_start, &prev_length, &prev_start); |
| ++n; |
| } |
| } |
| |
| *start = cur_hdr; |
| *count = n; |
| } |
| |
| static inline uint32_t _bcmtr_capture_msg_size_get(uint8_t *buf) |
| { |
| /* WARNING: do NOT access any members of bcmtr_capture_entry other than msg_size (the first member) as they may |
| have been wrapped to the beginning of the buffer. */ |
| return ((bcmtr_capture_entry *)(long)buf)->msg_size; |
| } |
| |
| static void _bcmtr_capture_copy( |
| bcmtr_capture_buf *tb, |
| uint8_t **dst, |
| uint8_t *src, |
| uint32_t to_copy, |
| uint32_t *remaining) |
| { |
| uint32_t left; |
| |
| left = tb->end - src; |
| if (left < to_copy) |
| { |
| memcpy(*dst, src, left); |
| memcpy((*dst) + left, tb->start, to_copy - left); |
| } |
| else |
| { |
| memcpy(*dst, src, to_copy); |
| } |
| (*dst) += to_copy; |
| (*remaining) -= to_copy; |
| } |
| |
| static void _bcmtr_capture_copy_bounded( |
| bcmtr_capture_buf *tb, |
| uint8_t **dst, |
| uint8_t *src, |
| uint32_t to_copy, |
| uint32_t *remaining, |
| uint32_t bound) |
| { |
| if (bound < to_copy) |
| { |
| src += to_copy - bound; |
| to_copy = bound; |
| } |
| if ((*remaining) < to_copy) |
| { |
| to_copy = *remaining; |
| } |
| _bcmtr_capture_copy(tb, dst, src, to_copy, remaining); |
| } |
| |
| /* Set capture, log, debug for selected messages */ |
| bcmos_errno bcmtr_cld_level_set(bcmolt_devid device, const bcmtr_cld_filter *filter, bcmtr_cld_type cld_level) |
| { |
| bcmolt_group_id msg_id; |
| bcmos_errno rc; |
| |
| if ((unsigned)device >= BCMTR_MAX_OLTS || !filter) |
| { |
| return BCM_ERR_PARM; |
| } |
| |
| /* Handle wildcard object */ |
| if (filter->object == BCMOLT_OBJECT_ANY) |
| { |
| bcmtr_cld_filter f = *filter; |
| |
| for (f.object = 0; f.object <= BCMOLT_OBJ_ID__NUM_OF; f.object++) |
| { |
| bcmtr_cld_level_set(device, &f, cld_level); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /* Handle wildcard group */ |
| if (filter->group == BCMOLT_MGT_GROUP_ANY) |
| { |
| bcmtr_cld_filter f = *filter; |
| |
| f.subgroup = BCMOLT_SUBGROUP_ANY; |
| for (f.group = BCMOLT_MGT_GROUP_CFG; f.group <= BCMOLT_MGT_GROUP__NUM_OF; f.group++) |
| { |
| bcmtr_cld_level_set(device, &f, cld_level); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /* Handle wildcard subgroup */ |
| if (filter->group == BCMOLT_MGT_GROUP_ANY || filter->subgroup == BCMOLT_SUBGROUP_ANY) |
| { |
| bcmtr_cld_filter f = *filter; |
| |
| f.subgroup = 0; |
| for (f.subgroup = 0; |
| bcmolt_group_id_combine(f.object, f.group, f.subgroup, &msg_id) == BCM_ERR_OK; |
| f.subgroup++) |
| { |
| bcmtr_cld_level_set(device, &f, cld_level); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /* If we are here - it is not a wildcard */ |
| rc = bcmolt_group_id_combine(filter->object, filter->group, filter->subgroup, &msg_id); |
| if (rc) |
| return rc; |
| |
| BUG_ON((unsigned)msg_id >= BCMOLT_GROUP_ID__NUM_OF); |
| bcmtr_cld_active_level[device][msg_id] = cld_level; |
| return BCM_ERR_OK; |
| } |
| |
| /* Get capture, log, debug for selected message */ |
| bcmos_errno bcmtr_cld_level_get(bcmolt_devid device, const bcmtr_cld_filter *filter, bcmtr_cld_type *cld_level) |
| { |
| bcmolt_group_id msg_id; |
| bcmos_errno rc; |
| if ((unsigned)device >= BCMTR_MAX_OLTS || !filter) |
| { |
| return BCM_ERR_PARM; |
| } |
| rc = bcmolt_group_id_combine(filter->object, filter->group, filter->subgroup, &msg_id); |
| if (rc) |
| return rc; |
| BUG_ON((unsigned)msg_id >= BCMOLT_GROUP_ID__NUM_OF); |
| *cld_level = bcmtr_cld_active_level[device][msg_id]; |
| return BCM_ERR_OK; |
| } |
| |
| /** Initialize capture */ |
| bcmos_errno bcmtr_capture_init(bcmolt_devid olt, const bcmtr_capture_parm *parm) |
| { |
| bcmtr_capture_buf *tb; |
| |
| if (olt >= BCMTR_MAX_OLTS || !parm) |
| return BCM_ERR_PARM; |
| if (capture_buf[olt].start) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: already initialized\n", olt); |
| return BCM_ERR_PARM; |
| } |
| |
| if (parm->size < BCMTR_CAPTURE_MIN_BUF_SIZE) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: capture buffer is too small (%u < %d)\n", |
| olt, parm->size, BCMTR_CAPTURE_MIN_BUF_SIZE); |
| return BCM_ERR_PARM; |
| } |
| |
| tb = &capture_buf[olt]; |
| tb->size = parm->size & ~0x3; |
| /* User-supplied or dynamically allocated buffer ? */ |
| if (parm->ptr) |
| tb->start = parm->ptr; |
| else |
| { |
| tb->start = bcmos_alloc(parm->size); |
| if (!tb->start) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: can't allocate capture buffer\n", olt); |
| tb->size = 0; |
| return BCM_ERR_NOMEM; |
| } |
| } |
| tb->end = (void *)((long)tb->start + parm->size); |
| tb->cur = tb->start; |
| tb->used = tb->wa = 0; |
| tb->active = parm->activate; |
| tb->parm = *parm; |
| |
| return BCM_ERR_OK; |
| } |
| |
| /** Destroy capture buffer */ |
| void bcmtr_capture_destroy(bcmolt_devid olt) |
| { |
| bcmtr_capture_buf *tb; |
| |
| if (olt >= BCMTR_MAX_OLTS) |
| return; |
| tb = &capture_buf[olt]; |
| tb->active = BCMOS_FALSE; |
| if (tb->start && !tb->parm.ptr) |
| bcmos_free(tb->start); |
| memset(tb, 0, sizeof(*tb)); |
| } |
| |
| /** Get capture recording info */ |
| bcmos_errno bcmtr_capture_info_get(bcmolt_devid olt, bcmtr_capture_info *info) |
| { |
| bcmtr_capture_buf *tb; |
| |
| if (olt >= BCMTR_MAX_OLTS || !info) |
| return BCM_ERR_PARM; |
| tb = &capture_buf[olt]; |
| if (tb->active) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: must stop first\n", olt); |
| return BCM_ERR_PARM; |
| } |
| info->size = tb->size; |
| info->used = tb->used; |
| info->wa = tb->wa; |
| info->msgs = _bcmtr_capture_nmsgs(tb); |
| info->lost = tb->events - info->msgs; |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcmtr_capture_size_get(bcmolt_devid olt, uint32_t *size) |
| { |
| bcmtr_capture_buf *tb; |
| uint32_t n = 0; |
| uint8_t *cur_hdr = NULL; |
| uint32_t i; |
| |
| *size = 0; |
| if (olt >= BCMTR_MAX_OLTS) |
| { |
| return BCM_ERR_PARM; |
| } |
| tb = &capture_buf[olt]; |
| if (!tb->start) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: not initialized\n", olt); |
| return BCM_ERR_PARM; |
| } |
| if (tb->active) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: must stop first\n", olt); |
| return BCM_ERR_PARM; |
| } |
| |
| if (!tb->used) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| /* Unwind capture buffer backward to get to the 1st recorded message */ |
| _bcmtr_capture_unwind(tb, &cur_hdr, &n); |
| |
| /* "first" points to the 1st recorded entry and "n" contains number of messages. |
| * Now go forward and copy to the user buffer |
| */ |
| BUG_ON(!cur_hdr); |
| for (i = 0; i < n; i++) |
| { |
| uint32_t msg_size = _bcmtr_capture_msg_size_get(cur_hdr) + sizeof(bcmtr_capture_entry); |
| uint32_t rounded_size = BCMOS_ROUND_UP(msg_size, sizeof(uint32_t)); |
| |
| (*size) += msg_size; |
| |
| /* Move to the next entry in capture buffer */ |
| cur_hdr += rounded_size + sizeof(uint32_t); |
| _bcmtr_capture_wrap(&cur_hdr, tb); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcmtr_capture_read(bcmolt_devid olt, uint8_t *buf, uint32_t offset, uint32_t *length) |
| { |
| bcmtr_capture_buf *tb; |
| uint32_t n = 0; |
| uint8_t *cur_hdr = NULL; |
| uint32_t cur_offset = 0; |
| uint32_t i; |
| |
| if (olt >= BCMTR_MAX_OLTS || !buf) |
| { |
| return BCM_ERR_PARM; |
| } |
| tb = &capture_buf[olt]; |
| if (!tb->start) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: not initialized\n", olt); |
| return BCM_ERR_PARM; |
| } |
| if (tb->active) |
| { |
| bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: must stop first\n", olt); |
| return BCM_ERR_PARM; |
| } |
| |
| if (!tb->used) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| /* Unwind capture buffer backward to get to the 1st recorded message */ |
| _bcmtr_capture_unwind(tb, &cur_hdr, &n); |
| |
| /* "first" points to the 1st recorded entry and "n" contains number of messages. |
| * Now go forward and copy to the user buffer |
| */ |
| BUG_ON(!cur_hdr); |
| for (i = 0; (i < n) && ((*length) > 0); i++) |
| { |
| uint32_t msg_size = _bcmtr_capture_msg_size_get(cur_hdr); |
| uint32_t rounded_size = BCMOS_ROUND_UP(msg_size, sizeof(uint32_t)); |
| |
| cur_offset += sizeof(bcmtr_capture_entry); |
| if (cur_offset > offset) |
| { |
| uint32_t temp[BCMTR_CAPTURE_ENTRY_FIELDS]; |
| uint8_t j; |
| |
| for (j = 0; j < BCMTR_CAPTURE_ENTRY_FIELDS; j++) |
| { |
| temp[j] = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, *((uint32_t*)(long)cur_hdr)); |
| cur_hdr += sizeof(uint32_t); |
| _bcmtr_capture_wrap(&cur_hdr, tb); |
| } |
| |
| _bcmtr_capture_copy_bounded( |
| tb, |
| &buf, |
| (uint8_t*)temp, |
| sizeof(bcmtr_capture_entry), |
| length, |
| cur_offset - offset); |
| } |
| else |
| { |
| cur_hdr += sizeof(bcmtr_capture_entry); |
| } |
| |
| cur_offset += msg_size; |
| if (cur_offset > offset) |
| { |
| _bcmtr_capture_copy_bounded( |
| tb, |
| &buf, |
| cur_hdr, |
| msg_size, |
| length, |
| cur_offset - offset); |
| } |
| |
| /* Move to the next entry in capture buffer */ |
| cur_hdr += rounded_size + sizeof(uint32_t); |
| _bcmtr_capture_wrap(&cur_hdr, tb); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_bool bcmtr_capture_entry_get_next(bcmolt_buf *buf, bcmtr_capture_entry *hdr, uint8_t **msg) |
| { |
| bcmos_bool valid; |
| |
| BUG_ON(buf == NULL); |
| BUG_ON(hdr == NULL); |
| |
| valid = bcmtr_capture_entry_unpack(buf, hdr); |
| if (msg != NULL) |
| { |
| *msg = bcmolt_buf_snap_get(buf); |
| } |
| return valid && bcmolt_buf_skip(buf, hdr->msg_size); |
| } |
| |
| /** Decode and dump capture recording */ |
| bcmos_errno bcmtr_capture_dump(bcmcli_session *session, bcmolt_devid olt, uint32_t *nmsgs) |
| { |
| bcmtr_capture_entry hdr; |
| uint8_t *msg_start; |
| bcmolt_buf buf; |
| uint8_t *data; |
| uint32_t length; |
| uint32_t remaining; |
| bcmos_errno rc; |
| |
| rc = bcmtr_capture_size_get(olt, &length); |
| BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc); |
| |
| /* Allocate temp buffer and read data into it */ |
| data = bcmos_calloc(length); |
| if (data == NULL) |
| { |
| bcmcli_session_print(session, "TRACE/%d: no memory\n", olt); |
| return BCM_ERR_NOMEM; |
| } |
| |
| remaining = length; |
| rc = bcmtr_capture_read(olt, data, 0, &remaining); |
| if (BCM_ERR_OK != rc) |
| { |
| bcmos_free(data); |
| return rc; |
| } |
| |
| /* Dump */ |
| bcmolt_buf_init(&buf, length - remaining, data, BCMOLT_BUF_ENDIAN_FIXED); |
| while (bcmtr_capture_entry_get_next(&buf, &hdr, &msg_start)) |
| { |
| bcmolt_buf msg_buf; |
| bcmolt_msg *msg = NULL; |
| bcmos_errno err; |
| |
| bcmcli_session_print(session, "\n%08x %s:\n", hdr.timestamp, bcmtr_cld_event_name(hdr.event)); |
| bcmolt_buf_init(&msg_buf, hdr.msg_size, msg_start, BCMOLT_BUF_ENDIAN_FIXED); |
| err = bcmolt_msg_unpack(&msg_buf, &msg); |
| if (BCM_ERR_OK == err) |
| { |
| (void)apicli_msg_dump(session, msg); |
| bcmolt_msg_free(msg); |
| } |
| else |
| { |
| bcmcli_session_hexdump(session, msg_start, 0, hdr.msg_size, NULL); |
| } |
| } |
| |
| bcmos_free(data); |
| |
| *nmsgs = _bcmtr_capture_nmsgs(&capture_buf[olt]); |
| |
| return BCM_ERR_OK; |
| } |
| |
| /** (Re)start / Suspend capture recording */ |
| bcmos_errno bcmtr_capture_start_stop(bcmolt_devid olt, bcmos_bool start) |
| { |
| if (olt >= BCMTR_MAX_OLTS) |
| return BCM_ERR_PARM; |
| if (!capture_buf[olt].start && start) |
| { |
| bcmcli_session_print(bcmtr_cld_session, |
| "TRACE/%d: Can't start recording - must initialize first\n", olt); |
| return BCM_ERR_PARM; |
| } |
| capture_buf[olt].active = start; |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_bool bcmtr_capture_is_active(bcmolt_devid olt) |
| { |
| return capture_buf[olt].active; |
| } |
| |
| bcmos_bool bcmtr_capture_entry_unpack(bcmolt_buf *buf, bcmtr_capture_entry *entry) |
| { |
| return |
| bcmolt_buf_read_u32(buf, &entry->msg_size) && |
| bcmolt_buf_read_u32(buf, &entry->event) && |
| bcmolt_buf_read_u32(buf, &entry->timestamp); |
| } |
| |
| /* Notify message to capture module - called from the transport layer */ |
| static void bcmtr_capture_notify(bcmolt_devid device, const bcmtr_hdr *trhdr, |
| bcmtr_cld_event_type ev, uint32_t ts, |
| const void *packed, uint32_t packed_length, bcmolt_msg *msg) |
| { |
| bcmtr_capture_buf *tb; |
| bcmtr_capture_entry hdr; |
| uint32_t rounded_size; |
| |
| tb = &capture_buf[device]; |
| |
| /* Sanity */ |
| if (!packed) |
| return; |
| hdr.msg_size = packed_length; |
| /* Enable & overflow checks */ |
| if (!tb->active) |
| return; |
| if (tb->parm.stop_on_full && (tb->used + hdr.msg_size + BCMTR_CAPTURE_OVERHEAD_SIZE > tb->size)) |
| return; |
| hdr.timestamp = ts; |
| hdr.event = ev; |
| rounded_size = BCMOS_ROUND_UP(hdr.msg_size, sizeof(uint32_t)); |
| _bcmtr_capture_store(tb, &hdr, sizeof(hdr)); |
| _bcmtr_capture_store(tb, packed, rounded_size); /* overflow by up to 3 bytes; is this safe? */ |
| rounded_size += sizeof(bcmtr_capture_entry) + sizeof(uint32_t); |
| _bcmtr_capture_store(tb, &rounded_size, sizeof(rounded_size)); |
| ++tb->events; |
| } |
| |
| /* Notify message to logger */ |
| static void bcmtr_log_notify(bcmolt_devid device, const bcmtr_hdr *hdr, |
| bcmtr_cld_event_type ev, uint32_t ts, |
| const void *packed, uint32_t packed_length, bcmolt_msg *msg) |
| { |
| #ifdef ENABLE_LOG |
| bcmos_errno err; |
| bcmolt_obj_id obj; |
| bcmolt_mgt_group group; |
| uint16_t subgroup; |
| const char *obj_name; |
| const char *subgroup_name; |
| const char *dummy_str; |
| |
| err = bcmolt_group_id_split(hdr->msg_id, &obj, &group, &subgroup); |
| BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,); |
| |
| err = api_cli_object_name(obj, &obj_name, &dummy_str); |
| BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,); |
| |
| err = api_cli_object_subgroup_name(obj, group, subgroup, &subgroup_name, &dummy_str); |
| BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,); |
| |
| /* log with the header but without file/line number (file/line number isn't helpful here). */ |
| bcm_dev_log_log( |
| bcmtr_cld_log_id, |
| DEV_LOG_LEVEL_INFO, |
| BCM_LOG_FLAG_NONE, |
| "%s %s: corr_tag=%u instance=%d obj=%s group=%s subgrp=%s org_ts=%u err=%s\n", |
| bcmtr_cld_event_name(ev), |
| hdr->dir == BCMOLT_MSG_DIR_RESPONSE ? "response" : "request", |
| hdr->corr_tag, |
| hdr->instance, |
| obj_name, |
| apicli_mgt_group_to_str(group), |
| subgroup_name, |
| ts, |
| msg ? bcmos_strerror(msg->err) : "N/A"); |
| #endif |
| } |
| |
| /* Dump message header and/or body */ |
| static void bcmtr_dump_notify( |
| bcmolt_devid device, |
| const bcmtr_hdr *hdr, |
| bcmtr_cld_event_type ev, |
| uint32_t ts, |
| const void *packed, |
| uint32_t packed_length, |
| bcmolt_msg *msg) |
| { |
| bcmos_errno err; |
| bcmtr_cld_type val = bcmtr_cld_active_level[device][hdr->msg_id]; |
| bcmolt_obj_id obj; |
| bcmolt_mgt_group group; |
| uint16_t subgroup; |
| const char *obj_name; |
| const char *subgroup_name; |
| const char *dummy_str; |
| |
| err = bcmolt_group_id_split(hdr->msg_id, &obj, &group, &subgroup); |
| BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,); |
| |
| err = api_cli_object_name(obj, &obj_name, &dummy_str); |
| BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,); |
| |
| err = api_cli_object_subgroup_name(obj, group, subgroup, &subgroup_name, &dummy_str); |
| BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,); |
| |
| /* always dump the message header to the scratch CLI session */ |
| bcmcli_session_print( |
| bcmtr_cld_scratch_session, |
| "[-- CLD: %s %s: corr_tag=%u instance=%d msg_id=%d obj=%s(%d) group=%s(%d) subgrp=%s(%d)", |
| bcmtr_cld_event_name(ev), |
| hdr->dir == BCMOLT_MSG_DIR_RESPONSE ? "response" : "request", |
| hdr->corr_tag, |
| hdr->instance, |
| hdr->msg_id, |
| obj_name, obj, |
| apicli_mgt_group_to_str(group), group, |
| subgroup_name, subgroup); |
| |
| if (hdr->more_fragments || (hdr->frag_number != 0)) |
| { |
| bcmcli_session_print( |
| bcmtr_cld_scratch_session, |
| " more_fragments=%d fragment_number=%u", |
| hdr->more_fragments, |
| hdr->frag_number); |
| } |
| |
| bcmcli_session_print(bcmtr_cld_scratch_session, " --]\n"); |
| |
| /* if configured for a full message dump, write the message data to the scratch session */ |
| if ((val & BCMTR_CLD_DUMP) == BCMTR_CLD_DUMP) |
| { |
| if (msg != NULL) |
| { |
| bcmcli_session_print(bcmtr_cld_scratch_session, "[-- CLD Message Dump Start --]\n"); |
| apicli_msg_dump(bcmtr_cld_scratch_session, msg); |
| } |
| else |
| { |
| bcmcli_session_print(bcmtr_cld_scratch_session, "[-- CLD Message Hex Dump Start --]\n"); |
| bcmcli_session_hexdump(bcmtr_cld_scratch_session, packed, 0, packed_length, NULL); |
| } |
| bcmcli_session_print(bcmtr_cld_scratch_session, "[-- CLD Message Dump End --]\n"); |
| } |
| |
| /* Write the scratch session's buffer to the real CLI and reset it */ |
| bcmcli_session_write(bcmtr_cld_session, bcmtr_cld_scratch_buf, bcmtr_cld_scratch_pos); |
| bcmtr_cld_scratch_pos = 0; |
| } |
| |
| /* Notify capture, log, debug */ |
| void bcmtr_cld_notify(bcmolt_devid device, const bcmtr_hdr *hdr, |
| bcmtr_cld_event_type ev, uint32_t ts, const uint8_t *packed, uint32_t packed_length, |
| bcmolt_msg *msg) |
| { |
| bcmtr_cld_type val = bcmtr_cld_active_level[device][hdr->msg_id]; |
| |
| if ((val & BCMTR_CLD_CAPTURE)) |
| bcmtr_capture_notify(device, hdr, ev, ts, packed, packed_length, msg); |
| if ((val & BCMTR_CLD_LOG)) |
| bcmtr_log_notify(device, hdr, ev, ts, packed, packed_length, msg); |
| if ((val & BCMTR_CLD_DUMP)) |
| bcmtr_dump_notify(device, hdr, ev, ts, packed, packed_length, msg); |
| } |
| |
| |
| bcmos_errno bcmtr_cld_init(bcmcli_session *session) |
| { |
| bcmos_errno err; |
| bcmcli_session_parm scratch_session_parm = { .write = bcmtr_log_cli_print }; |
| |
| err = bcmcli_session_open_user(&scratch_session_parm, &bcmtr_cld_scratch_session); |
| BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err); |
| |
| bcmtr_cld_session = session; |
| |
| #ifdef ENABLE_LOG |
| bcmtr_cld_log_id = bcm_dev_log_id_register("cld", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH); |
| #endif |
| |
| #ifdef ENABLE_CLI |
| err = bcmtr_cld_cli_init(); |
| BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err); |
| #endif |
| |
| return BCM_ERR_OK; |
| } |
| |
| /** Clean up transport capture, log, debug service |
| */ |
| void bcmtr_cld_exit(void) |
| { |
| #ifdef ENABLE_CLI |
| bcmtr_cld_cli_exit(); |
| #endif |
| if (bcmtr_cld_scratch_session) |
| { |
| bcmcli_session_close(bcmtr_cld_scratch_session); |
| bcmtr_cld_scratch_session = NULL; |
| } |
| } |