| /* |
| <: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 "bcm_dev_log.h" |
| #include "bcmolt_api.h" |
| #include "bcm_api_cli_helpers.h" |
| #include "bcmolt_model_types.h" |
| #include "bcmtr_debug.h" |
| #include "bcmtr_interface.h" |
| #include "bcmolt_user_appl_playback.h" |
| #include "bcmolt_bit_utils.h" |
| |
| /* 'MPBx' in ASCII (M)aple (P)lay(b)ack version x */ |
| #define VERSION0 0x4d504230 |
| #define VERSION1 0x4d504231 |
| |
| static const uint32_t CHUNK_SIZE = 2000; |
| |
| static const char *cap_loc_str[BCMOLT_API_CAPTURE_LOCATION__NUM_OF] = |
| { |
| "device", |
| "host" |
| }; |
| |
| typedef struct |
| { |
| dev_log_id log_id; |
| } playback_context; |
| |
| static playback_context pb_ctxt[BCMTR_MAX_OLTS]; |
| |
| typedef enum |
| { |
| PB_FORMAT_API_CLI, |
| PB_FORMAT_C_API, |
| |
| PB_FORMAT__COUNT |
| } pb_format; |
| |
| typedef enum |
| { |
| PB_CLI_EXTRA_NONE, |
| PB_CLI_EXTRA_MULTI, |
| PB_CLI_EXTRA_STAT, |
| PB_CLI_EXTRA_SUBGROUP |
| } pb_cli_extra; |
| |
| typedef enum |
| { |
| PB_FIELDS_NONE, |
| PB_FIELDS_GET, |
| PB_FIELDS_MULTI, |
| PB_FIELDS_SET |
| } pb_fields; |
| |
| typedef struct |
| { |
| uint32_t version; |
| bcmolt_api_capture_location location; |
| bcmolt_firmware_sw_version fw_ver; |
| bcmolt_host_sw_version host_ver; |
| uint32_t buf_size; |
| void *capture_buffer; |
| } bcmolt_playback; |
| |
| typedef struct |
| { |
| bcmolt_buf buf; |
| bcmtr_capture_entry hdr; |
| uint8_t *msg_start; |
| } playback_iterator_raw; |
| |
| typedef struct |
| { |
| playback_iterator_raw raw; |
| bcmolt_buf msg_buf; |
| bcmolt_msg *msg; |
| bcmos_errno err; |
| } playback_iterator; |
| |
| #define FOR_EACH_CAPTURE_ENTRY(it, buffer, size) \ |
| bcmolt_buf_init(&(it).buf, size, buffer, BCMOLT_BUF_ENDIAN_FIXED); \ |
| while (bcmtr_capture_entry_get_next(&(it).buf, &(it).hdr, &(it).msg_start)) |
| |
| #define FOR_EACH_PLAYBACK_MSG(it, buffer, size) \ |
| bcmolt_buf_init(&(it).raw.buf, size, buffer, BCMOLT_BUF_ENDIAN_FIXED); \ |
| (it).msg = NULL; \ |
| while (playback_next_entry_unpacked(&(it))) |
| |
| #define DEVICE_REF "device_id" |
| #define RC_REF "rc" |
| #define VAL_REF "val" |
| #define MSG_REF "msg" |
| #define KEY_REF "key" |
| #define LIST_MEM_REF "list_mem" |
| #define MSG_SET_REF "msg_set" |
| #define MAX_MSGS_REF "max_msgs" |
| #define INVERT_FILTER_REF "invert_filter" |
| #define STAT_FLAGS_REF "stat_flags" |
| #define DYN_LIST_SIZE "APICLI_DYNAMIC_LIST_BUFFER_SIZE" |
| |
| static void playback_iterator_cleanup(playback_iterator *it) |
| { |
| if ((it->err == BCM_ERR_OK) && (it->msg != NULL)) |
| { |
| bcmolt_msg_free(it->msg); |
| it->msg = NULL; |
| } |
| } |
| |
| static bcmos_bool playback_next_entry_unpacked(playback_iterator *it) |
| { |
| playback_iterator_cleanup(it); |
| bcmos_bool ret = bcmtr_capture_entry_get_next(&it->raw.buf, &it->raw.hdr, &it->raw.msg_start); |
| if (ret) |
| { |
| bcmolt_buf_init(&it->msg_buf, it->raw.hdr.msg_size, it->raw.msg_start, BCMOLT_BUF_ENDIAN_FIXED); |
| it->err = bcmolt_msg_unpack(&it->msg_buf, &it->msg); |
| } |
| return ret; |
| } |
| |
| static bcmos_errno playback_read_block(uint32_t offset, uint32_t size, uint8_t *buf) |
| { |
| bcmos_errno err; |
| bcmolt_debug_cfg debug_cfg; |
| bcmolt_debug_key debug_key = { }; |
| bcmolt_api_capture_buffer_reader reader = { .offset = offset, .size = size }; |
| |
| BCMOLT_CFG_INIT(&debug_cfg, debug, debug_key); |
| BCMOLT_CFG_PROP_SET(&debug_cfg, debug, api_capture_buffer_read, reader); |
| err = bcmolt_cfg_set(current_device, &debug_cfg.hdr); |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Failed to update reader (%u, %u)!\n", offset, size); |
| } |
| else |
| { |
| BCMOLT_CFG_INIT(&debug_cfg, debug, debug_key); |
| BCMOLT_CFG_PROP_GET(&debug_cfg, debug, api_capture_buffer); |
| debug_cfg.data.api_capture_buffer.val = buf; |
| err = bcmolt_cfg_get(current_device, &debug_cfg.hdr); |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Failed to retrieve capture buffer chunk (%u, %u)!\n", |
| offset, size); |
| } |
| } |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_read(void **buffer, uint32_t *length, bcmolt_api_capture_location *capture_location) |
| { |
| bcmos_errno err; |
| bcmolt_debug_cfg debug_cfg; |
| bcmolt_debug_key debug_key = { }; |
| |
| if ((buffer == NULL) || (length == NULL)) |
| { |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "buffer (%p) and length (%p) cannot be NULL\n", buffer, length); |
| } |
| |
| BCMOLT_CFG_INIT(&debug_cfg, debug, debug_key); |
| BCMOLT_CFG_PROP_GET(&debug_cfg, debug, api_capture_stats); |
| BCMOLT_CFG_PROP_GET(&debug_cfg, debug, api_capture_cfg); |
| err = bcmolt_cfg_get(current_device, &debug_cfg.hdr); |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Failed to retrieve capture stats!\n"); |
| } |
| else |
| { |
| void *capture_buffer; |
| uint32_t buf_size = debug_cfg.data.api_capture_stats.readable_bytes; |
| uint32_t offset = 0; |
| |
| *capture_location = debug_cfg.data.api_capture_cfg.location; |
| |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Retrieving %u byte buffer\n", buf_size); |
| capture_buffer = bcmos_alloc(buf_size); |
| if (NULL != capture_buffer) |
| { |
| while ((offset + CHUNK_SIZE) < buf_size) |
| { |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Reading bytes %u - %u\n", offset, offset + CHUNK_SIZE); |
| err = playback_read_block(offset, CHUNK_SIZE, (uint8_t*)capture_buffer + offset); |
| if (BCM_ERR_OK != err) |
| { |
| break; |
| } |
| offset += CHUNK_SIZE; |
| } |
| |
| if (BCM_ERR_OK == err) |
| { |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Reading bytes %u - %u\n", offset, buf_size); |
| err = playback_read_block(offset, buf_size - offset, (uint8_t*)capture_buffer + offset); |
| } |
| } |
| |
| *buffer = capture_buffer; |
| *length = buf_size; |
| } |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_dump(bcmcli_session *session, void *capture_buf, uint32_t buf_size) |
| { |
| playback_iterator it; |
| bcmos_errno err = BCM_ERR_OK; |
| |
| FOR_EACH_PLAYBACK_MSG(it, capture_buf, buf_size) |
| { |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Dumping message at %u of %u (%u)\n", |
| bcmolt_buf_get_used(&it.raw.buf), buf_size, it.raw.hdr.msg_size); |
| bcmcli_session_print(session, "\n%08x %u:\n", it.raw.hdr.timestamp, it.raw.hdr.event); |
| if (BCM_ERR_OK == it.err) |
| { |
| err = apicli_msg_dump(session, it.msg); |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Dump status: %s\n", bcmos_strerror(err)); |
| } |
| else |
| { |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Unpacking failed: %s\n", bcmos_strerror(err)); |
| if ((it.raw.msg_start + it.raw.hdr.msg_size) > (it.raw.buf.start + it.raw.buf.len)) |
| { |
| bcmcli_session_print(session, "Message length is insane!\n"); |
| } |
| else |
| { |
| bcmcli_session_hexdump(session, it.raw.msg_start, 0, it.raw.hdr.msg_size, NULL); |
| } |
| } |
| } |
| |
| return err; |
| } |
| |
| static bcmos_bool playback_should_send(bcmolt_api_capture_location capture_location, bcmtr_cld_event_type event_type) |
| { |
| switch (capture_location) |
| { |
| case BCMOLT_API_CAPTURE_LOCATION_DEVICE: |
| switch (event_type) |
| { |
| case BCMTR_CLD_EV_RECV: |
| case BCMTR_CLD_EV_RECV_DISCARD: |
| return BCMOS_TRUE; |
| default: |
| return BCMOS_FALSE; |
| } |
| case BCMOLT_API_CAPTURE_LOCATION_HOST: |
| switch (event_type) |
| { |
| case BCMTR_CLD_EV_SEND: |
| case BCMTR_CLD_EV_RESEND: |
| return BCMOS_TRUE; |
| default: |
| return BCMOS_FALSE; |
| } |
| default: |
| return BCMOS_FALSE; |
| } |
| } |
| |
| /*lint -e{429} */ |
| static bcmos_errno playback_replay( |
| bcmolt_devid device, |
| void *capture_buf, |
| uint32_t buf_size, |
| bcmolt_api_capture_location location, |
| bcmos_bool keep_time) |
| { |
| bcmos_errno err = BCM_ERR_OK; |
| playback_iterator_raw it; |
| uint32_t last_time_us = 0; |
| bcmos_bool first = BCMOS_TRUE; |
| |
| FOR_EACH_CAPTURE_ENTRY(it, capture_buf, buf_size) |
| { |
| bcmolt_buf msg_buf; |
| bcmolt_msg *msg = NULL; |
| |
| BCM_LOG(DEBUG, pb_ctxt[device].log_id, "Processing message (%u,%u,%u)\n", |
| it.hdr.event, it.hdr.timestamp, it.hdr.msg_size); |
| if (playback_should_send(location, (bcmtr_cld_event_type)it.hdr.event)) |
| { |
| if (keep_time) |
| { |
| if (!first) |
| { |
| /* approximate original timing; doesn't account for processing time in this code - this could be |
| improved */ |
| BCM_LOG(DEBUG, pb_ctxt[device].log_id, "Sleeping for %u us\n", it.hdr.timestamp - last_time_us); |
| bcmos_usleep(it.hdr.timestamp - last_time_us); |
| } |
| else |
| { |
| first = BCMOS_FALSE; |
| } |
| last_time_us = it.hdr.timestamp; |
| } |
| BCM_LOG(DEBUG, pb_ctxt[device].log_id, "Unpacking message\n"); |
| bcmolt_buf_init(&msg_buf, it.hdr.msg_size, it.msg_start, BCMOLT_BUF_ENDIAN_FIXED); |
| err = bcmolt_msg_unpack(&msg_buf, &msg); |
| if (BCM_ERR_OK == err) |
| { |
| BCM_LOG(DEBUG, pb_ctxt[device].log_id, "Sending message\n"); |
| err = bcmtr_send(device, msg, BCMTR_SEND_FLAGS_NONE); |
| bcmolt_msg_free(msg); |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(INFO, pb_ctxt[device].log_id, "Sending failed: %s\n", bcmos_strerror(err)); |
| return err; |
| } |
| } |
| else |
| { |
| BCM_LOG(INFO, pb_ctxt[device].log_id, "Unpacking failed: %s\n", bcmos_strerror(err)); |
| return err; |
| } |
| } |
| } |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_cli_dump(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms) |
| { |
| bcmos_errno err; |
| void *capture_buffer; |
| uint32_t buf_size; |
| bcmolt_api_capture_location location; |
| |
| err = playback_read(&capture_buffer, &buf_size, &location); |
| |
| if (BCM_ERR_OK == err) |
| { |
| bcmcli_print(session, "Capture from %s:\n", cap_loc_str[location]); |
| playback_dump(session, capture_buffer, buf_size); |
| } |
| |
| bcmos_free(capture_buffer); |
| |
| return err; |
| } |
| |
| static void playback_fw_version_get(bcmolt_devid device, bcmolt_firmware_sw_version *fw) |
| { |
| bcmos_errno err; |
| bcmolt_device_cfg dev_cfg; |
| bcmolt_device_key dev_key = { }; |
| |
| BCMOLT_CFG_INIT(&dev_cfg, device, dev_key); |
| BCMOLT_CFG_PROP_GET(&dev_cfg, device, firmware_sw_version); |
| err = bcmolt_cfg_get(device, &dev_cfg.hdr); |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(WARNING, pb_ctxt[device].log_id, "Failed to retrieve fw version!\n"); |
| } |
| else |
| { |
| *fw = dev_cfg.data.firmware_sw_version; |
| } |
| } |
| |
| static void playback_host_version_get(bcmolt_devid device, bcmolt_host_sw_version *host) |
| { |
| bcmos_errno err; |
| bcmolt_device_cfg dev_cfg; |
| bcmolt_device_key dev_key = { }; |
| |
| BCMOLT_CFG_INIT(&dev_cfg, device, dev_key); |
| BCMOLT_CFG_PROP_GET(&dev_cfg, device, host_sw_version); |
| err = bcmolt_cfg_get(device, &dev_cfg.hdr); |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(WARNING, pb_ctxt[device].log_id, "Failed to retrieve host version!\n"); |
| } |
| else |
| { |
| *host = dev_cfg.data.host_sw_version; |
| } |
| } |
| |
| static bcmos_errno playback_cli_save(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms) |
| { |
| const char *filename = bcmcli_find_named_parm(session, "file")->value.string; |
| bcmolt_playback mpb = {}; |
| bcmos_errno err; |
| FILE *file; |
| uint32_t temp; |
| |
| err = playback_read(&mpb.capture_buffer, &mpb.buf_size, &mpb.location); |
| |
| if (BCM_ERR_OK == err) |
| { |
| playback_fw_version_get(current_device, &mpb.fw_ver); |
| playback_host_version_get(current_device, &mpb.host_ver); |
| |
| file = fopen(filename, "wb"); |
| /* write file version */ |
| temp = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, VERSION1); |
| fwrite(&temp, sizeof(uint32_t), 1, file); |
| /* write capture location */ |
| temp = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, (uint32_t)mpb.location); |
| fwrite(&temp, sizeof(uint32_t), 1, file); |
| /* write firmware version */ |
| fwrite(&mpb.fw_ver.major, sizeof(uint8_t), 1, file); |
| fwrite(&mpb.fw_ver.minor, sizeof(uint8_t), 1, file); |
| fwrite(&mpb.fw_ver.revision, sizeof(uint8_t), 1, file); |
| temp = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, mpb.fw_ver.model); |
| fwrite(&temp, sizeof(uint32_t), 1, file); |
| fwrite(mpb.fw_ver.build_time, sizeof(mpb.fw_ver.build_time), 1, file); |
| /* write host version */ |
| fwrite(&mpb.host_ver.major, sizeof(uint8_t), 1, file); |
| fwrite(&mpb.host_ver.minor, sizeof(uint8_t), 1, file); |
| fwrite(&mpb.host_ver.revision, sizeof(uint8_t), 1, file); |
| temp = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, mpb.host_ver.model); |
| fwrite(&temp, sizeof(uint32_t), 1, file); |
| fwrite(mpb.host_ver.build_time, sizeof(mpb.host_ver.build_time), 1, file); |
| /* write capture buffer */ |
| temp = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, mpb.buf_size); |
| fwrite(&temp, sizeof(uint32_t), 1, file); |
| fwrite(mpb.capture_buffer, mpb.buf_size, 1, file); |
| fclose(file); |
| } |
| |
| bcmos_free(mpb.capture_buffer); |
| |
| return err; |
| } |
| |
| static bcmos_bool playback_version_match(const bcmolt_playback *mpb) |
| { |
| bcmolt_firmware_sw_version fw_curr = {}; |
| bcmolt_host_sw_version host_curr = {}; |
| |
| playback_fw_version_get(current_device, &fw_curr); |
| playback_host_version_get(current_device, &host_curr); |
| |
| if ((mpb->fw_ver.model != fw_curr.model) || (mpb->host_ver.model != host_curr.model) || |
| (fw_curr.model == 0) || (host_curr.model == 0)) |
| { |
| BCM_LOG(WARNING, pb_ctxt[current_device].log_id, |
| "Possible version mismatch: Capture FW %u, HOST %u; Current FW %u, HOST %u\n", |
| mpb->fw_ver.model, mpb->host_ver.model, fw_curr.model, host_curr.model); |
| return BCMOS_FALSE; |
| } |
| |
| return BCMOS_TRUE; |
| } |
| |
| static bcmos_errno playback_file_open(const char *filename, bcmolt_playback *mpb) |
| { |
| FILE *file; |
| uint32_t temp; |
| bcmos_errno err = BCM_ERR_OK; |
| uint32_t items_read; |
| |
| file = fopen(filename, "rb"); |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| return BCM_ERR_PARSE; |
| |
| mpb->version = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| switch (mpb->version) |
| { |
| case VERSION0: |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| break; |
| temp = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| mpb->location = (bcmolt_api_capture_location)temp; |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| break; |
| mpb->buf_size = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| mpb->capture_buffer = bcmos_alloc(mpb->buf_size); |
| items_read = fread(mpb->capture_buffer, mpb->buf_size, 1, file); |
| if (items_read != 1) |
| break; |
| break; |
| case VERSION1: |
| /* read capture location */ |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| break; |
| temp = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| mpb->location = (bcmolt_api_capture_location)temp; |
| /* read firmware version */ |
| items_read = fread(&mpb->fw_ver.major, sizeof(uint8_t), 1, file); |
| if (items_read != 1) |
| break; |
| items_read = fread(&mpb->fw_ver.minor, sizeof(uint8_t), 1, file); |
| if (items_read != 1) |
| break; |
| items_read = fread(&mpb->fw_ver.revision, sizeof(uint8_t), 1, file); |
| if (items_read != 1) |
| break; |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| break; |
| mpb->fw_ver.model = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| items_read = fread(mpb->fw_ver.build_time, sizeof(mpb->fw_ver.build_time), 1, file); |
| if (items_read != 1) |
| break; |
| /* read host version */ |
| items_read = fread(&mpb->host_ver.major, sizeof(uint8_t), 1, file); |
| if (items_read != 1) |
| break; |
| items_read = fread(&mpb->host_ver.minor, sizeof(uint8_t), 1, file); |
| if (items_read != 1) |
| break; |
| items_read = fread(&mpb->host_ver.revision, sizeof(uint8_t), 1, file); |
| if (items_read != 1) |
| break; |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| break; |
| mpb->host_ver.model = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| items_read = fread(mpb->host_ver.build_time, sizeof(mpb->host_ver.build_time), 1, file); |
| if (items_read != 1) |
| break; |
| /* read capture buffer */ |
| items_read = fread(&temp, sizeof(uint32_t), 1, file); |
| if (items_read != 1) |
| break; |
| mpb->buf_size = BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, temp); |
| mpb->capture_buffer = bcmos_alloc(mpb->buf_size); |
| items_read = fread(mpb->capture_buffer, mpb->buf_size, 1, file); |
| if (items_read != 1) |
| break; |
| break; |
| default: |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Unknown version: %u\n", mpb->version); |
| err = BCM_ERR_PARSE; |
| break; |
| } |
| |
| fclose(file); |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_cli_replay(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms) |
| { |
| const char *filename = bcmcli_find_named_parm(session, "file")->value.string; |
| bcmos_bool ignore_ver = BCMOS_FALSE; |
| bcmos_errno err; |
| bcmolt_playback mpb = {}; |
| |
| bcmcli_cmd_parm *iv_parm = bcmcli_find_named_parm(session, "ignore_version"); |
| if (iv_parm != NULL) |
| { |
| ignore_ver = iv_parm->value.enum_val == (long)BCMOS_TRUE; |
| } |
| |
| err = playback_file_open(filename, &mpb); |
| |
| if (BCM_ERR_OK == err) |
| { |
| if (playback_version_match(&mpb) || ignore_ver) |
| { |
| err = playback_replay(current_device, mpb.capture_buffer, mpb.buf_size, mpb.location, BCMOS_TRUE); |
| } |
| else |
| { |
| bcmcli_print(session, "Possible version mismatch; use ignore_version=yes to force playback\n"); |
| err = BCM_ERR_IMAGE_TYPE; |
| } |
| bcmos_free(mpb.capture_buffer); |
| } |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_api_cli_append_prop( |
| bcmolt_string *api_cli, |
| uint32_t size, |
| void *data, |
| const bcmcli_prop_descr *pd, |
| const char *prefix) |
| { |
| bcmos_errno err; |
| bcmcli_session *str; |
| void *prop_data = (void *)((long)data + pd->offset); |
| |
| BCMOS_CHECK_RETURN_ERROR(pd->offset >= size, BCM_ERR_INTERNAL); |
| err = bcmcli_session_open_string(&str, api_cli); |
| BCMOS_RETURN_IF_ERROR(err); |
| err = apicli_dump_prop_param(str, pd, prop_data, prefix); |
| bcmcli_session_close(str); |
| BCMOS_RETURN_IF_ERROR(err); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_api_cli_key_write( |
| bcmolt_string* api_cli, |
| const bcmolt_msg* msg, |
| uint32_t key_size, |
| uint32_t key_offset) |
| { |
| bcmos_errno err; |
| const bcmcli_prop_descr *pd; |
| void *data = (void *)((long)msg + key_offset); |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, BCMOLT_MGT_GROUP_KEY, 0, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| err = playback_api_cli_append_prop(api_cli, key_size, data, pd, " "); |
| BCMOS_RETURN_IF_ERROR(err); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_api_cli_pm_write(bcmolt_string *api_cli, const bcmolt_msg* msg, bcmolt_presence_mask pm) |
| { |
| int n; |
| const bcmcli_prop_descr *pd; |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, msg->group, msg->subgroup, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| if (!(pm & (1ULL << prop))) |
| { |
| continue; |
| } |
| n = bcmolt_string_append(api_cli, " %s=yes", pd->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_api_cli_props_write( |
| bcmolt_string *api_cli, |
| const bcmolt_msg* msg, |
| const char *prefix, |
| uint32_t size, |
| uint32_t offset) |
| { |
| bcmos_errno err; |
| const bcmcli_prop_descr *pd; |
| void *data = (void *)((long)msg + offset); |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, msg->group, msg->subgroup, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| if (!(msg->presence_mask & (1ULL << prop))) |
| { |
| continue; |
| } |
| err = playback_api_cli_append_prop(api_cli, size, data, pd, prefix); |
| BCMOS_RETURN_IF_ERROR(err); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_api_cli_cmd_get( |
| bcmolt_string *api_cli, |
| const bcmolt_msg* msg, |
| const char *cmd, |
| pb_cli_extra extra_parms, |
| pb_fields field_parms) |
| { |
| int n; |
| bcmos_errno err; |
| const char *name; |
| const char *desc; |
| uint32_t key_size; |
| uint32_t key_offset; |
| uint32_t data_size; |
| uint32_t data_offset; |
| |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "Start %s\n", cmd); |
| |
| err = api_cli_object_name(msg->obj_type, &name, &desc); |
| BCMOS_RETURN_IF_ERROR(err); |
| |
| n = bcmolt_string_append(api_cli, "/api/%s object=%s", cmd, name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| |
| switch (extra_parms) |
| { |
| case PB_CLI_EXTRA_MULTI: |
| n = bcmolt_string_append( |
| api_cli, |
| " max_msgs=%u filter_invert=%s", |
| msg->msg_set->max_instances, |
| BITS_SET(msg->msg_set->filter_flags, BCMOLT_FILTER_FLAGS_INVERT_SELECTION) ? "yes" : "no"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| break; |
| case PB_CLI_EXTRA_STAT: |
| n = bcmolt_string_append(api_cli, " clear=%s", BITS_SET(msg->type, BCMOLT_MSG_TYPE_CLEAR) ? "yes" : "no"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| break; |
| case PB_CLI_EXTRA_SUBGROUP: |
| err = api_cli_object_subgroup_name(msg->obj_type, msg->group, msg->subgroup, &name, &desc); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(api_cli, " sub=%s", name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| break; |
| default: |
| break; |
| } |
| |
| /* get message info */ |
| err = api_cli_object_struct_size( |
| msg->obj_type, |
| msg->group, |
| msg->subgroup, |
| &key_size, |
| &key_offset, |
| &data_size, |
| &data_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| |
| if (BCMOLT_MGT_GROUP_AUTO_CFG != msg->group) |
| { |
| /* write key */ |
| err = playback_api_cli_key_write(api_cli, msg, key_size, key_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| } |
| |
| switch (field_parms) |
| { |
| case PB_FIELDS_GET: |
| /* write presence mask */ |
| err = playback_api_cli_pm_write(api_cli, msg, msg->presence_mask); |
| BCMOS_RETURN_IF_ERROR(err); |
| break; |
| case PB_FIELDS_MULTI: |
| /* write filter */ |
| err = playback_api_cli_props_write(api_cli, msg, " filter.", data_size, data_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| /* write presence mask */ |
| err = playback_api_cli_pm_write(api_cli, msg, msg->msg_set->presence_mask); |
| BCMOS_RETURN_IF_ERROR(err); |
| break; |
| case PB_FIELDS_SET: |
| /* write properties */ |
| err = playback_api_cli_props_write(api_cli, msg, " ", data_size, data_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| break; |
| default: |
| break; |
| } |
| |
| n = bcmolt_string_append(api_cli, "\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| |
| BCM_LOG(DEBUG, pb_ctxt[current_device].log_id, "End %s\n", cmd); |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_api_cli_get(bcmolt_string* api_cli, const bcmolt_msg* msg) |
| { |
| switch (msg->group) |
| { |
| case BCMOLT_MGT_GROUP_CFG: |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| return playback_api_cli_cmd_get(api_cli, msg, "get", PB_CLI_EXTRA_NONE, PB_FIELDS_GET); |
| case BCMOLT_MSG_TYPE_GET_MULTI: |
| return playback_api_cli_cmd_get(api_cli, msg, "multiget", PB_CLI_EXTRA_MULTI, PB_FIELDS_MULTI); |
| case BCMOLT_MSG_TYPE_SET: |
| return playback_api_cli_cmd_get(api_cli, msg, "set", PB_CLI_EXTRA_NONE, PB_FIELDS_SET); |
| case BCMOLT_MSG_TYPE_CLEAR: |
| return playback_api_cli_cmd_get(api_cli, msg, "clear", PB_CLI_EXTRA_NONE, PB_FIELDS_NONE); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| break; |
| case BCMOLT_MGT_GROUP_STAT: |
| return playback_api_cli_cmd_get(api_cli, msg, "stat", PB_CLI_EXTRA_STAT, PB_FIELDS_GET); |
| case BCMOLT_MGT_GROUP_STAT_CFG: |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| return playback_api_cli_cmd_get(api_cli, msg, "saget", PB_CLI_EXTRA_SUBGROUP, PB_FIELDS_NONE); |
| case BCMOLT_MSG_TYPE_SET: |
| return playback_api_cli_cmd_get(api_cli, msg, "saset", PB_CLI_EXTRA_SUBGROUP, PB_FIELDS_SET); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| break; |
| case BCMOLT_MGT_GROUP_AUTO_CFG: |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| return playback_api_cli_cmd_get(api_cli, msg, "acget", PB_CLI_EXTRA_NONE, PB_FIELDS_GET); |
| case BCMOLT_MSG_TYPE_SET: |
| return playback_api_cli_cmd_get(api_cli, msg, "acset", PB_CLI_EXTRA_NONE, PB_FIELDS_SET); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| break; |
| case BCMOLT_MGT_GROUP_OPER: |
| return playback_api_cli_cmd_get(api_cli, msg, "oper", PB_CLI_EXTRA_SUBGROUP, PB_FIELDS_SET); |
| case BCMOLT_MGT_GROUP_PROXY: |
| return playback_api_cli_cmd_get(api_cli, msg, "send", PB_CLI_EXTRA_SUBGROUP, PB_FIELDS_SET); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| } |
| |
| static bcmos_errno playback_convert_api_cli(bcmolt_playback *mpb, FILE *out_file) |
| { |
| playback_iterator it; |
| bcmos_errno err; |
| bcmolt_string *str; |
| |
| err = bcmolt_string_create(&str, 2048); |
| BCMOS_RETURN_IF_ERROR(err); |
| FOR_EACH_PLAYBACK_MSG(it, mpb->capture_buffer, mpb->buf_size) |
| { |
| if (playback_should_send(mpb->location, (bcmtr_cld_event_type)it.raw.hdr.event)) |
| { |
| if (BCM_ERR_OK == it.err) |
| { |
| bcmolt_string_reset(str); |
| err = playback_api_cli_get(str, it.msg); |
| fwrite(bcmolt_string_get(str), sizeof(char), strlen(bcmolt_string_get(str)), out_file); |
| } |
| else |
| { |
| err = it.err; |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Unpacking failed: %s\n", bcmos_strerror(err)); |
| } |
| } |
| } |
| bcmolt_string_destroy(str); |
| |
| return err; |
| } |
| |
| static bcmos_bool playback_field_contains_var_list(const bcmcli_type_descr *type) |
| { |
| if (type == NULL) |
| { |
| return BCMOS_FALSE; |
| } |
| |
| if (type->base_type == BCMOLT_BASE_TYPE_ID_ARR_DYN) |
| { |
| return BCMOS_TRUE; |
| } |
| else if (type->base_type == BCMOLT_BASE_TYPE_ID_ARR_FIXED) |
| { |
| return playback_field_contains_var_list(type->x.arr_fixed.elem_type); |
| } |
| else if (type->base_type == BCMOLT_BASE_TYPE_ID_STRUCT) |
| { |
| for (uint16_t i = 0; i < type->x.s.num_fields; ++i) |
| { |
| if (playback_field_contains_var_list(type->x.s.fields[i].type)) |
| { |
| return BCMOS_TRUE; |
| } |
| } |
| } |
| else if (type->base_type == BCMOLT_BASE_TYPE_ID_UNION) |
| { |
| for (uint16_t i = 0; i < type->x.u.num_common_fields; ++i) |
| { |
| if (playback_field_contains_var_list(type->x.u.common_fields[i].type)) |
| { |
| return BCMOS_TRUE; |
| } |
| } |
| for (uint16_t i = 0; type->x.u.common_fields[type->x.u.classifier_idx].type->x.e[i].name != NULL; ++i) |
| { |
| if (playback_field_contains_var_list(type->x.u.union_fields[i].type)) |
| { |
| return BCMOS_TRUE; |
| } |
| } |
| } |
| else |
| { |
| /* not a variable sized list */ |
| } |
| |
| return BCMOS_FALSE; |
| } |
| |
| static bcmos_bool playback_msg_contains_var_list(const bcmolt_msg* msg) |
| { |
| const bcmcli_prop_descr *pd; |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, msg->group, msg->subgroup, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| if (playback_field_contains_var_list(pd->type)) |
| { |
| return BCMOS_TRUE; |
| } |
| } |
| |
| return BCMOS_FALSE; |
| } |
| |
| static bcmos_errno playback_string_write_upper(bcmolt_string *dest, const char* src) |
| { |
| int n; |
| |
| while (*src != '\0') |
| { |
| n = bcmolt_string_append(dest, "%c", (char)toupper((int)*src)); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| ++src; |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_c_api_pm_write( |
| bcmolt_string *c_api, |
| const bcmolt_msg* msg, |
| bcmolt_presence_mask pm, |
| const char *extra, |
| const char *field, |
| const char *obj) |
| { |
| int n; |
| bcmos_errno err; |
| const bcmcli_prop_descr *pd; |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, msg->group, msg->subgroup, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| if (!(pm & (1ULL << prop))) |
| { |
| continue; |
| } |
| n = bcmolt_string_append(c_api, "\tBCMOLT_%s", extra); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_string_write_upper(c_api, apicli_mgt_group_to_str(msg->group)); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, "_PROP_GET(%s, %s, %s);\n", field, obj, pd->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_c_api_append_init( |
| bcmolt_string *c_api, |
| void *data, |
| const bcmcli_type_descr *td, |
| const char* name) |
| { |
| bcmos_errno rc; |
| bcmcli_session *str; |
| |
| rc = bcmcli_session_open_string(&str, c_api); |
| BCMOS_RETURN_IF_ERROR(rc); |
| rc = apicli_dump_dyn_array(str, td, data, name); |
| bcmcli_session_close(str); |
| BCMOS_RETURN_IF_ERROR(rc); |
| |
| return BCM_ERR_OK; |
| } |
| |
| |
| static bcmos_errno playback_c_api_append_prop( |
| bcmolt_string *c_api, |
| uint32_t size, |
| void *data, |
| const bcmcli_prop_descr *pd) |
| { |
| bcmos_errno err; |
| bcmcli_session *str; |
| void *prop_data = (void *)((long)data + pd->offset); |
| |
| BCMOS_CHECK_RETURN_ERROR(pd->offset >= size, BCM_ERR_INTERNAL); |
| err = bcmcli_session_open_string(&str, c_api); |
| BCMOS_RETURN_IF_ERROR(err); |
| err = apicli_dump_prop_initializer(str, pd, prop_data); |
| bcmcli_session_close(str); |
| BCMOS_RETURN_IF_ERROR(err); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_c_api_make_var_lists( |
| bcmolt_string *c_api, |
| const bcmcli_type_descr *type, |
| void *data, |
| const char *name) |
| { |
| int n; |
| bcmos_errno rc; |
| |
| if (type == NULL) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| if (type->base_type == BCMOLT_BASE_TYPE_ID_ARR_DYN) |
| { |
| n = bcmolt_string_append(c_api, "\t%s %s[] = ", type->x.arr_dyn.elem_type->name, name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| rc = playback_c_api_append_init(c_api, data, type, name); |
| BCMOS_RETURN_IF_ERROR(rc); |
| n = bcmolt_string_append(c_api, ";\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else if (type->base_type == BCMOLT_BASE_TYPE_ID_ARR_FIXED) |
| { |
| for (uint16_t i = 0; i < type->x.arr_fixed.size; ++i) |
| { |
| char index_name[64]; |
| |
| sprintf(index_name, "%s%u", name, i); |
| rc = playback_c_api_make_var_lists(c_api, type->x.arr_fixed.elem_type, data, index_name); |
| BCMOS_RETURN_IF_ERROR(rc); |
| data = (void*)((long)data + type->x.arr_fixed.elem_type->size); |
| } |
| } |
| else if (type->base_type == BCMOLT_BASE_TYPE_ID_STRUCT) |
| { |
| for (uint16_t i = 0; i < type->x.s.num_fields; ++i) |
| { |
| const bcmcli_field_descr *field = &type->x.s.fields[i]; |
| rc = playback_c_api_make_var_lists(c_api, field->type, (void*)((long)data + field->offset), field->name); |
| BCMOS_RETURN_IF_ERROR(rc); |
| } |
| } |
| else if (type->base_type == BCMOLT_BASE_TYPE_ID_UNION) |
| { |
| for (uint16_t i = 0; i < type->x.u.num_common_fields; ++i) |
| { |
| const bcmcli_field_descr *field = &type->x.u.common_fields[i]; |
| rc = playback_c_api_make_var_lists(c_api, field->type, (void*)((long)data + field->offset), field->name); |
| BCMOS_RETURN_IF_ERROR(rc); |
| } |
| for (uint16_t i = 0; type->x.u.common_fields[type->x.u.classifier_idx].type->x.e[i].name != NULL; ++i) |
| { |
| const bcmcli_field_descr *field = &type->x.u.union_fields[i]; |
| rc = playback_c_api_make_var_lists(c_api, field->type, (void*)((long)data + field->offset), field->name); |
| BCMOS_RETURN_IF_ERROR(rc); |
| } |
| } |
| else |
| { |
| /* not a variable sized list */ |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_c_api_props_write( |
| bcmolt_string *c_api, |
| const bcmolt_msg* msg, |
| const char* object, |
| uint32_t size, |
| uint32_t offset) |
| { |
| int n; |
| bcmos_errno err; |
| const char *subgroup; |
| const char *desc; |
| const bcmcli_prop_descr *pd; |
| void *data = (void *)((long)msg + offset); |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, msg->group, msg->subgroup, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| if (!(msg->presence_mask & (1ULL << prop))) |
| { |
| continue; |
| } |
| playback_c_api_make_var_lists(c_api, pd->type, (void*)((long)data + pd->offset), pd->name); |
| } |
| |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, msg->group, msg->subgroup, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| if (!(msg->presence_mask & (1ULL << prop))) |
| { |
| continue; |
| } |
| if ((pd->type->base_type == BCMOLT_BASE_TYPE_ID_STRUCT) || |
| (pd->type->base_type == BCMOLT_BASE_TYPE_ID_UNION) || |
| (pd->type->base_type == BCMOLT_BASE_TYPE_ID_ARR_DYN) || |
| (pd->type->base_type == BCMOLT_BASE_TYPE_ID_ARR_FIXED)) |
| { |
| n = bcmolt_string_append(c_api, "\t%s "VAL_REF" = ", pd->type->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_c_api_append_prop(c_api, size, data, pd); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ";\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\tBCMOLT_"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_string_write_upper(c_api, apicli_mgt_group_to_str(msg->group)); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, "_PROP_SET(&msg, %s", object); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| if ((msg->group == BCMOLT_MGT_GROUP_OPER) || (msg->group == BCMOLT_MGT_GROUP_PROXY)) |
| { |
| err = api_cli_object_subgroup_name(msg->obj_type, msg->group, msg->subgroup, &subgroup, &desc); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ", %s", subgroup); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| n = bcmolt_string_append(c_api, ", %s, "VAL_REF");\n", pd->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else |
| { |
| n = bcmolt_string_append(c_api, "\tBCMOLT_"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_string_write_upper(c_api, apicli_mgt_group_to_str(msg->group)); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, "_PROP_SET(&msg, %s", object); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| if ((msg->group == BCMOLT_MGT_GROUP_OPER) || (msg->group == BCMOLT_MGT_GROUP_PROXY)) |
| { |
| err = api_cli_object_subgroup_name(msg->obj_type, msg->group, msg->subgroup, &subgroup, &desc); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ", %s", subgroup); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| n = bcmolt_string_append(c_api, ", %s, ", pd->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_c_api_append_prop(c_api, size, data, pd); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ");\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_c_api_code_get( |
| bcmolt_string* c_api, |
| const bcmolt_msg* msg, |
| const char *call, |
| pb_fields field_parms) |
| { |
| int n; |
| bcmos_errno err; |
| const char *object; |
| const char *group; |
| const char *subgroup; |
| const char *desc; |
| uint32_t key_size; |
| uint32_t key_offset; |
| uint32_t data_size; |
| uint32_t data_offset; |
| void *data; |
| const bcmcli_prop_descr *pd; |
| bcmos_bool var_list = (BCMOLT_MSG_TYPE_GET == msg->type) && playback_msg_contains_var_list(msg); |
| |
| err = api_cli_object_name(msg->obj_type, &object, &desc); |
| BCMOS_RETURN_IF_ERROR(err); |
| err = api_cli_object_subgroup_name(msg->obj_type, msg->group, msg->subgroup, &subgroup, &desc); |
| BCMOS_RETURN_IF_ERROR(err); |
| |
| group = apicli_mgt_group_to_str(msg->group); |
| |
| /* get message info */ |
| err = api_cli_object_struct_size( |
| msg->obj_type, |
| msg->group, |
| msg->subgroup, |
| &key_size, |
| &key_offset, |
| &data_size, |
| &data_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| |
| /* init variables */ |
| n = bcmolt_string_append(c_api, "\t{\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| if ((msg->group == BCMOLT_MGT_GROUP_OPER) || (msg->group == BCMOLT_MGT_GROUP_PROXY)) |
| { |
| n = bcmolt_string_append(c_api, "\tbcmolt_%s_%s "MSG_REF";\n", object, subgroup); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else |
| { |
| n = bcmolt_string_append(c_api, "\tbcmolt_%s_%s "MSG_REF";\n", object, group); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| n = bcmolt_string_append(c_api, "\tbcmolt_%s_key "KEY_REF" = { };\n", object); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| |
| if (var_list) |
| { |
| n = bcmolt_string_append(c_api, "\tuint8_t* "LIST_MEM_REF";\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| |
| if (msg->type == BCMOLT_MSG_TYPE_GET_MULTI) |
| { |
| n = bcmolt_string_append(c_api, "\tbcmolt_msg_set* "MSG_SET_REF" = NULL;\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\tuint32_t "MAX_MSGS_REF";\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\tbcmos_bool "INVERT_FILTER_REF";\n\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\t"MAX_MSGS_REF" = %d;\n", msg->msg_set->max_instances); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\t"INVERT_FILTER_REF" = %s;\n", BITS_SET(msg->msg_set->filter_flags, BCMOLT_FILTER_FLAGS_INVERT_SELECTION) ? "BCMOS_TRUE" : "BCMOS_FALSE"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\tbcmolt_msg_set_alloc(BCMOLT_OBJ_ID_"); |
| err = playback_string_write_upper(c_api, object); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ", BCMOLT_MGT_GROUP_"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_string_write_upper(c_api, group); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ", "MAX_MSGS_REF", &"MSG_SET_REF");\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else if (msg->group == BCMOLT_MGT_GROUP_STAT) |
| { |
| n = bcmolt_string_append(c_api, "\tbcmolt_stat_flags "STAT_FLAGS_REF";\n\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\t"STAT_FLAGS_REF" = %s;\n", BITS_SET(msg->type, BCMOLT_MSG_TYPE_CLEAR) ? "BCMOLT_STAT_FLAGS_CLEAR_ON_READ" : "BCMOLT_STAT_FLAGS_NONE"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else |
| { |
| n = bcmolt_string_append(c_api, "\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| |
| /* set key */ |
| if (BCMOLT_MGT_GROUP_AUTO_CFG != msg->group) |
| { |
| /* write key */ |
| data = (void *)((long)msg + key_offset); |
| for (uint16_t prop = 0; |
| api_cli_object_property(msg->obj_type, BCMOLT_MGT_GROUP_KEY, 0, prop, &pd) == BCM_ERR_OK; |
| ++prop) |
| { |
| n = bcmolt_string_append(c_api, "\t"KEY_REF".%s = ", pd->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| if ((BCMOLT_BASE_TYPE_ID_MAC == pd->type->base_type) || |
| (BCMOLT_BASE_TYPE_ID_IPV4 == pd->type->base_type) || |
| (BCMOLT_BASE_TYPE_ID_STRUCT == pd->type->base_type) || |
| (BCMOLT_BASE_TYPE_ID_UNION == pd->type->base_type)) |
| { |
| n = bcmolt_string_append(c_api, "(%s)", pd->type->name); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| err = playback_c_api_append_prop(c_api, key_size, data, pd); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, ";\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| } |
| |
| /* set properties */ |
| n = bcmolt_string_append(c_api, "\tBCMOLT_"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| err = playback_string_write_upper(c_api, group); |
| BCMOS_RETURN_IF_ERROR(err); |
| n = bcmolt_string_append(c_api, "_INIT(&"MSG_REF", %s", object); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| if ((msg->group == BCMOLT_MGT_GROUP_STAT_CFG) || |
| (msg->group == BCMOLT_MGT_GROUP_OPER) || |
| (msg->group == BCMOLT_MGT_GROUP_PROXY)) |
| { |
| n = bcmolt_string_append(c_api, ", %s", subgroup); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| n = bcmolt_string_append(c_api, ", "KEY_REF");\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| |
| switch (field_parms) |
| { |
| case PB_FIELDS_GET: |
| /* write presence mask */ |
| err = playback_c_api_pm_write(c_api, msg, msg->presence_mask, "", "&"MSG_REF, object); |
| BCMOS_RETURN_IF_ERROR(err); |
| if (var_list) |
| { |
| n = bcmolt_string_append(c_api, "\t"LIST_MEM_REF" = bcmos_calloc("DYN_LIST_SIZE");\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append( |
| c_api, |
| "\tBCMOLT_CFG_LIST_BUF_SET(&"MSG_REF", %s, "LIST_MEM_REF", "DYN_LIST_SIZE");\n", object); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| break; |
| case PB_FIELDS_MULTI: |
| /* write filter */ |
| err = playback_c_api_props_write(c_api, msg, object, data_size, data_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| /* write presence mask */ |
| err = playback_c_api_pm_write(c_api, msg, msg->presence_mask, "MSGSET_", MSG_SET_REF, object); |
| BCMOS_RETURN_IF_ERROR(err); |
| break; |
| case PB_FIELDS_SET: |
| /* write properties */ |
| err = playback_c_api_props_write(c_api, msg, object, data_size, data_offset); |
| BCMOS_RETURN_IF_ERROR(err); |
| break; |
| default: |
| break; |
| } |
| |
| /* call API */ |
| n = bcmolt_string_append(c_api, "\t"RC_REF" = bcmolt_%s_%s("DEVICE_REF", &"MSG_REF".hdr", group, call); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| if (msg->type == BCMOLT_MSG_TYPE_GET_MULTI) |
| { |
| n = bcmolt_string_append( |
| c_api, |
| ", ("INVERT_FILTER_REF") ? BCMOLT_FILTER_FLAGS_INVERT_SELECTION : BCMOLT_FILTER_FLAGS_NONE, "MSG_SET_REF); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else if (msg->group == BCMOLT_MGT_GROUP_STAT) |
| { |
| n = bcmolt_string_append(c_api, ", "STAT_FLAGS_REF); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| } |
| else |
| { |
| /* do nothing */ |
| } |
| n = bcmolt_string_append(c_api, ");\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\tbcmos_printf(\"bcmolt_%s_%s returned %%s (%%d)\\n\", bcmos_strerror("RC_REF"), "RC_REF");\n", group, call); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| n = bcmolt_string_append(c_api, "\t}\n\n"); |
| BCMOS_CHECK_RETURN_ERROR(n <= 0, BCM_ERR_INTERNAL); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno playback_c_api_get(bcmolt_string* c_api, const bcmolt_msg* msg) |
| { |
| switch (msg->group) |
| { |
| case BCMOLT_MGT_GROUP_CFG: |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| return playback_c_api_code_get(c_api, msg, "get", PB_FIELDS_GET); |
| case BCMOLT_MSG_TYPE_GET_MULTI: |
| return playback_c_api_code_get(c_api, msg, "get_multi", PB_FIELDS_MULTI); |
| case BCMOLT_MSG_TYPE_SET: |
| return playback_c_api_code_get(c_api, msg, "set", PB_FIELDS_SET); |
| case BCMOLT_MSG_TYPE_CLEAR: |
| return playback_c_api_code_get(c_api, msg, "clear", PB_FIELDS_NONE); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| break; |
| case BCMOLT_MGT_GROUP_STAT: |
| return playback_c_api_code_get(c_api, msg, "get", PB_FIELDS_GET); |
| case BCMOLT_MGT_GROUP_STAT_CFG: |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| return playback_c_api_code_get(c_api, msg, "get", PB_FIELDS_NONE); |
| case BCMOLT_MSG_TYPE_SET: |
| return playback_c_api_code_get(c_api, msg, "set", PB_FIELDS_SET); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| break; |
| case BCMOLT_MGT_GROUP_AUTO_CFG: |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| return playback_c_api_code_get(c_api, msg, "get", PB_FIELDS_GET); |
| case BCMOLT_MSG_TYPE_SET: |
| return playback_c_api_code_get(c_api, msg, "set", PB_FIELDS_SET); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| break; |
| case BCMOLT_MGT_GROUP_OPER: |
| return playback_c_api_code_get(c_api, msg, "submit", PB_FIELDS_SET); |
| case BCMOLT_MGT_GROUP_PROXY: |
| return playback_c_api_code_get(c_api, msg, "send", PB_FIELDS_SET); |
| default: |
| return BCM_ERR_INTERNAL; |
| } |
| } |
| |
| static bcmos_errno playback_convert_c_api(bcmolt_playback *mpb, FILE *out_file) |
| { |
| playback_iterator it; |
| bcmos_errno err; |
| bcmolt_string *str; |
| uint32_t last_time_us = 0; |
| bcmos_bool first = BCMOS_TRUE; |
| |
| err = bcmolt_string_create(&str, 16384); |
| BCMOS_RETURN_IF_ERROR(err); |
| fprintf(out_file, "void run_playback(void)\n{\n"); |
| fprintf(out_file, "\tbcmolt_devid "DEVICE_REF" = %d;\n", current_device); |
| fprintf(out_file, "\tbcmos_errno "RC_REF" = BCM_ERR_OK;\n\n"); |
| FOR_EACH_PLAYBACK_MSG(it, mpb->capture_buffer, mpb->buf_size) |
| { |
| if (playback_should_send(mpb->location, (bcmtr_cld_event_type)it.raw.hdr.event)) |
| { |
| if (BCM_ERR_OK == it.err) |
| { |
| if (!first) |
| { |
| /* approximate original timing; doesn't account for processing time in this code - this could be |
| improved */ |
| fprintf(out_file, "\n\tbcmos_usleep(%u);\n\n", it.raw.hdr.timestamp - last_time_us); |
| } |
| else |
| { |
| first = BCMOS_FALSE; |
| } |
| last_time_us = it.raw.hdr.timestamp; |
| bcmolt_string_reset(str); |
| err = playback_c_api_get(str, it.msg); |
| fwrite(bcmolt_string_get(str), sizeof(char), strlen(bcmolt_string_get(str)), out_file); |
| } |
| else |
| { |
| err = it.err; |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Unpacking failed: %s\n", bcmos_strerror(err)); |
| } |
| } |
| } |
| fprintf(out_file, "}\n"); |
| bcmolt_string_destroy(str); |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_convert(bcmolt_playback *mpb, pb_format format, FILE *out_file) |
| { |
| bcmos_errno err; |
| |
| switch (format) |
| { |
| case PB_FORMAT_API_CLI: |
| err = playback_convert_api_cli(mpb, out_file); |
| break; |
| case PB_FORMAT_C_API: |
| err = playback_convert_c_api(mpb, out_file); |
| break; |
| default: |
| err = BCM_ERR_PARM; |
| BCM_LOG(ERROR, pb_ctxt[current_device].log_id, "Unknown format: %d\n", format); |
| break; |
| } |
| |
| return err; |
| } |
| |
| static bcmos_errno playback_cli_conv(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms) |
| { |
| const char *infilename = bcmcli_find_named_parm(session, "infile")->value.string; |
| const char *outfilename = bcmcli_find_named_parm(session, "outfile")->value.string; |
| pb_format format = (pb_format)bcmcli_find_named_parm(session, "format")->value.enum_val; |
| bcmos_bool ignore_ver = BCMOS_FALSE; |
| bcmos_errno err; |
| bcmolt_playback mpb = {}; |
| FILE *out_file; |
| |
| bcmcli_cmd_parm *iv_parm = bcmcli_find_named_parm(session, "ignore_version"); |
| if (iv_parm != NULL) |
| { |
| ignore_ver = iv_parm->value.enum_val == (long)BCMOS_TRUE; |
| } |
| |
| err = playback_file_open(infilename, &mpb); |
| |
| if (BCM_ERR_OK == err) |
| { |
| if (playback_version_match(&mpb) || ignore_ver) |
| { |
| out_file = fopen(outfilename, "wb"); |
| playback_convert(&mpb, format, out_file); |
| fclose(out_file); |
| } |
| else |
| { |
| bcmcli_print(session, "Possible version mismatch; use ignore_version=yes to force conversion\n"); |
| err = BCM_ERR_IMAGE_TYPE; |
| } |
| bcmos_free(mpb.capture_buffer); |
| } |
| |
| return err; |
| } |
| |
| void bcmolt_user_appl_playback_cli_init(bcmcli_entry *top_dir) |
| { |
| #ifdef ENABLE_CLI |
| bcmcli_entry *plb_dir = bcmcli_dir_add(top_dir, "playback", "playback", BCMCLI_ACCESS_ADMIN, NULL); |
| BUG_ON(NULL == plb_dir); |
| |
| static bcmcli_enum_val format_enum_table[PB_FORMAT__COUNT+1] = |
| { |
| {.name = "api_cli", .val = (long)PB_FORMAT_API_CLI}, |
| {.name = "c_api", .val = (long)PB_FORMAT_C_API}, |
| BCMCLI_ENUM_LAST |
| }; |
| |
| BCMCLI_MAKE_CMD_NOPARM(plb_dir, "dump", "dump capture", playback_cli_dump); |
| BCMCLI_MAKE_CMD(plb_dir, "save", "save capture", playback_cli_save, |
| BCMCLI_MAKE_PARM("file", "filename", BCMCLI_PARM_STRING, BCMCLI_PARM_FLAG_NONE)); |
| BCMCLI_MAKE_CMD(plb_dir, "replay", "replay capture", playback_cli_replay, |
| BCMCLI_MAKE_PARM("file", "filename", BCMCLI_PARM_STRING, BCMCLI_PARM_FLAG_NONE), |
| BCMCLI_MAKE_PARM_ENUM("ignore_version", "Ignore version mismatch", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL)); |
| BCMCLI_MAKE_CMD(plb_dir, "conv", "Convert capture", playback_cli_conv, |
| BCMCLI_MAKE_PARM("infile", "Input filename", BCMCLI_PARM_STRING, BCMCLI_PARM_FLAG_NONE), |
| BCMCLI_MAKE_PARM("outfile", "Output filename", BCMCLI_PARM_STRING, BCMCLI_PARM_FLAG_NONE), |
| BCMCLI_MAKE_PARM_ENUM("format", "Output format", format_enum_table, BCMCLI_PARM_FLAG_NONE), |
| BCMCLI_MAKE_PARM_ENUM("ignore_version", "Ignore version mismatch", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL)); |
| #endif |
| } |
| |
| void bcmolt_user_appl_playback_init(void) |
| { |
| for (uint32_t i = 0; i < BCMTR_MAX_OLTS; i++) |
| { |
| char log_name[MAX_DEV_LOG_ID_NAME]; |
| snprintf(log_name, sizeof(log_name)-1, "user_appl_playback_%u", i); |
| pb_ctxt[i].log_id = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH); |
| } |
| } |
| |