BAL and Maple Release 2.2
Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bcm68620_release/release/host_reference/user_appl/Makefile b/bcm68620_release/release/host_reference/user_appl/Makefile
new file mode 100644
index 0000000..37df795
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/Makefile
@@ -0,0 +1,28 @@
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl
+ MOD_TYPE = app
+ MOD_DEPS = host_api api_dev_log bcm_board bcm_user_appl_ps bcm_user_appl_eon bcm_user_appl_omon \
+ bcm_user_appl_epon_oam bcm_user_appl_epon_oam_cli pcie pcie_mod \
+ bcm_user_appl_epon_hde bcm_api_proxy common_epon_oam common_gpon bcm_image_transfer bcm_sw_upgrade \
+ device_selector bcm_remote_cli bcm_user_appl_playback bcm_user_appl_dpoe_sec
+
+ifneq ("$(RELEASE_BUILD)", "y")
+ MOD_DEPS += bcm_user_appl_gpon_stress bcm_user_appl_gpon_ds_omci \
+ bcm_user_appl_gpon_sn_acquisition bcm_user_appl_gpon_statistics \
+ bcm_user_appl_gpon_mac_learning bcm_user_appl_gpon_rssi \
+ bcm_user_appl_omci_swdl bcm_user_appl_remote_logger bcm_user_appl_onu_tuning
+endif
+
+ MOD_INC_DIRS = $(TOP)/host/board/wrx $(TOP)/common/drivers/maple/pcie
+ srcs = bcmolt_user_appl.c bcmolt_user_appl_cli.c bcmolt_user_appl_ex_cli.c bcmolt_user_appl_gpon_utils.c
+
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+ ifeq ($(ENABLE_KT2), y)
+ MOD_DEFS += -DENABLE_KT2
+ #EXTRA_INCLUDES += -I$(TOP_DIR)/common/cli
+ endif
+
+ MOD_DEPS += transport
+endif
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl.c b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl.c
new file mode 100644
index 0000000..279c314
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl.c
@@ -0,0 +1,520 @@
+/*
+<: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 <bcmolt_host_api.h>
+#include <bcmolt_board.h>
+#include <bcmolt_board_cli.h>
+#include <bcmolt_user_appl_cli.h>
+#include <bcmolt_user_appl_ex_cli.h>
+#include <bcmolt_user_appl_epon_oam.h>
+#if defined(LINUX_KERNEL_SPACE)
+#include <bcmolt_dev_log_linux.h>
+#endif
+#include <bcmolt_dev_selector.h>
+#include <bcm_api_cli.h>
+#include <bcmolt_api_proxy.h>
+#include <bcmolt_remote_cli.h>
+#include <bcmolt_image_transfer.h>
+
+#ifdef SIMULATION_BUILD
+ static void using_inband_set(bcmos_bool using_inband){}
+ static bcmos_bool using_inband_get(void)
+ {
+ return BCMOS_FALSE;
+ }
+ extern uint32_t bcmtr_host_ip;
+ extern uint16_t bcmtr_host_udp_port;
+ extern uint32_t bcmtr_olt_ip[BCMTR_MAX_OLTS];
+ extern uint16_t bcmtr_olt_udp_port[BCMTR_MAX_OLTS];
+#else
+ #include <bcmtr_plugin.h>
+#endif
+
+
+#define CLI_HOST_PROMPT_FORMAT "BCM.%u> "
+
+static bcmcli_session *current_session;
+
+//#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE)
+//extern uint32_t bcmtr_host_ip;
+//extern uint16_t bcmtr_host_udp_port;
+//extern uint32_t bcmtr_olt_ip[BCMTR_MAX_OLTS];
+//extern uint16_t bcmtr_olt_udp_port[BCMTR_MAX_OLTS];
+//#endif
+
+#ifdef ENABLE_LOG
+static int nologger=0;
+#endif
+
+static int _cmd_using_inband(bcmcli_session *sess, const bcmcli_cmd_parm parm[], uint16_t nParms)
+{
+ bcmcli_session_print(sess, "Maple management using %s\n", (using_inband_get())? "In-Band" : "PCIe");
+ return 0;
+}
+
+static int _cli_help(const char *cmd)
+{
+ const char *p;
+
+ while ((p = strchr(cmd, '/')))
+ {
+ cmd = p + 1;
+ }
+
+ fprintf(stderr,
+ "%s [-l <level>]"
+#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE)
+ " [-p udp_port_device1] [-p udp_port_device2] ...\n"
+ "OR\n"
+ " [-olt ip_device1:port_device1] [-olt ip_device2:port_device2] ...\n"
+ " [-listen ip:port]\n"
+#endif
+#ifdef ENABLE_LOG
+ " [-nl]\n"
+#endif
+ " [-ne]\n"
+ " [-dev]\n"
+ " [-if_mask mask]\n"
+ " [-proxy proxy_port_device1] [-proxy proxy_port_device2] ...\n"
+ " [-ud]\n"
+ " [-on_ready cmd]\n", cmd);
+ fprintf(stderr,
+ "\t\t level == guest | admin | debug\n"
+#ifdef ENABLE_LOG
+ "\t\t -nl - disable kernel logger integration\n"
+#endif
+ "\t\t -ne - disable line editing\n"
+ "\t\t -dev - device ID to auto-register for indications and run proxy on (default 0)\n"
+ "\t\t -if_mask - PON NI mask to apply to auto-registration of indications\n"
+ "\t\t -proxy - run the API proxy listening on this UDP port\n"
+ "\t\t -ud - use a unix domain connection to a user space device control"
+ "\t\t -on_ready - command for API proxy to execute after receiving device ready indication\n");
+ return -EINVAL;
+}
+
+/* quit command handler */
+static int _cmd_quit(bcmcli_session *sess, const bcmcli_cmd_parm parm[], uint16_t nParms)
+{
+ bcmcli_stop(sess);
+ bcmcli_session_print(sess, "Maple CLI terminated by 'Quit' command\n");
+ return 0;
+}
+
+#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE)
+/* parse ip:port */
+static bcmos_errno _parse_ip_port(const char *s, uint32_t *ip, uint16_t *port)
+{
+ int n;
+ uint32_t ip1, ip2, ip3, ip4, pp;
+
+ n = sscanf(s, "%u.%u.%u.%u:%u", &ip1, &ip2, &ip3, &ip4, &pp);
+ if (n != 5 || ip1 > 0xff || ip2 > 0xff || ip3 > 0xff || ip4 > 0xff || pp > 0xffff)
+ {
+ fprintf(stderr, "Can't parse %s. Must be ip_address:port\n", s);
+ return BCM_ERR_PARM;
+ }
+ *ip = (ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4;
+ *port = pp;
+ return BCM_ERR_OK;
+}
+#endif
+
+/* Try to learn system mode */
+static bcmolt_system_mode _get_system_mode(bcmolt_devid dev)
+{
+ bcmolt_device_key key = {};
+ bcmolt_device_cfg dev_cfg;
+ bcmos_errno rc;
+
+ BCMOLT_CFG_INIT(&dev_cfg, device, key);
+ BCMOLT_CFG_PROP_GET(&dev_cfg, device, system_mode);
+ rc = bcmolt_cfg_get(dev, &dev_cfg.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ bcmos_printf("Device %u: failed to read system mode (%s). Waiting for configuration\n",
+ (unsigned)dev, bcmos_strerror(rc));
+ return BCMOLT_SYSTEM_MODE__NUM_OF;
+ }
+ bcmos_printf("Device %u: System mode: %s\n",
+ (unsigned)dev, bcmolt_system_mode_name(dev_cfg.data.system_mode));
+ return dev_cfg.data.system_mode;
+}
+
+#ifdef ENABLE_LOG
+static int dev_log_time_to_str_cb(uint32_t time_stamp, char *time_str, int time_str_size)
+{
+ return snprintf(time_str, time_str_size, "%u", time_stamp / 1000); /* convert from usec to msec. */
+}
+
+static bcmos_errno bcm_init_logger(void)
+{
+#define DEV_LOG_SIZE1 1<<11
+#define DEV_LOG_SIZE2 1<<13
+#define DEV_LOG_QUEUE_SIZE 1000
+
+ static uint8_t logger_buf1[DEV_LOG_SIZE1];
+ static uint8_t logger_buf2[DEV_LOG_SIZE2];
+ bcmos_errno err;
+ void *addresses[DEV_LOG_MAX_FILES] = {logger_buf1, logger_buf2};
+ uint32_t sizes[DEV_LOG_MAX_FILES] = {sizeof(logger_buf1), sizeof(logger_buf2)};
+ uint32_t flags[DEV_LOG_MAX_FILES] = {BCM_DEV_LOG_FILE_FLAG_STOP_WHEN_FULL, BCM_DEV_LOG_FILE_FLAG_WRAP_AROUND};
+
+ err = bcm_dev_log_init_default_logger(addresses, sizes, flags, BCM_SIZEOFARRAY(addresses), 0x4000, TASK_PRIORITY_DEV_LOG, DEV_LOG_QUEUE_SIZE);
+ BCMOS_TRACE_CHECK_RETURN(err, err, "bcm_dev_log_initialize_dev_logger_default()\n");
+
+ bcm_dev_log_level_set_style(DEV_LOG_LEVEL_FATAL, BCM_DEV_LOG_STYLE_BOLD);
+ bcm_dev_log_level_set_style(DEV_LOG_LEVEL_ERROR, BCM_DEV_LOG_STYLE_BOLD);
+
+ bcm_dev_log_set_time_to_str_cb(dev_log_time_to_str_cb);
+
+#if defined(LINUX_KERNEL_SPACE)
+ if (!nologger)
+ {
+ err = bcm_dev_log_linux_init();
+ BCMOS_TRACE_CHECK_RETURN(err, err, "bcm_dev_log_linux_init()\n");
+ }
+#endif
+
+ BCM_LOG(INFO, def_log_id, "Hello Logger\n");
+
+ return BCM_ERR_OK;
+}
+#endif
+
+static void handle_system_mode_change(bcmolt_devid dev)
+{
+ bcmcli_entry *cur_dir = bcmcli_dir_get(current_session);
+ char old_dir_name[32] = "";
+ bcmolt_system_mode system_mode;
+
+ bcmolt_system_mode_get(dev, &system_mode);
+ bcm_common_gpon_init(system_mode);
+
+ if (dev == current_device)
+ {
+ if (cur_dir)
+ strncpy(old_dir_name, bcmcli_token_name(cur_dir), sizeof(old_dir_name) - 1);
+
+ api_cli_set_commands(current_session);
+
+ /* Restore current CLI directory */
+ cur_dir = bcmcli_dir_find(NULL, old_dir_name);
+ if (cur_dir)
+ bcmcli_dir_set(current_session, cur_dir);
+ }
+
+ bcmolt_user_appl_cli_handle_system_mode_change(dev);
+}
+
+static void cli_get_prompt(bcmcli_session *session, char *buf, uint32_t max_len)
+{
+ snprintf(buf, max_len, CLI_HOST_PROMPT_FORMAT, current_device);
+}
+
+int main(int argc, char *argv[])
+{
+ bcmcli_session_parm mon_session_parm;
+#ifndef SIMULATION_BUILD
+ uint32_t fpga_version;
+#endif
+ int noedit = 0;
+ int rc;
+ int i;
+ bcmolt_host_init_params params =
+ {
+#ifdef ENABLE_LOG
+ .logger_init_cb = bcm_init_logger,
+#endif
+ .run_dev_ctrl = BCMOS_FALSE
+ };
+ bcmolt_devid device = 0;
+ bcmos_bool specific_device = BCMOS_FALSE;
+ bcmos_bool run_proxy = BCMOS_FALSE;
+ bcmos_bool run_remote_cli = BCMOS_FALSE;
+ uint32_t proxy_port[BCMTR_MAX_OLTS] = {};
+ uint32_t remote_cli_port = 0;
+ int cur_proxy_dev = 0;
+ char *on_ready_cmd = NULL;
+ struct bcmolt_rx_cfg rx_cfg =
+ {
+ .obj_type = BCMOLT_OBJECT_ANY,
+ .flags = BCMOLT_AUTO_FLAGS_NONE,
+ };
+#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE)
+ int cur_olt = 0;
+#endif
+
+ (void)noedit; /* prevent warning */
+
+ memset(&mon_session_parm, 0, sizeof(mon_session_parm));
+ mon_session_parm.get_prompt = cli_get_prompt;
+ mon_session_parm.access_right = BCMCLI_ACCESS_ADMIN;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp(argv[i], "-l"))
+ {
+ ++i;
+ if (!strcmp(argv[i], "admin"))
+ mon_session_parm.access_right = BCMCLI_ACCESS_ADMIN;
+ else if (!strcmp(argv[i], "guest"))
+ mon_session_parm.access_right = BCMCLI_ACCESS_GUEST;
+ else if (!strcmp(argv[i], "debug"))
+ mon_session_parm.access_right = BCMCLI_ACCESS_DEBUG;
+ else
+ return _cli_help(argv[0]);
+ }
+ else if (!strcmp(argv[i], "-ne"))
+ {
+ noedit = 1;
+ }
+#ifdef ENABLE_LOG
+ else if (!strcmp(argv[i], "-nl"))
+ {
+ nologger = 1;
+ }
+#endif
+#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE)
+ else if (!strcmp(argv[i], "-p"))
+ {
+ if (cur_olt >= BCMTR_MAX_OLTS)
+ {
+ printf("Too many -p and/or -olt options\n");
+ return -1;
+ }
+ ++i;
+ bcmtr_olt_udp_port[cur_olt] = strtol(argv[i], NULL, 0);
+ ++cur_olt;
+ }
+ else if (!strcmp(argv[i], "-olt"))
+ {
+ if (cur_olt >= BCMTR_MAX_OLTS)
+ {
+ printf("Too many -p and/or -olt options\n");
+ return -1;
+ }
+ ++i;
+ if (_parse_ip_port(argv[i], &bcmtr_olt_ip[cur_olt], &bcmtr_olt_udp_port[cur_olt]) != BCM_ERR_OK)
+ return -1;
+ ++cur_olt;
+ }
+ else if (!strcmp(argv[i], "-listen"))
+ {
+ ++i;
+ if (_parse_ip_port(argv[i], &bcmtr_host_ip, &bcmtr_host_udp_port) != BCM_ERR_OK)
+ return -1;
+ }
+#endif
+ else if (!strcmp(argv[i], "-dev"))
+ {
+ ++i;
+ device = (bcmolt_devid)strtoul(argv[i], NULL, 0);
+ if (device >= BCMTR_MAX_OLTS)
+ {
+ printf("Invalid device ID\n");
+ return -1;
+ }
+ current_device = device;
+ specific_device = BCMOS_TRUE;
+ }
+ else if (!strcmp(argv[i], "-if_mask"))
+ {
+ ++i;
+ rx_cfg.pon_ni_mask = (uint32_t)strtoul(argv[i], NULL, 0);
+ }
+ else if (!strcmp(argv[i], "-proxy"))
+ {
+ if (cur_proxy_dev >= BCMTR_MAX_OLTS)
+ {
+ printf("Too many -proxy options\n");
+ return -1;
+ }
+ ++i;
+ run_proxy = BCMOS_TRUE;
+ proxy_port[cur_proxy_dev] = (uint32_t)strtoul(argv[i], NULL, 0);
+ if (proxy_port[cur_proxy_dev] > 0xffff)
+ {
+ printf("Invalid proxy port %u\n", (unsigned)proxy_port[cur_proxy_dev]);
+ return -1;
+ }
+ ++cur_proxy_dev;
+ }
+ else if (!strcmp(argv[i], "-remote"))
+ {
+ ++i;
+ run_remote_cli = BCMOS_TRUE;
+ remote_cli_port = (uint32_t)strtoul(argv[i], NULL, 0);
+ }
+ else if (!strcmp(argv[i], "-ud"))
+ {
+ using_inband_set(BCMOS_TRUE);
+ }
+ else if (!strcmp(argv[i], "-on_ready"))
+ {
+ ++i;
+ on_ready_cmd = argv[i];
+ }
+ else
+ {
+ return _cli_help(argv[0]);
+ }
+ }
+
+ rc = bcmolt_host_init(¶ms);
+ BUG_ON(rc);
+
+ bcmos_trace_level_set(BCMOS_TRACE_LEVEL_DEBUG);
+
+ if (noedit)
+ {
+ mon_session_parm.line_edit_mode = BCMCLI_LINE_EDIT_DISABLE;
+ }
+ if (bcmcli_session_open(&mon_session_parm, ¤t_session))
+ {
+ printf("Can't open CLI session\n");
+ return -EINVAL;
+ }
+
+ /* Try to learn system mode for all devices */
+ if (specific_device)
+ {
+ bcmolt_system_mode_set(current_device, _get_system_mode(current_device));
+ }
+ else
+ {
+ for (i = 0; i < BCMTR_MAX_OLTS; i++)
+ bcmolt_system_mode_set(i, _get_system_mode(i));
+ }
+
+ /* Init device selector */
+ rc = bcmolt_dev_sel_init(NULL);
+
+ /* Add Maple API commands */
+ rc = rc ? rc : api_cli_init(NULL, current_session);
+
+ /* (x)GPON init. Does nothing if system mode is not (x)GPON */
+ if (rc == BCM_ERR_OK)
+ {
+ bcmolt_system_mode system_mode = BCMOLT_SYSTEM_MODE__NUM_OF;
+ bcmolt_system_mode_get(current_device, &system_mode);
+ bcm_common_gpon_init(system_mode);
+ }
+
+ /* Add transport commands */
+ rc = rc ? rc : bcmtr_cli_init();
+
+ /* Add capture, log, debug commands */
+ rc = rc ? rc : bcmtr_cld_init(current_session);
+
+ /* Initialize embedded CLI module */
+ rc = rc ? rc : bcm_embedded_cli_init();
+
+ rc = rc ? rc : bcmos_cli_init(NULL);
+
+ rc = rc ? rc : bcmolt_user_appl_cli_init(NULL);
+
+ rc = rc ? rc : bcmolt_user_appl_ex_cli_init();
+
+
+#if defined(LINUX_KERNEL_SPACE)
+ rc = rc ? rc : bcm_board_init();
+ rc = rc ? rc : bcm_board_cli_init(NULL);
+#endif
+
+#ifdef ENABLE_LOG
+ /* logger CLI directory */
+ bcm_dev_log_cli_init(NULL);
+#endif
+
+ if (run_proxy)
+ {
+ /* If executing for specific device, run only 1 proxy instance.
+ * Otherwise, create as many instances as requested
+ */
+ if (specific_device)
+ rc = rc ? rc : bcmolt_api_proxy_init(NULL, device, proxy_port[0], on_ready_cmd);
+ else
+ {
+ for (i = 0; i < cur_proxy_dev; i++)
+ {
+ rc = rc ? rc : bcmolt_api_proxy_init(NULL, i, proxy_port[i], on_ready_cmd);
+ }
+ }
+ }
+
+ if (run_remote_cli)
+ {
+ rc = rc ? rc : bcmolt_remote_cli_init(NULL, device, remote_cli_port);
+ }
+
+ /* Add other commands, e.g., access to embedded CLI */
+ if (rc)
+ return rc;
+
+ rx_cfg.rx_cb = bcmolt_user_appl_indication_cb;
+ rc = bcmolt_auto_rx_cb_set(device, &rx_cfg);
+ BUG_ON(BCM_ERR_OK != rc);
+ rx_cfg.rx_cb = bcmolt_user_appl_proxy_rx_cb;
+ rc = bcmolt_proxy_rx_cb_set(device, &rx_cfg);
+ BUG_ON(BCM_ERR_OK != rc);
+
+ bcmolt_user_mftp_init();
+
+ sm_change_cb = handle_system_mode_change;
+
+ BCMCLI_MAKE_CMD_NOPARM(NULL, "quit", "Quit", _cmd_quit);
+ BCMCLI_MAKE_CMD_NOPARM(NULL, "using_inband", "Using In Band", _cmd_using_inband);
+
+#ifndef SIMULATION_BUILD
+ bcm_board_fpga_version_get(&fpga_version);
+ bcmcli_session_print(current_session, "FPGA version: %d\n", fpga_version);
+#endif
+
+ /* Process user input until EOF or quit command */
+ bcmcli_driver(current_session);
+
+#if defined(LINUX_KERNEL_SPACE)
+ bcm_board_uninit();
+#endif
+ bcmtr_exit();
+ bcmcli_session_close(current_session);
+ bcmcli_token_destroy(NULL);
+
+ if (run_proxy)
+ {
+ bcmolt_api_proxy_stop();
+ }
+
+ if (run_remote_cli)
+ {
+ bcmolt_remote_cli_stop();
+ }
+
+ return 0;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_cli.c b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_cli.c
new file mode 100644
index 0000000..8c621da
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_cli.c
@@ -0,0 +1,568 @@
+/*
+ <: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 <bcmolt_msg.h>
+#include <bcmolt_api.h>
+#include <bcmcli.h>
+#include <bcm_api_cli.h>
+#include <bcm_api_dev_log.h>
+#include <bcm_dev_log.h>
+#include <bcmolt_msg_pack.h>
+#include "bcmolt_user_appl_cli.h"
+#include "bcmolt_user_appl_gpon_utils.h"
+#include "bcmolt_user_appl_ps.h"
+#include "bcmolt_user_appl_ps_cli.h"
+#ifndef RELEASE_BUILD
+#include "bcmolt_user_appl_omci_swdl_cli.h"
+#include "bcmolt_user_appl_omci_swdl.h"
+#include "bcmolt_user_appl_gpon_mac_learning_cli.h"
+#include "bcmolt_user_appl_gpon_mac_learning.h"
+#include "bcmolt_user_appl_gpon_stress_cli.h"
+#include "bcmolt_user_appl_gpon_stress.h"
+#include "bcmolt_user_appl_gpon_sn_acquisition_cli.h"
+#include "bcmolt_user_appl_gpon_sn_acquisition.h"
+#include "bcmolt_user_appl_gpon_ds_omci_cli.h"
+#include "bcmolt_user_appl_gpon_ds_omci.h"
+#include "bcmolt_user_appl_gpon_statistics_cli.h"
+#include "bcmolt_user_appl_gpon_statistics.h"
+#include "bcmolt_user_appl_gpon_rssi_cli.h"
+#include "bcmolt_user_appl_gpon_rssi.h"
+#include "bcmolt_user_appl_remote_logger_cli.h"
+#include "bcmolt_user_appl_remote_logger.h"
+#include "bcmolt_user_appl_onu_tuning_cli.h"
+#include "bcmolt_user_appl_onu_tuning.h"
+#endif /* Not RELEASE_BUILD */
+#include "bcmolt_eon.h"
+#include "bcmolt_epon_hde.h"
+#ifndef SIMULATION_BUILD
+#include "omon.h"
+#include <bcmolt_dev_ctrl_ioctl.h>
+#include <bcmtr_pcie.h>
+#endif /* #endif SIMULATION_BUILD */
+#include "bcmolt_board.h"
+#include "bcmolt_api_proxy.h"
+#include "bcmolt_remote_cli.h"
+#include "bcmolt_user_appl_epon_oam_cli.h"
+#include "bcmolt_user_appl_epon_oam.h"
+#include "bcmolt_image_transfer_cli.h"
+#include "bcmolt_sw_upgrade_cli.h"
+#include "bcmolt_user_appl_playback.h"
+#include "bcmolt_user_appl_dpoe_sec.h"
+
+#ifdef ENABLE_LOG
+static uint32_t *bcmlcli_pci_dumpptr;
+static dev_log_id user_appl_log_id;
+static dev_log_id ind_log_id[BCMTR_MAX_INSTANCES];
+static bcmcli_entry *user_dir;
+static bcmos_bool sys_mode_set[BCMTR_MAX_OLTS] = {};
+
+static void user_appl_api_msg_dump(const char *title, bcmolt_msg *msg)
+{
+ uint8_t inst = bcmolt_msg_instance(msg);
+ inst = (inst > BCMTR_MAX_INSTANCES) ? 0 : inst;
+ bcmolt_msg_log(ind_log_id[inst], msg);
+}
+
+#ifndef RELEASE_BUILD
+static bcmos_errno bcmolt_user_appl_process_exception_ind(bcmolt_devid device_id, bcmolt_auto *ind)
+{
+ switch (ind->hdr.obj_type)
+ {
+ case BCMOLT_OBJ_ID_DEVICE:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_DEVICE_AUTO_CFG_ID_SW_EXCEPTION:
+ case BCMOLT_DEVICE_AUTO_CFG_ID_SW_ERROR:
+ /* Stop all user application tasks. */
+ if (bcmolt_gpon_mac_learning_appl_is_running(device_id))
+ bcmolt_gpon_mac_learning_appl_stop(device_id);
+ if (bcmolt_gpon_stress_appl_is_running(device_id))
+ bcmolt_gpon_stress_appl_stop(device_id);
+ if (bcmolt_gpon_sn_acquisition_appl_is_running(device_id))
+ bcmolt_gpon_sn_acquisition_appl_stop(device_id);
+ if (bcmolt_user_appl_gpon_statistics_is_running(device_id))
+ bcmolt_user_appl_gpon_statistics_stop(device_id);
+ if (bcmolt_gpon_ds_omci_appl_is_running(device_id))
+ bcmolt_gpon_ds_omci_appl_stop(device_id);
+ if (bcmolt_gpon_rssi_appl_is_running(device_id))
+ bcmolt_gpon_rssi_appl_stop(device_id);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return BCM_ERR_OK;
+}
+#endif
+
+void bcmolt_user_appl_indication_cb(bcmolt_devid device_id, bcmolt_msg *msg)
+{
+ uint8_t instance = bcmolt_msg_instance(msg);
+ bcmolt_auto *ind = (bcmolt_auto *)msg;
+ bcmolt_ps_pon ps_pon = { device_id, instance };
+
+ user_appl_api_msg_dump("Indication", msg);
+ bcmolt_ps_process_ind(&ps_pon, ind);
+
+#ifndef RELEASE_BUILD
+ bcmolt_user_appl_process_exception_ind(device_id, ind);
+ /* Forward the indication to each user application. These applications will ignore indications that aren't
+ * relevant and errors will be printed to dedicated log IDs per application. */
+ bcmolt_gpon_mac_learning_process_ind(device_id, ind);
+ bcmolt_gpon_stress_process_ind(device_id, ind);
+ bcmolt_gpon_sn_acquisition_process_ind(device_id, ind);
+ bcmolt_user_appl_gpon_statistics_process_ind(device_id, ind);
+ bcmolt_gpon_ds_omci_process_ind(device_id, ind);
+ bcmolt_gpon_rssi_process_ind(device_id, ind);
+ bcmolt_onu_tuning_process_ind(device_id, ind);
+#endif /* Not RELEASE_BUILD */
+
+#ifndef SIMULATION_BUILD
+ bcmolt_user_appl_omon_handle_ind(device_id, instance, ind);
+#endif
+
+ bcmolt_api_proxy_auto_rx_cb(device_id, msg);
+ bcmolt_remote_cli_auto_rx_cb(device_id, msg);
+
+ /* Free the indication since we're done processing it */
+ bcmolt_msg_free(msg);
+}
+
+void bcmolt_user_appl_proxy_rx_cb(bcmolt_devid device_id, bcmolt_msg *msg)
+{
+ bcmolt_proxy_rx *proxy_rx = (bcmolt_proxy_rx *)msg;
+
+#ifndef RELEASE_BUILD
+ bcmolt_omci_swdl_process_proxy_rx(device_id, proxy_rx);
+
+ /* Dump RX packet if OMCI SWDL is Not running*/
+ if (!bcmolt_omci_swdl_appl_is_running(device_id))
+ {
+ user_appl_api_msg_dump("Proxy Rx", msg);
+ }
+#else
+ user_appl_api_msg_dump("Proxy Rx", msg);
+#endif /* Not RELEASE_BUILD */
+
+ bcmolt_api_proxy_auto_rx_cb(device_id, msg);
+ bcmolt_remote_cli_auto_rx_cb(device_id, msg);
+ bcmolt_user_appl_eon_process_rx(device_id, proxy_rx);
+ bcmolt_epon_hde_process_rx(device_id, bcmolt_msg_instance(msg), proxy_rx);
+ bcmolt_user_appl_epon_oam_handle_rx(device_id, proxy_rx, NULL);
+ bcmolt_user_appl_dpoe_sec_process_proxy_rx(device_id, proxy_rx);
+ bcmolt_msg_free(msg);
+}
+
+/* example on how to read the embedded logger from the host */
+static bcmos_errno _apicli_read_embedded_logger(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_logger_cfg cfg;
+ bcmolt_logger_key key = {.file_id = parm[0].value.unumber, .reserved = 0};
+ int rc;
+
+ bcm_dev_log_log(
+ user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "--------------------------------------------------------------------------------------------------\n");
+ bcm_dev_log_log(
+ user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "| device %u logger |\n", current_device);
+ bcm_dev_log_log(user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "|-------------------------------------------------------------------------------------------------|\n");
+
+ while (1)
+ {
+ BCMOLT_CFG_INIT(&cfg, logger, key);
+ BCMOLT_CFG_PROP_GET(&cfg, logger, buffer);
+ rc = bcmolt_cfg_get(current_device, &cfg.hdr);
+ if (rc > 0)
+ {
+ bcmos_printf("get obj error %d\n", rc);
+ }
+ bcmos_printf("%s", cfg.data.buffer.buff);
+ if (cfg.data.buffer.msg_to_read == 0)
+ {
+ break;
+ }
+ }
+ return BCM_ERR_OK;
+}
+
+/* example on how to read all the embedded log entries from the host */
+static bcmos_errno _apicli_get_all_log_entries(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_log_entry_cfg cfg;
+ bcmolt_log_entry_key key = {.log_id = 0, .reserved=0};
+ int rc;
+
+ char *_log_level[] =
+ {
+ "NO_LOG",
+ "FATAL",
+ "ERROR",
+ "WARNING",
+ "INFO" ,
+ "DEBUG"
+ };
+
+ char *_log_type[] =
+ {
+ "NONE",
+ "PRINT",
+ "SAVE",
+ "BOTH"
+ };
+
+ char *_log_style[] =
+ {
+ "NORMAL",
+ "BOLD",
+ "UNDERLINE",
+ "BLIK",
+ "REVERSE_VIDEO"
+ };
+
+ bcm_dev_log_log(
+ user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "--------------------------------------------------------------------------------------------------\n");
+ bcm_dev_log_log(
+ user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "| LOG ID | LOG NAME | LOG level print | LOG level save | log type | log style |\n");
+ bcm_dev_log_log(user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "|-------------------------------------------------------------------------------------------------|\n");
+
+ while (1)
+ {
+ BCMOLT_CFG_INIT(&cfg, log_entry, key);
+ BCMOLT_CFG_PROP_GET(&cfg, log_entry, all_properties);
+ rc = bcmolt_cfg_get(current_device, &cfg.hdr);
+ if (rc)
+ {
+ break;
+ }
+ bcm_dev_log_log(user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER | BCM_LOG_FLAG_CALLER_FMT,
+ "|%-8d|%-23s|%-17s|%-17s|%-10s|%-17s|\n",
+ key.log_id,
+ cfg.data.log_name.str,
+ _log_level[cfg.data.log_level_print],
+ _log_level[cfg.data.log_level_save],
+ _log_type[cfg.data.log_type],
+ _log_style[cfg.data.log_style]);
+ bcm_dev_log_log(user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "|-------------------------------------------------------------------------------------------------|\n");
+ key.log_id++;
+
+ }
+ bcm_dev_log_log(user_appl_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "%s", "|-------------------------------------------------------------------------------------------------|\n");
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno _apicli_indication_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ struct bcmolt_rx_cfg rx_cfg =
+ {
+ .obj_type = BCMOLT_OBJECT_ANY,
+ .rx_cb = bcmolt_user_appl_indication_cb,
+ .flags = BCMOLT_AUTO_FLAGS_NONE,
+ .pon_ni_mask = parm[0].value.unumber
+ };
+ return bcmolt_auto_rx_cb_set(current_device, &rx_cfg);
+}
+
+static bcmos_errno _apicli_proxy_rx_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ struct bcmolt_rx_cfg rx_cfg =
+ {
+ .obj_type = BCMOLT_OBJECT_ANY,
+ .rx_cb = bcmolt_user_appl_proxy_rx_cb,
+ .flags = BCMOLT_AUTO_FLAGS_NONE,
+ .pon_ni_mask = parm[0].value.unumber
+ };
+ return bcmolt_proxy_rx_cb_set(current_device, &rx_cfg);
+}
+#endif
+
+#ifndef SIMULATION_BUILD
+static bcmos_errno _apicli_pcistat(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmos_errno rc;
+ uint8_t start;
+ bcm_pcied_stat *stat;
+
+ start = 0;
+ if (n_parms > 0)
+ {
+ if (!strncmp(parm[0].value.string, "yes", strlen("yes")))
+ start = 1;
+ }
+
+ rc = bcm_board_pci_debug(0, MAPLE_DEV_CTRL_IOCTL_OP_PCI_STAT, start, 0, bcmlcli_pci_dumpptr);
+ stat = (bcm_pcied_stat *)bcmlcli_pci_dumpptr;
+ BCMOS_TRACE_CHECK_RETURN(rc, rc, "bcm_board_pci_debug() failed\n");
+ bcmcli_session_print(session, "Received : %d\n", stat->rx_counter);
+ bcmcli_session_print(session, "Send : %d\n", stat->tx_counter);
+ bcmcli_session_print(session, "Rx Done Isr : %d\n", stat->rx_done_isr_counter);
+ bcmcli_session_print(session, "Rx Err Isr : %d\n", stat->rx_err_isr_counter);
+ bcmcli_session_print(session, "Tx Done Isr : %d\n", stat->tx_done_isr_counter);
+ bcmcli_session_print(session, "Tx Err Isr : %d\n", stat->tx_err_isr_counter);
+ bcmcli_session_print(session, "Rx empty : %d\n", stat->rx_pcie_empty_counter);
+ bcmcli_session_print(session, "Tx full : %d\n\n", stat->tx_pcie_full_counter);
+
+ return BCM_ERR_OK;
+}
+static bcmos_errno _apicli_pcidump(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmos_errno rc;
+ uint32_t command;
+ int32_t start, howmany;
+
+ start = -1;
+ howmany = 1;
+
+ if (n_parms > 1)
+ {
+ start = parm[1].value.number;
+ howmany = 1;
+ if (n_parms > 2)
+ howmany = parm[2].value.number;
+ }
+ if (!strncmp(parm[0].value.string, "tx", strlen("tx")))
+ {
+ command = MAPLE_DEV_CTRL_IOCTL_OP_PCI_DUMP_TX;
+ }
+ else if (!strncmp(parm[0].value.string, "rx", strlen("rx")))
+ {
+ command = MAPLE_DEV_CTRL_IOCTL_OP_PCI_DUMP_RX;
+ }
+ else
+ {
+ bcmcli_session_print(session, "Error - wrong command: %s\n", parm[0].value.string);
+ return BCM_ERR_PARM;
+ }
+
+ rc = bcm_board_pci_debug(0, command, start, howmany, bcmlcli_pci_dumpptr);
+ BCMOS_TRACE_CHECK_RETURN(rc, rc, "bcm_board_pci_debug() failed\n");
+ bcmcli_session_print(session, "%s", (char *)bcmlcli_pci_dumpptr);
+
+ return BCM_ERR_OK;
+}
+#endif
+
+static void bcmolt_user_appl_cli_start_epon(void)
+{
+ bcmolt_user_appl_epon_oam_init();
+ bcmolt_user_appl_epon_oam_cli_init(user_dir);
+
+ bcmolt_user_appl_eon_init();
+ bcmolt_user_appl_eon_cli_init(user_dir);
+
+#ifndef SIMULATION_BUILD
+ bcmolt_epon_omon_appl_init();
+ bcmolt_user_appl_epon_omon_cli_init(user_dir);
+
+ bcmolt_epon_hde_appl_init();
+ bcmolt_epon_hde_appl_cli_init(user_dir);
+#endif
+
+ bcmolt_user_appl_dpoe_sec_init();
+ bcmolt_user_appl_dpoe_sec_cli_init(user_dir);
+}
+
+static void bcmolt_user_appl_cli_start_gpon(bcmolt_devid dev)
+{
+ bcmolt_user_appl_gpon_utils_init(user_dir);
+
+#ifndef RELEASE_BUILD
+ bcmolt_gpon_mac_learning_appl_init(dev);
+ bcmolt_user_appl_gpon_mac_learning_cli_init(user_dir);
+
+ bcmolt_gpon_stress_appl_init(dev);
+ bcmolt_user_appl_gpon_stress_cli_init(user_dir);
+
+ bcmolt_gpon_sn_acquisition_appl_init(dev);
+ bcmolt_user_appl_gpon_sn_acquisition_cli_init(user_dir);
+
+ bcmolt_user_appl_gpon_statistics_init(dev);
+ bcmolt_user_appl_gpon_statistics_cli_init(user_dir);
+
+ bcmolt_gpon_ds_omci_appl_init(dev);
+ bcmolt_user_appl_gpon_ds_omci_cli_init(user_dir);
+
+ bcmolt_omci_swdl_appl_init(dev);
+ bcmolt_user_appl_omci_swdl_cli_init(user_dir);
+
+ bcmolt_gpon_rssi_appl_init(dev);
+ bcmolt_user_appl_gpon_rssi_cli_init(user_dir);
+#endif /* Not RELEASE_BUILD */
+}
+
+static void bcmolt_user_appl_cli_start_xgpon(bcmolt_devid dev)
+{
+ bcmolt_user_appl_gpon_utils_init(user_dir);
+
+#ifndef RELEASE_BUILD
+
+ bcmolt_gpon_stress_appl_init(dev);
+ bcmolt_user_appl_gpon_stress_cli_init(user_dir);
+
+ bcmolt_gpon_sn_acquisition_appl_init(dev);
+ bcmolt_user_appl_gpon_sn_acquisition_cli_init(user_dir);
+
+ bcmolt_user_appl_gpon_statistics_init(dev);
+ bcmolt_user_appl_gpon_statistics_cli_init(user_dir);
+
+ bcmolt_gpon_ds_omci_appl_init(dev);
+ bcmolt_user_appl_gpon_ds_omci_cli_init(user_dir);
+
+ bcmolt_gpon_rssi_appl_init(dev);
+ bcmolt_user_appl_gpon_rssi_cli_init(user_dir);
+
+ bcmolt_onu_tuning_appl_init(dev);
+ bcmolt_user_appl_onu_tuning_cli_init(user_dir);
+#endif /* Not RELEASE_BUILD */
+}
+
+void bcmolt_user_appl_cli_handle_system_mode_change(bcmolt_devid dev)
+{
+ bcmolt_system_mode sys_mode;
+
+ if (sys_mode_set[dev] == BCMOS_TRUE)
+ {
+ return; /* only run once */
+ }
+
+ sys_mode_set[dev] = BCMOS_TRUE;
+ (void)bcmolt_system_mode_get(dev, &sys_mode);
+ switch (sys_mode)
+ {
+ case BCMOLT_SYSTEM_MODE_EPON__16_X:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__2_X_10_G:
+ bcmolt_user_appl_cli_start_epon();
+ break;
+ case BCMOLT_SYSTEM_MODE_GPON__16_X:
+ case BCMOLT_SYSTEM_MODE_GPON__8_X:
+ case BCMOLT_SYSTEM_MODE_GPON__4_X:
+ bcmolt_user_appl_cli_start_gpon(dev);
+ break;
+ case BCMOLT_SYSTEM_MODE_XGPON_1__8_X:
+ case BCMOLT_SYSTEM_MODE_XGPON_1__4_X:
+ case BCMOLT_SYSTEM_MODE_XGS__2_X_10_G:
+ case BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G:
+ bcmolt_user_appl_cli_start_xgpon(dev);
+ break;
+ case BCMOLT_SYSTEM_MODE__NUM_OF: /* unconfigured */
+ sys_mode_set[dev] = BCMOS_FALSE;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+}
+
+bcmos_errno bcmolt_user_appl_cli_init(bcmcli_entry *top_dir)
+{
+ bcmos_errno err;
+ bcmlcli_pci_dumpptr = bcmos_alloc(1024*5);
+ uint8_t inst;
+
+ if (!bcmlcli_pci_dumpptr)
+ {
+ BCMOS_CHECK_RETURN_ERROR(!bcmlcli_pci_dumpptr, BCM_ERR_NOMEM);
+ return BCM_ERR_NULL;
+ }
+#ifdef ENABLE_LOG
+ static bcmcli_enum_val enum_table_file_id[] =
+ {
+ { .name = "SRAM", .val = BCMOLT_LOG_FILE_ID_SRAM },
+ { .name = "DDR", .val = BCMOLT_LOG_FILE_ID_DDR },
+ BCMCLI_ENUM_LAST
+ };
+
+ bcmolt_msg_log_init(); /* initialize API/logger integration */
+ user_appl_log_id = bcm_dev_log_id_register("user_appl", DEV_LOG_LEVEL_DEBUG, DEV_LOG_ID_TYPE_BOTH);
+ for (inst = 0; inst < BCMTR_MAX_INSTANCES; inst++)
+ {
+ char log_name[MAX_DEV_LOG_ID_NAME];
+ sprintf(log_name, "ind_msg%u", inst);
+ ind_log_id[inst] = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_DEBUG, DEV_LOG_ID_TYPE_BOTH);
+ }
+#endif
+ user_dir = bcmcli_dir_add(top_dir, "user", "User application", BCMCLI_ACCESS_ADMIN, NULL);
+ BCMOS_CHECK_RETURN_ERROR(!user_dir, BCM_ERR_NOMEM);
+#ifdef ENABLE_LOG
+ BCMCLI_MAKE_CMD(user_dir, "register_indication_handler", "Register indication handler", _apicli_indication_handler,
+ BCMCLI_MAKE_PARM_DEFVAL("pon_ni_mask", "PON_NI interface bitmask. 0=all", BCMCLI_PARM_UNUMBER, 0, 0));
+ BCMCLI_MAKE_CMD(user_dir, "register_proxy_rx_handler", "Register Proxy Rx handler", _apicli_proxy_rx_handler,
+ BCMCLI_MAKE_PARM_DEFVAL("pon_ni_mask", "PON_NI interface bitmask. 0=all", BCMCLI_PARM_UNUMBER, 0, 0));
+ BCMCLI_MAKE_CMD(user_dir, "read_embedded_logger", "Read embedded logger", _apicli_read_embedded_logger,
+ BCMCLI_MAKE_PARM_ENUM("file_id", "file_id", enum_table_file_id, 0));
+
+ BCMCLI_MAKE_CMD_NOPARM(user_dir, "get_all_log_entries", "Get all embedded log entries", _apicli_get_all_log_entries);
+#endif
+
+#ifndef SIMULATION_BUILD
+ BCMCLI_MAKE_CMD(user_dir, "pcistat", "Print PCIe statistics", _apicli_pcistat,
+ BCMCLI_MAKE_PARM("clear", "clear", BCMCLI_PARM_STRING, BCMCLI_PARM_FLAG_OPTIONAL));
+ BCMCLI_MAKE_CMD(user_dir, "pcidump", "Print PCIe ring", _apicli_pcidump,
+ BCMCLI_MAKE_PARM("dir", "direction: tx or rx", BCMCLI_PARM_STRING, 0),
+ BCMCLI_MAKE_PARM("from", "start index", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM("howmany", "howmany", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL));
+#endif
+
+#ifndef RELEASE_BUILD
+ bcmolt_remote_logger_appl_init();
+ bcmolt_remote_logger_appl_cli_init(user_dir);
+#endif
+
+ err = bcmolt_user_appl_cli_image_transfer_init(user_dir);
+ BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err);
+
+ err = bcmolt_user_appl_cli_sw_upgrade_init(user_dir);
+ BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err);
+
+ bcmolt_ps_appl_init();
+ err = bcmolt_user_appl_ps_cli_init(user_dir);
+ BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err);
+
+ bcmolt_user_appl_playback_init();
+ bcmolt_user_appl_playback_cli_init(user_dir);
+
+ bcmolt_user_appl_cli_handle_system_mode_change(current_device);
+
+ return err;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_cli.h b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_cli.h
new file mode 100644
index 0000000..43a105e
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_cli.h
@@ -0,0 +1,45 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_CLI_H_
+#define _BCMOLT_USER_APPL_CLI_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_msg.h>
+
+void bcmolt_user_appl_indication_cb(bcmolt_devid dev, bcmolt_msg *msg);
+
+void bcmolt_user_appl_proxy_rx_cb(bcmolt_devid dev, bcmolt_msg *msg);
+
+void bcmolt_user_appl_cli_handle_system_mode_change(bcmolt_devid dev);
+
+bcmos_errno bcmolt_user_appl_cli_init(bcmcli_entry *top_dir);
+
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_ex_cli.c b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_ex_cli.c
new file mode 100644
index 0000000..b6ac016
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_ex_cli.c
@@ -0,0 +1,771 @@
+/*
+<: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 <bcmolt_msg.h>
+#include <bcmolt_api.h>
+#include <bcmcli.h>
+#include <bcm_api_cli.h>
+#include <bcm_api_cli_helpers.h>
+#include <bcm_dev_log.h>
+#include "bcmolt_user_appl_ex_cli.h"
+
+#ifdef ENABLE_LOG
+static dev_log_id user_appl_ex_log_id;
+
+static void user_appl_general_indication_cb(bcmolt_devid olt, bcmolt_msg *msg)
+{
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "[-- Dev %u: Indication Received --]\n", olt);
+ switch (msg->obj_type)
+ {
+ case BCMOLT_OBJ_ID_DEBUG:
+ break;
+ case BCMOLT_OBJ_ID_GPON_NI:
+ switch (msg->subgroup)
+ {
+ case BCMOLT_GPON_NI_AUTO_ID_LOS:
+ break;
+ case BCMOLT_GPON_NI_AUTO_ID_STATE_CHANGE_COMPLETED:
+ {
+ bcmolt_gpon_ni_state_change_completed *ind = (bcmolt_gpon_ni_state_change_completed*)msg;
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "Dev %u: pon ni state changed new_state %d, prev state %d, result %d \n",
+ olt, ind->data.new_state, ind->data.previous_state, ind->data.result);
+ break;
+ }
+
+ default:
+ break;
+ /* ... */
+ }
+ break;
+ case BCMOLT_OBJ_ID_GPON_ONU:
+ break;
+ default:
+ break;
+ /* ... */
+ }
+ bcmolt_msg_free(msg);
+}
+
+static void user_appl_pon_ni_indication_cb(bcmolt_devid olt, bcmolt_msg *msg)
+{
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "[-- Dev %u: Pon NI Indication Received --]\n", olt);
+ switch (msg->obj_type)
+ {
+ case BCMOLT_OBJ_ID_GPON_NI:
+ switch (msg->subgroup)
+ {
+ case BCMOLT_GPON_NI_AUTO_ID_LOS:
+ break;
+ case BCMOLT_GPON_NI_AUTO_ID_STATE_CHANGE_COMPLETED:
+ {
+ bcmolt_gpon_ni_state_change_completed *ind = (bcmolt_gpon_ni_state_change_completed*)msg;
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "Dev %u: pon ni state changed new_state %d, prev state %d, result %d \n",
+ olt, ind->data.new_state, ind->data.previous_state, ind->data.result);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "[-- Dev %u: some other indication --]\n", olt);
+ break;
+ /* ... */
+ }
+ bcmolt_msg_free(msg);
+}
+
+static void user_appl_device_logger_indication_cb(bcmolt_devid olt, bcmolt_msg *msg)
+{
+ if (msg->subgroup == BCMOLT_DEBUG_AUTO_ID_CLI_OUTPUT)
+ {
+ bcmolt_debug_cli_output *ind = (bcmolt_debug_cli_output *)msg;
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "Dev %u: [device logger] %s\n", olt, ind->data.data.val);
+ }
+}
+
+static bcmos_errno user_appl_indication_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_rx_cfg rx_cfg;
+ bcmos_errno rc;
+
+ rx_cfg.obj_type = BCMOLT_OBJ_ID_DEBUG;
+ rx_cfg.rx_cb =user_appl_device_logger_indication_cb;
+ rx_cfg.flags = BCMOLT_AUTO_FLAGS_NONE;
+ rc = bcmolt_auto_rx_cb_set(current_device, &rx_cfg);
+ if (rc != BCM_ERR_OK)
+ {
+ return rc;
+ }
+
+ rx_cfg.obj_type = BCMOLT_OBJ_ID_GPON_NI;
+ rx_cfg.rx_cb = user_appl_pon_ni_indication_cb;
+ rx_cfg.flags = BCMOLT_AUTO_FLAGS_NONE;
+ rc = bcmolt_auto_rx_cb_set(current_device, &rx_cfg);
+ if (rc != BCM_ERR_OK)
+ {
+ return rc;
+ }
+
+ rx_cfg.obj_type = BCMOLT_OBJECT_ANY;
+ rx_cfg.rx_cb = user_appl_general_indication_cb;
+ rx_cfg.flags = BCMOLT_AUTO_FLAGS_NONE;
+ return bcmolt_auto_rx_cb_set(current_device, &rx_cfg);
+}
+
+static bcmos_errno user_appl_log_entry_set(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmos_errno err;
+ bcmolt_log_entry_cfg cfg; /**< declare main API struct */
+ bcmolt_log_entry_key key = {.log_id= 0}; /**< declare key */
+
+ /* init the API struct */
+ BCMOLT_CFG_INIT(&cfg, log_entry, key);
+
+ BCMOLT_CFG_PROP_SET(&cfg, log_entry, log_level_print, BCMOLT_LOG_LEVEL_INFO);
+ BCMOLT_CFG_PROP_SET(&cfg, log_entry, log_level_save, BCMOLT_LOG_LEVEL_DEBUG);
+ /* call API */
+ err = bcmolt_cfg_set(0, &cfg.hdr);
+ return err;
+}
+
+static bcmos_errno user_appl_gpon_ni_disable_indication (bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmos_errno err;
+ bcmolt_gpon_ni_auto_cfg cfg; /**< declare main API struct */
+ bcmolt_gpon_ni_key key = {.pon_ni= 0}; /**< declare key */
+
+ /* init the API struct */
+ BCMOLT_AUTO_CFG_INIT(&cfg, gpon_ni, key);
+
+ BCMOLT_AUTO_CFG_PROP_SET(&cfg, gpon_ni, serial_number_acquisition_cycle_start, BCMOS_FALSE);
+ BCMOLT_AUTO_CFG_PROP_SET(&cfg, gpon_ni, stat_alarm_cleared, BCMOS_FALSE);
+ /* call API */
+ err = bcmolt_auto_cfg_set(0, &cfg.hdr);
+ return err;
+}
+
+static bcmos_errno user_appl_redirect_device_logger (bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_debug_cfg cfg;
+ bcmolt_debug_key key = {};
+
+ BCMOLT_CFG_INIT(&cfg, debug, key);
+ BCMOLT_CFG_PROP_SET(&cfg, debug, console_redirection, BCMOLT_CONSOLE_REDIRECTION_CLONE);
+ bcmolt_cfg_set(0, &cfg.hdr);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno user_appl_gpon_device_init(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_device_cfg cfg;
+ bcmos_errno err;
+ bcmolt_device_key key = {.reserved = 0};
+ bcmolt_device_nni_speed nni_speed = {}; /**< Device network interface speed configuration */
+ bcmolt_device_connect oper;
+
+ nni_speed.first_half = BCMOLT_NNI_SPEED_GBPS_1;
+ nni_speed.second_half = BCMOLT_NNI_SPEED_GBPS_1;
+
+ BCMOLT_CFG_INIT(&cfg, device, key);
+ BCMOLT_CFG_PROP_SET(&cfg, device, system_mode, BCMOLT_SYSTEM_MODE_GPON__16_X);
+ BCMOLT_CFG_PROP_SET(&cfg, device, keepalive_interval, 5);
+ BCMOLT_CFG_PROP_SET(&cfg, device, keepalive_tolerance, 5);
+ BCMOLT_CFG_PROP_SET(&cfg, device, nni_speed, nni_speed);
+ err = bcmolt_cfg_set(0, &cfg.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ BCMOLT_OPER_INIT(&oper, device, connect, key);
+ err = bcmolt_oper_submit(0, &oper.hdr);
+ return err;
+}
+
+static bcmos_errno user_appl_gpon_set_trx(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_trx_cfg cfg;
+ bcmolt_gpon_trx_key key = {.pon_ni = 0};
+
+ BCMOLT_CFG_INIT(&cfg, gpon_trx, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_trx, transceiver_type, BCMOLT_TRX_TYPE_SOURCE_PHOTONICS);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_set_interworking_mode(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_iwf_cfg cfg;
+ bcmolt_gpon_iwf_key key = {.pon_ni = 0};
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf, iwf_mode, BCMOLT_IWF_MODE_PER_FLOW_MODE);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+
+}
+
+static bcmos_errno user_appl_gpon_set_mac_table_config(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_iwf_cfg cfg_iwf;
+ bcmolt_gpon_iwf_key key_iwf = {.pon_ni = 0};
+ bcmolt_mac_table_configuration mac_table_configuration;
+ mac_table_configuration.aging_time = 10000;
+ mac_table_configuration.automatic_mac_aging = BCMOLT_CONTROL_STATE_DISABLE;
+ mac_table_configuration.automatic_mac_learning = BCMOLT_CONTROL_STATE_DISABLE;
+ mac_table_configuration.automatic_mac_move = BCMOLT_CONTROL_STATE_DISABLE;
+ mac_table_configuration.automatic_static_mode = BCMOLT_CONTROL_STATE_DISABLE;
+ mac_table_configuration.default_flow_id = 600;
+ mac_table_configuration.learning_mode = BCMOLT_MAC_TABLE_LEARNING_MODE_NORMAL;
+ mac_table_configuration.miss_fallback = BCMOLT_MAC_TABLE_MISS_FALLBACK_DEFAULT_FLOW;
+
+ BCMOLT_CFG_INIT(&cfg_iwf, gpon_iwf, key_iwf);
+ BCMOLT_CFG_PROP_SET(&cfg_iwf, gpon_iwf, mac_table_configuration, mac_table_configuration);
+ /* must set the IWF to per fow for mac table mapping to work */
+ BCMOLT_CFG_PROP_SET(&cfg_iwf, gpon_iwf, iwf_mode, BCMOLT_IWF_MODE_PER_FLOW_MODE);
+ return bcmolt_cfg_set(0, &cfg_iwf.hdr);
+}
+
+static bcmos_errno user_appl_gpon_activate_pon(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_ni_set_pon_state oper_ni;
+ bcmolt_gpon_ni_key key_ni = {.pon_ni = 0};
+
+ BCMOLT_OPER_INIT(&oper_ni, gpon_ni, set_pon_state, key_ni);
+ BCMOLT_OPER_PROP_SET(&oper_ni, gpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_WORKING);
+ return bcmolt_oper_submit(0, &oper_ni.hdr);
+}
+
+static bcmos_errno user_appl_gpon_start_sn_acquisition(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_ni_cfg cfg_ni;
+ bcmolt_gpon_ni_key key_ni = {.pon_ni = 0};
+ bcmolt_gpon_sn_acquisition sn_acquisition;
+
+ sn_acquisition.control = BCMOLT_CONTROL_STATE_ENABLE;
+ sn_acquisition.interval = 77000;
+ sn_acquisition.onu_post_discovery_mode = BCMOLT_ONU_POST_DISCOVERY_MODE_DISABLE;
+
+ BCMOLT_CFG_INIT(&cfg_ni, gpon_ni, key_ni);
+ BCMOLT_CFG_PROP_SET(&cfg_ni, gpon_ni, sn_acquisition, sn_acquisition);
+ return bcmolt_cfg_set(0, &cfg_ni.hdr);
+}
+
+static bcmos_errno user_appl_gpon_ni_get_stats(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_ni_stat stat; /**< declare main API struct */
+ bcmolt_gpon_ni_key key = {}; /**< declare key */
+
+ /* init the API struct */
+ BCMOLT_STAT_INIT(&stat, gpon_ni, key);
+ BCMOLT_STAT_PROP_GET(&stat, gpon_ni, all_properties);
+ return bcmolt_stat_get(0, &stat.hdr, BCMOS_TRUE);
+}
+
+static bcmos_errno user_appl_gpon_onu_config(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_key key = {.pon_ni = 0, .onu_id = 2};
+ bcmolt_serial_number serial_number = {.vendor_id = {0x00,0x00,0x00, 0x00}, .vendor_specific = {0x00,0x00,0x00, 0x02}};
+ bcmolt_arr_u8_10 password = {.arr = {0x00,0x00,0x00, 0x00, 0x00,0x00,0x00, 0x00, 0x00, 0x00}};
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, serial_number, serial_number);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, password, password);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, auto_password_learning, BCMOS_TRUE);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, us_fec, BCMOS_FALSE);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, omci_port_id, 2);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, ds_ber_reporting_interval, 5000);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_activate_onu(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_onu_set_onu_state oper;
+ bcmolt_gpon_onu_key key = {.pon_ni = 0, .onu_id = 2};
+
+ BCMOLT_OPER_INIT(&oper, gpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_ACTIVE);
+ return bcmolt_oper_submit(0, &oper.hdr);
+}
+
+static bcmos_errno user_appl_gpon_alloc_id_config(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_alloc_cfg cfg;
+ bcmolt_gpon_alloc_key key = {.pon_ni = 0, .alloc_id = 320};
+
+ bcmolt_pon_alloc_sla sla; /**< Alloc ID SLA. */
+
+ sla.additional_bw_eligibility = BCMOLT_ADDITIONAL_BW_ELIGIBILITY_NON_ASSURED;
+ sla.cbr_rt_bw = 0;
+ sla.cbr_nrt_bw = 140000000;
+ sla.guaranteed_bw = 140000000;
+ sla.maximum_bw = 140000000;
+ sla.alloc_type = BCMOLT_ALLOC_TYPE_NSR;
+ sla.cbr_rt_compensation = BCMOS_FALSE;
+ sla.cbr_nrt_ap_index = 0;
+ sla.cbr_rt_ap_index = 0;
+ sla.weight = 0;
+ sla.priority = 0;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_alloc, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_alloc, onu_id, 2);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_alloc, sla, sla);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+/* Configure a GEM - for DS or bidirectional traffic */
+static bcmos_errno user_appl_gpon_gem_port_config(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_gem_port_cfg cfg;
+ bcmolt_gpon_gem_port_key key = {.pon_ni = 0, .gem_port_id = 320};
+
+ bcmolt_gem_port_configuration configuration = {.direction = BCMOLT_GEM_PORT_DIRECTION_BIDIRECTIONAL, .type = BCMOLT_GEM_PORT_TYPE_UNICAST};
+ BCMOLT_CFG_INIT(&cfg, gpon_gem_port, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_gem_port, onu_id, 2);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_gem_port, downstream_encryption_mode, BCMOLT_CONTROL_STATE_DISABLE);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_gem_port, configuration, configuration);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_gem_port, upstream_destination_queue, BCMOLT_US_GEM_PORT_DESTINATION_DATA);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_gem_port, control, BCMOLT_CONTROL_STATE_ENABLE);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_ds_gem_port_modify(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ /* this can only be called in PER flow mode */
+ /* set DS port mapping initial GEM 300, final GEM 320 */
+ bcmolt_gpon_iwf_ds_egress_flow_key key = {.pon_ni = 0, .flow_id = 300};
+ bcmolt_gpon_iwf_ds_egress_flow_cfg cfg;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_ds_egress_flow, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_ds_egress_flow, gem_port, 320);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_set_ds_per_vlan_mapping_method(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ /* When working with per flow mode the user must add an entry for each VID (flow) this way */
+ bcmolt_gpon_iwf_ds_ingress_flow_key key = {.pon_ni = 0, .vlan_id = 300};
+ bcmolt_gpon_iwf_ds_ingress_flow_cfg cfg;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_ds_ingress_flow, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_ds_ingress_flow, mapping_method, BCMOLT_VLAN_TO_FLOW_MAPPING_METHOD_VID);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_ds_ingress_flow, mapping_tag, BCMOLT_MAPPING_TAG_METHOD_OUTER_VID);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_ds_ingress_flow, vlan_action, BCMOLT_DS_VLAN_ACTION_TRANSPARENT);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_send_omci_packet(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_onu_cpu_packets proxy; /**< declare main API struct */
+ bcmolt_gpon_onu_key key = {.pon_ni = 0, .onu_id = 2}; /**< declare key */
+ bcmolt_u8_list_u32_max_2048 buffer;
+ uint8_t cpu_buf[100];
+ buffer.len = 48;
+ buffer.val = cpu_buf;
+
+ /* init the API struct */
+ BCMOLT_PROXY_INIT(&proxy, gpon_onu, cpu_packets, key);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, packet_type, BCMOLT_PACKET_TYPE_OMCI);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, calc_crc, BCMOS_TRUE);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, number_of_packets, 1);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, packet_size, 48);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, buffer, buffer);
+
+ /* call API */
+ return bcmolt_proxy_send(0, &proxy.hdr);
+}
+
+static bcmos_errno user_appl_gpon_send_cpu_packets_over_gem_ports(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_ni_cpu_packets proxy; /**< declare main API struct */
+ bcmolt_gpon_ni_key key = {.pon_ni = 0}; /**< declare key */
+ bcmolt_gpon_gem_id gem_port_id_array[10];
+ bcmolt_gpon_gem_id_list_u8_max_16 gem_port_list;
+ bcmolt_u8_list_u32_max_2048 buffer;
+ uint8_t cpu_buf[100];
+ buffer.len = 96;
+ buffer.val = cpu_buf;
+
+ gem_port_list.len = 10;
+ gem_port_list.val = gem_port_id_array;
+
+ /* init the API struct */
+ BCMOLT_PROXY_INIT(&proxy, gpon_ni, cpu_packets, key);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, packet_type, BCMOLT_PACKET_TYPE_CPU);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, calc_crc, BCMOS_TRUE);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, gem_port_list, gem_port_list);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, buffer, buffer);
+
+ /* call API */
+ return bcmolt_proxy_send(0, &proxy.hdr);
+}
+
+static bcmos_errno user_appl_gpon_add_mac_entry(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ /* this can only be called in per flow mode. */
+ /* In order to pass traffic you must also add an entry to IWF ingress port as described in _gpon_set_per_vlan_mapping_method */
+ bcmolt_gpon_iwf_mac_table_cfg cfg;
+ bcmolt_gpon_iwf_mac_table_key key = {.pon_ni = 0, .mac_address.u8 = {0x00,0x00,0x00, 0x00, 0x00,0x00}, .vlan = 320};
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_mac_table, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_mac_table, flow_id, 320);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_dump_mac_table(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ const uint16_t max_msgs_per_call = 10;
+ const bcmolt_devid device = 0;
+ const uint16_t pon = 0;
+
+ bcmos_errno err;
+ bcmolt_msg_set *msg_set;
+ bcmolt_gpon_iwf_mac_table_cfg filter;
+ uint16_t i;
+ uint16_t count = 0;
+
+ /* a key of all Fs means "start from the beginning" */
+ bcmolt_gpon_iwf_mac_table_key wildcard_key =
+ { .pon_ni = pon, .mac_address.u8 = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, .vlan = 0xFFFF };
+
+ /* allocate space for the return values */
+ err = bcmolt_msg_set_alloc(BCMOLT_OBJ_ID_GPON_IWF_MAC_TABLE, BCMOLT_MGT_GROUP_CFG, max_msgs_per_call, &msg_set);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ /* initialize the filter structure and ask to read the flow ID / static flag */
+ BCMOLT_CFG_INIT(&filter, gpon_iwf_mac_table, wildcard_key);
+ BCMOLT_MSGSET_CFG_PROP_GET(msg_set, gpon_iwf_mac_table, flow_id);
+ BCMOLT_MSGSET_CFG_PROP_GET(msg_set, gpon_iwf_mac_table, stat);
+
+ /* filter to only include non-static entries (you could also filter on flow ID, or omit this to include all) */
+ BCMOLT_CFG_PROP_SET(&filter, gpon_iwf_mac_table, stat, BCMOS_FALSE);
+
+ do
+ {
+ /* call get multiple objects API function */
+ err = bcmolt_cfg_get_multi(device, &filter.hdr, BCMOLT_FILTER_FLAGS_NONE, msg_set);
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "bcmolt_cfg_get_multi returned error: %s (%d)\n", bcmos_strerror(err), err);
+ break;
+ }
+
+ /* print each key/config structure that was received */
+ for (i = 0; i < msg_set->num_instances; i++)
+ {
+ bcmolt_gpon_iwf_mac_table_cfg *cfg = (bcmolt_gpon_iwf_mac_table_cfg *)msg_set->msg[i];
+ bcmcli_session_print(
+ session,
+ "entry[%d] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ count,
+ cfg->key.mac_address.u8[0],
+ cfg->key.mac_address.u8[1],
+ cfg->key.mac_address.u8[2],
+ cfg->key.mac_address.u8[3],
+ cfg->key.mac_address.u8[4],
+ cfg->key.mac_address.u8[5]);
+ bcmcli_session_print(session, "entry[%d] VID: %d\n", count, cfg->key.vlan);
+ bcmcli_session_print(session, "entry[%d] flow ID: %d\n", count, cfg->data.flow_id);
+ bcmcli_session_print(session, "entry[%d] static: %s\n", count, cfg->data.stat ? "yes" : "no");
+ count++;
+ }
+
+ /* update the key for next call */
+ filter.key = *((bcmolt_gpon_iwf_mac_table_key *)msg_set->next_key);
+
+ /* keep calling the function until we have retrieved all entries */
+ } while (msg_set->more);
+
+ bcmolt_msg_set_free(msg_set);
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno user_appl_gpon_set_us_flow_configuration(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_gpon_iwf_us_flow_cfg cfg;
+ bcmolt_gpon_iwf_us_flow_key key = {.pon_ni = 0, .gem_port_id = 320};
+ bcmolt_vlan_tag vlan_tag;
+
+ /* vlad tag parameters for the add vlan */
+ vlan_tag.pbit = 9;
+ vlan_tag.vlan_id = 300;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_us_flow, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_us_flow, flow_id, 320);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_us_flow, mac_learning, BCMOS_FALSE);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_us_flow, vlan_action, BCMOLT_US_VLAN_ACTION_ADD);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_us_flow, vlan_tag, vlan_tag);
+ /* what is the tpid index */
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_us_flow, tpid_index, 0);
+ return bcmolt_cfg_set(0, &cfg.hdr);
+}
+
+static bcmos_errno user_appl_gpon_configure_and_activate_pon(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ /* TRX and PON Link configuration structures + PON control operation */
+ bcmos_errno rc;
+ bcmolt_gpon_ni_key key_ni = {.pon_ni = 0};
+ bcmolt_gpon_ni_cfg cfg_ni;
+ bcmolt_gpon_ni_set_pon_state oper_ni;
+
+ bcmolt_gpon_sn_acquisition sn_acquisition = {.control = BCMOLT_CONTROL_STATE_ENABLE, .interval = 10000, .onu_post_discovery_mode = BCMOLT_ONU_POST_DISCOVERY_MODE_NONE};
+ bcmolt_pon_drift_control drift_control = {.drift_interval = 1000, .drift_limit = 4, .transmission_control_limit = 8};
+ bcmolt_ber_monitor_params ber_monitor = {.us_ber_interval = 1000, .sd_threshold = 5, .sf_threshold = 3};
+ bcmolt_gpon_trx_key key_trx = {.pon_ni = 0};
+ bcmolt_gpon_trx_cfg cfg_trx;
+ bcmolt_trx_delimiter delimiter = {};
+
+ /* overide only the relevant parameters */
+ delimiter.pattern[0] = 0x0B;
+ delimiter.pattern[1] = 0x59;
+ delimiter.pattern[2] = 0x83;
+ delimiter.size = 3;
+ delimiter.window_size = 124;
+
+ BCMOLT_CFG_INIT(&cfg_trx, gpon_trx, key_trx);
+ BCMOLT_CFG_PROP_SET(&cfg_trx, gpon_trx, transceiver_type, BCMOLT_TRX_TYPE_SOURCE_PHOTONICS);
+ BCMOLT_CFG_PROP_SET(&cfg_trx, gpon_trx, delimiter, delimiter);
+ rc = bcmolt_cfg_set(0, &cfg_trx.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ bcm_dev_log_log(user_appl_ex_log_id, DEV_LOG_LEVEL_DEBUG, BCM_LOG_FLAG_NO_HEADER,
+ "Error in set trx %d", rc);
+ }
+
+ /* Initialize and configure PON */
+ BCMOLT_CFG_INIT(&cfg_ni, gpon_ni, key_ni);
+ BCMOLT_CFG_PROP_SET(&cfg_ni, gpon_ni, sn_acquisition, sn_acquisition);
+ BCMOLT_CFG_PROP_SET(&cfg_ni, gpon_ni, drift_control, drift_control);
+ BCMOLT_CFG_PROP_SET(&cfg_ni, gpon_ni, ber_monitor, ber_monitor);
+ BCMOLT_CFG_PROP_SET(&cfg_ni, gpon_ni, ds_ber_reporting_interval, 1000);
+ BCMOLT_CFG_PROP_SET(&cfg_ni, gpon_ni, preassigned_equalization_delay, 0);
+ rc = bcmolt_cfg_set(0, &cfg_ni.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ return rc;
+ }
+
+ BCMOLT_OPER_INIT(&oper_ni, gpon_ni, set_pon_state, key_ni);
+ BCMOLT_OPER_PROP_SET(&oper_ni, gpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_WORKING);
+ return bcmolt_oper_submit(0, &oper_ni.hdr);
+}
+
+static bcmos_errno gpon_add_activate_onu (bcmolt_devid dev, bcmolt_gpon_ni pon_ni, bcmolt_gpon_onu_id onu_id, bcmolt_serial_number serial_number)
+{
+ bcmolt_gpon_onu_cfg onu_cfg;
+ bcmolt_gpon_onu_key onu_key = {.pon_ni = pon_ni, .onu_id = onu_id};
+ bcmolt_gpon_onu_set_onu_state onu_oper;
+ bcmolt_arr_u8_10 password = {.arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
+ bcmolt_gpon_gem_port_cfg gem_cfg;
+ bcmolt_gpon_gem_port_key gem_key = {.pon_ni = pon_ni, .gem_port_id = onu_id};
+ bcmos_errno rc;
+
+ BCMOLT_CFG_INIT(&onu_cfg, gpon_onu, onu_key);
+ BCMOLT_CFG_PROP_SET(&onu_cfg, gpon_onu, serial_number, serial_number);
+ BCMOLT_CFG_PROP_SET(&onu_cfg, gpon_onu, password, password);
+ BCMOLT_CFG_PROP_SET(&onu_cfg, gpon_onu, auto_password_learning, BCMOS_TRUE);
+ BCMOLT_CFG_PROP_SET(&onu_cfg, gpon_onu, us_fec, BCMOS_TRUE);
+ BCMOLT_CFG_PROP_SET(&onu_cfg, gpon_onu, omci_port_id, 2);
+ BCMOLT_CFG_PROP_SET(&onu_cfg, gpon_onu, ds_ber_reporting_interval, 5000);
+ rc = bcmolt_cfg_set(dev, &onu_cfg.hdr);
+ if (rc)
+ {
+ return rc;
+ }
+
+ BCMOLT_CFG_INIT(&gem_cfg, gpon_gem_port, gem_key);
+ BCMOLT_CFG_PROP_SET(&gem_cfg, gpon_gem_port, downstream_encryption_mode, BCMOLT_CONTROL_STATE_ENABLE);
+ rc = bcmolt_cfg_set(dev, &gem_cfg.hdr);
+ if (rc)
+ {
+ return rc;
+ }
+
+ BCMOLT_OPER_INIT(&onu_oper, gpon_onu, set_onu_state, onu_key);
+ BCMOLT_OPER_PROP_SET(&onu_oper, gpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_ACTIVE);
+ rc = bcmolt_oper_submit(dev, &onu_oper.hdr);
+ return rc;
+}
+
+static bcmos_errno user_appl_gpon_config_and_activate_onu (bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ bcmolt_serial_number serial_number = {.vendor_id = {0x00,0x00,0x00, 0x00}, .vendor_specific = {0x00,0x00,0x00, 0x02}};
+ return gpon_add_activate_onu(0, 0, 2, serial_number);
+}
+
+static bcmos_errno user_appl_epon_get_all_link_alarms(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t nparms)
+{
+ const uint16_t max_msgs_per_call = 10;
+ const bcmolt_devid device_id = 0;
+ const uint16_t pon = 0;
+ /* configure filter to select only links which do NOT have all alarms off */
+ const bcmolt_filter_flags flags = BCMOLT_FILTER_FLAGS_INVERT_SELECTION;
+ const bcmolt_epon_link_alarm_state alarm_state_filter =
+ {
+ .invalid_mpcp_report_received = BCMOLT_STATUS_OFF,
+ .key_exchange_failure = BCMOLT_STATUS_OFF,
+ .mpcp_report_timeout = BCMOLT_STATUS_OFF,
+ .oam_keepalive_timeout = BCMOLT_STATUS_OFF,
+ };
+
+ bcmos_errno err = BCM_ERR_OK;
+ bcmolt_msg_set *msg_set;
+ bcmolt_epon_link_cfg filter;
+ bcmolt_epon_link_key wildcard_key;
+ uint16_t i;
+
+ /* retrieve the EPON NI MAC address */
+ bcmolt_epon_ni_cfg pon_cfg;
+ bcmolt_epon_ni_key pon_key = { .epon_ni = pon };
+
+ BCMOLT_CFG_INIT(&pon_cfg, epon_ni, pon_key);
+ BCMOLT_CFG_PROP_GET(&pon_cfg, epon_ni, mac_address);
+ err = bcmolt_cfg_get(device_id, &pon_cfg.hdr);
+ if (BCM_ERR_OK != err)
+ {
+ bcmcli_session_print(session, "Failed to retrieve EPON NI MAC address!\n");
+ return err;
+ }
+
+ /* a key with the EPON NI MAC address means "start from the beginning" */
+ wildcard_key.epon_ni = pon;
+ wildcard_key.mac_address = pon_cfg.data.mac_address;
+
+ /* allocate space for the return values */
+ err = bcmolt_msg_set_alloc(BCMOLT_OBJ_ID_EPON_LINK, BCMOLT_MGT_GROUP_CFG, max_msgs_per_call, &msg_set);
+ if (BCM_ERR_OK != err)
+ {
+ bcmcli_session_print(session, "Failed to allocate space for return!\n");
+ return err;
+ }
+
+ /* initialize the filter structure and ask to read the alarm state */
+ BCMOLT_CFG_INIT(&filter, epon_link, wildcard_key);
+ BCMOLT_MSGSET_CFG_PROP_GET(msg_set, epon_link, alarm_state);
+
+ /* apply filter */
+ BCMOLT_CFG_PROP_SET(&filter, epon_link, alarm_state, alarm_state_filter);
+
+ do
+ {
+ /* call get multiple objects API function */
+ err = bcmolt_cfg_get_multi(device_id, &filter.hdr, flags, msg_set);
+ if (BCM_ERR_OK != err)
+ {
+ bcmcli_session_print(session, "bcmolt_cfg_get_multi returned error: %s (%d)\n", bcmos_strerror(err), err);
+ break;
+ }
+
+ /* print each key/config that was received */
+ for (i = 0; i < msg_set->num_instances; i++)
+ {
+ bcmolt_epon_link_cfg *cfg = (bcmolt_epon_link_cfg*)msg_set->msg[i];
+ bcmcli_session_print(session, "Link: NI %u, MAC %02x%02x%02x%02x%02x%02x\n",
+ cfg->key.epon_ni,
+ cfg->key.mac_address.u8[0],
+ cfg->key.mac_address.u8[1],
+ cfg->key.mac_address.u8[2],
+ cfg->key.mac_address.u8[3],
+ cfg->key.mac_address.u8[4],
+ cfg->key.mac_address.u8[5]);
+ bcmcli_session_print(session, "\tInvalid MPCP Report Received: %u\n",
+ cfg->data.alarm_state.invalid_mpcp_report_received);
+ bcmcli_session_print(session, "\tKey Exchange failure: %u\n",
+ cfg->data.alarm_state.key_exchange_failure);
+ bcmcli_session_print(session, "\tMPCP Report Timeout: %u\n",
+ cfg->data.alarm_state.mpcp_report_timeout);
+ bcmcli_session_print(session, "\tOAM Keepalive Timeout: %u\n",
+ cfg->data.alarm_state.oam_keepalive_timeout);
+ }
+
+ /* update the key for next call */
+ filter.key = *((bcmolt_epon_link_key*)msg_set->next_key);
+
+ /* keep calling the function until we have retrieved all entries */
+ } while (msg_set->more);
+
+ bcmolt_msg_set_free(msg_set);
+ return err;
+}
+#endif
+
+bcmos_errno bcmolt_user_appl_ex_cli_init(void)
+{
+ bcmcli_entry *parent = bcmcli_dir_find(NULL, "user");
+ bcmcli_entry *dir;
+
+#ifdef ENABLE_LOG
+ user_appl_ex_log_id = bcm_dev_log_id_register("user_appl_ex", DEV_LOG_LEVEL_DEBUG, DEV_LOG_ID_TYPE_BOTH);
+#endif
+
+ dir = bcmcli_dir_add(parent, "example", "User application", BCMCLI_ACCESS_ADMIN, NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+#ifdef ENABLE_LOG
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_device_init", "gpon device init", user_appl_gpon_device_init);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "indication_handler", "indication handler", user_appl_indication_handler);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "log_entry_set", "log enrty set", user_appl_log_entry_set);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "redirect_device_logger", "redirect device logger", user_appl_redirect_device_logger);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_ni_disable_indication", "gpon_ni_disable_indication", user_appl_gpon_ni_disable_indication);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_ni_get_stats", "gpon_ni_get_stats", user_appl_gpon_ni_get_stats);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_start_sn_acquisition", "gpon start sn acquisition", user_appl_gpon_start_sn_acquisition);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_set_mac_table_config", "gpon set mac table config", user_appl_gpon_set_mac_table_config);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_set_interworking_mode", "gpon set interworking mode", user_appl_gpon_set_interworking_mode);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_set_trx", "gpon set trx", user_appl_gpon_set_trx);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_activate_pon", "gpon activate pon", user_appl_gpon_activate_pon);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_onu_config", "gpon onu config", user_appl_gpon_onu_config);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_activate_onu", "gpon activate onu", user_appl_gpon_activate_onu);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_alloc_id_config", "gpon alloc config", user_appl_gpon_alloc_id_config);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_gem_port_config", "gpon gem port config", user_appl_gpon_gem_port_config);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_add_mac_entry", "gpon add mac entry", user_appl_gpon_add_mac_entry);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_dump_mac_table", "print entire GPON MAC table", user_appl_gpon_dump_mac_table);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_ds_gem_port_modify", " gpon ds gem port modify", user_appl_gpon_ds_gem_port_modify);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_set_per_vlan_mapping_method", "gpon set per vlan mapping method", user_appl_gpon_set_ds_per_vlan_mapping_method);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_set_us_flow_configuration", "gpon set us flow configuration", user_appl_gpon_set_us_flow_configuration);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_send_omci_packet", "appl gpon send omci packet", user_appl_gpon_send_omci_packet);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_send_cpu_packets_over_gem_ports", "gpon send cpu packets over gem ports", user_appl_gpon_send_cpu_packets_over_gem_ports);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_configure_and_activate_pon", "configure and activate pon", user_appl_gpon_configure_and_activate_pon);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "gpon_config_and_activate_onu", "config and activate onu", user_appl_gpon_config_and_activate_onu);
+ BCMCLI_MAKE_CMD_NOPARM(dir, "epon_get_all_link_alarms", "retrieve all link alarms", user_appl_epon_get_all_link_alarms);
+#endif
+ return BCM_ERR_OK;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_ex_cli.h b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_ex_cli.h
new file mode 100644
index 0000000..1f5e544
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_ex_cli.h
@@ -0,0 +1,38 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_EX_CLI_H_
+#define _BCMOLT_USER_APPL_EX_CLI_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_msg.h>
+
+bcmos_errno bcmolt_user_appl_ex_cli_init(void);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_gpon_utils.c b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_gpon_utils.c
new file mode 100644
index 0000000..ad8d4a7
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_gpon_utils.c
@@ -0,0 +1,768 @@
+/*
+<: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 <bcmolt_host_api.h>
+#include "bcmolt_user_appl_gpon_utils.h"
+#include <bcmolt_dev_selector.h>
+
+static bcmcli_entry *gpon_utils_cli_dir;
+static bcmcli_entry *gpon_utils_cli_top_dir;
+static bcmos_bool gpon_utils_is_registered;
+
+static bcmos_errno bcmolt_user_appl_gpon_utils_create(bcmcli_entry *top_dir);
+
+#define MAX_GEM_PORTS_FOR_SEND 64
+#define MAX_ONUS_FOR_SEND 64
+#define MAX_PACKET_SIZE 2048
+
+typedef enum
+{
+ GENERATED_PACKET_PATTERN_BYTE_CYCLE,
+ GENERATED_PACKET_PATTERN_WORD_CYCLE,
+ GENERATED_PACKET_PATTERN_CONST
+} generated_packet_pattern;
+
+typedef enum
+{
+ PON_MODE_GPON,
+ PON_MODE_XGPON,
+ PON_MODE_INVALID
+} pon_mode;
+
+static uint8_t saved_packet[MAX_PACKET_SIZE];
+static bcmolt_packet_type saved_packet_type;
+static bcmos_bool saved_packet_calc_crc;
+static uint16_t saved_packet_len = 0;
+
+static void gpon_utils_cli_dir_cli_destroy(void)
+{
+ if (gpon_utils_cli_dir)
+ {
+ bcmcli_token_destroy(gpon_utils_cli_dir);
+ gpon_utils_cli_dir = NULL;
+ }
+}
+
+/* Current device change indication */
+static void bcmolt_user_appl_gpon_utils_device_change_ind(bcmcli_session *session, bcmolt_devid dev)
+{
+ bcmolt_system_mode system_mode;
+ bcmos_errno err = bcmolt_system_mode_get(dev, &system_mode);
+
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "Error device Id\n");
+ return;
+ }
+
+ if (system_mode == BCMOLT_SYSTEM_MODE_GPON__16_X ||
+ system_mode == BCMOLT_SYSTEM_MODE_GPON__8_X ||
+ system_mode == BCMOLT_SYSTEM_MODE_GPON__4_X ||
+ system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
+ system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
+ system_mode == BCMOLT_SYSTEM_MODE_XGS__2_X_10_G ||
+ system_mode == BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G)
+ {
+ bcmcli_session_print(session, "Building gpon_utils CLI for device %d\n", dev);
+
+ err = bcmolt_user_appl_gpon_utils_create(gpon_utils_cli_top_dir);
+ if (err)
+ {
+ bcmcli_session_print(session, "Error building gpon_utils CLI\n");
+ }
+ }
+ else
+ {
+ gpon_utils_cli_dir_cli_destroy();
+ }
+}
+
+static inline pon_mode pon_mode_get(bcmolt_pon_ni pon_id)
+{
+ bcmolt_system_mode system_mode;
+ bcmos_errno err = bcmolt_system_mode_get(current_device, &system_mode);
+ if (err != BCM_ERR_OK)
+ {
+ return PON_MODE_INVALID;
+ }
+
+ if (system_mode == BCMOLT_SYSTEM_MODE_GPON_8_XGPON_4_X_COEXISTENCE)
+ {
+ /* special handling for the 8x GPON / 4x XGPON co-existence mode */
+ return pon_id < 8 ? PON_MODE_XGPON : PON_MODE_GPON;
+ }
+ else if (bcmolt_obj_tag_valid_for_system_mode(system_mode, BCMOLT_OBJ_TAG_GPON))
+ {
+ return PON_MODE_GPON;
+ }
+ else if (bcmolt_obj_tag_valid_for_system_mode(system_mode, BCMOLT_OBJ_TAG_XGPON))
+ {
+ return PON_MODE_XGPON;
+ }
+ else
+ {
+ return PON_MODE_INVALID;
+ }
+}
+
+static bcmos_errno build_packet(bcmcli_session *session, bcmolt_packet_type type)
+{
+ uint16_t i;
+ bcmos_bool calc_crc = (bcmos_bool)bcmcli_find_named_parm(session, "calc_crc")->value.unumber;
+ uint16_t packet_size = (uint16_t)bcmcli_find_named_parm(session, "packet_size")->value.unumber;
+ generated_packet_pattern pattern =
+ (generated_packet_pattern)bcmcli_find_named_parm(session, "pattern")->value.unumber;
+ uint8_t const_value = (uint8_t)bcmcli_find_named_parm(session, "const_value")->value.unumber;
+ uint16_t data_offset;
+
+ /* save packet type / size / CRC flag */
+ saved_packet_type = type;
+ saved_packet_len = packet_size;
+ saved_packet_calc_crc = calc_crc;
+
+ if (type == BCMOLT_PACKET_TYPE_CPU)
+ {
+ bcmos_mac_address eth_da = bcmcli_find_named_parm(session, "eth_da")->value.mac;
+ bcmos_mac_address eth_sa = bcmcli_find_named_parm(session, "eth_sa")->value.mac;
+
+ /* copy destination MAC */
+ memcpy(saved_packet, ð_da, 6);
+
+ /* copy source MAC */
+ memcpy(&saved_packet[6], ð_sa, 6);
+
+ /* write EtherType of 0000 */
+ memset(&saved_packet[12], 0, 2);
+
+ /* data starts after EtherType */
+ data_offset = 14;
+ }
+ else
+ {
+ /* fill the entire packet */
+ data_offset = 0;
+ }
+
+ /* fill the rest of the buffer using the specified data pattern */
+ switch (pattern)
+ {
+ case GENERATED_PACKET_PATTERN_BYTE_CYCLE:
+ const_value = 0;
+ for (i = data_offset; i < packet_size; i++)
+ {
+ saved_packet[i] = const_value++;
+ }
+ break;
+ case GENERATED_PACKET_PATTERN_WORD_CYCLE:
+ /* first 4 bytes = 00000000, next 4 = 11111111, next 4 = 22222222, etc */
+ const_value = 0;
+ for (i = data_offset; i < packet_size; i += 4)
+ {
+ uint16_t j;
+ for (j = i; j < i + 4 && j < packet_size; j++)
+ {
+ saved_packet[j] = const_value;
+ }
+ const_value = (const_value == 0xFF) ? 0 : const_value + 0x11;
+ }
+ break;
+ case GENERATED_PACKET_PATTERN_CONST:
+ for (i = data_offset; i < packet_size; i++)
+ {
+ saved_packet[i] = const_value;
+ }
+ break;
+ default:
+ bcmcli_session_print(session, "[Error] Invalid pattern: %u\n", pattern);
+ return BCM_ERR_INTERNAL;
+ }
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno build_cpu_packet_cb(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmos_errno err = build_packet(session, BCMOLT_PACKET_TYPE_CPU);
+ if (err == BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "CPU packet buffer saved successfully\n");
+ }
+ else
+ {
+ bcmcli_session_print(session, "[Error] saving CPU packet buffer: %s\n", bcmos_strerror(err));
+ }
+ return err;
+}
+
+static bcmos_errno build_omci_packet_cb(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmos_errno err = build_packet(session, BCMOLT_PACKET_TYPE_OMCI);
+ if (err == BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "OMCI packet buffer saved successfully\n");
+ }
+ else
+ {
+ bcmcli_session_print(session, "[Error] saving OMCI packet buffer: %s\n", bcmos_strerror(err));
+ }
+ return err;
+}
+
+static bcmos_errno send_packet_to_gpon_gem_list(bcmcli_session *session, uint16_t gem_count, bcmolt_gpon_gem_id *gems)
+{
+ uint16_t count = 1;
+ uint16_t i;
+ uint16_t gem_idx;
+ bcmos_errno err;
+ bcmcli_cmd_parm *parm;
+ bcmolt_devid device = (bcmolt_devid)bcmcli_find_named_parm(session, "device")->value.unumber;
+ bcmolt_pon_ni pon_ni = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "pon_ni")->value.unumber;
+ bcmolt_gpon_ni_key pon_ni_key = { .pon_ni = pon_ni };
+ bcmolt_gpon_gem_id_list_u8_max_16 gem_port_list;
+ bcmolt_gpon_ni_cpu_packets proxy;
+ bcmolt_u8_list_u32_max_2048 buffer = { .len = saved_packet_len, .val = saved_packet };
+
+ if (saved_packet_len == 0)
+ {
+ bcmcli_session_print(session, "[Error] you must build/save a packet before you can send it\n");
+ return BCM_ERR_PARM;
+ }
+
+ parm = bcmcli_find_named_parm(session, "count");
+ if (parm != NULL)
+ {
+ count = (uint16_t)parm->value.unumber;
+ }
+
+ bcmcli_session_print(
+ session,
+ "Sending %u proxy packet%s to %u GEM port%s...\n",
+ count,
+ count == 1 ? "" : "s",
+ gem_count,
+ gem_count == 1 ? "" : "s");
+
+ /* build the proxy API message, except the GEM port list */
+ BCMOLT_PROXY_INIT(&proxy, gpon_ni, cpu_packets, pon_ni_key);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, packet_type, saved_packet_type);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, calc_crc, saved_packet_calc_crc);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, buffer, buffer);
+
+ /* call the proxy API <count> times */
+ for (i = 0; i < count; i++)
+ {
+ /* we may need to split this up into multiple API calls (only 16 GEM ports are allowed per proxy API) */
+ for (gem_idx = 0; gem_idx < gem_count; gem_idx += 16)
+ {
+ gem_port_list.len = MIN(16, gem_count - gem_idx);
+ gem_port_list.val = &gems[gem_idx];
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_ni, cpu_packets, gem_port_list, gem_port_list);
+
+ err = bcmolt_proxy_send(device, &proxy.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(
+ session,
+ "[Error] proxy send: packet index %u GEM index %u: %s\n",
+ i,
+ gem_idx,
+ bcmos_strerror(err));
+ return err;
+ }
+ }
+ }
+
+ bcmcli_session_print(session, "%u proxy packet%s sent successfully!\n", count, count == 1 ? "" : "s");
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno send_packet_to_xgpon_gem_list(bcmcli_session *session, uint16_t gem_count, bcmolt_xgpon_gem_id *gems)
+{
+ uint16_t count = 1;
+ uint16_t i;
+ uint16_t gem_idx;
+ bcmos_errno err;
+ bcmcli_cmd_parm *parm;
+ bcmolt_devid device = (bcmolt_devid)bcmcli_find_named_parm(session, "device")->value.unumber;
+ bcmolt_pon_ni pon_ni = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "pon_ni")->value.unumber;
+ bcmolt_xgpon_ni_key pon_ni_key = { .pon_ni = pon_ni };
+ bcmolt_xgpon_gem_id_list_u8_max_16 gem_port_list;
+ bcmolt_xgpon_ni_cpu_packets proxy;
+ bcmolt_u8_list_u32_max_2048 buffer = { .len = saved_packet_len, .val = saved_packet };
+
+ if (saved_packet_len == 0)
+ {
+ bcmcli_session_print(session, "[Error] you must build/save a packet before you can send it\n");
+ return BCM_ERR_PARM;
+ }
+
+ parm = bcmcli_find_named_parm(session, "count");
+ if (parm != NULL)
+ {
+ count = (uint16_t)parm->value.unumber;
+ }
+
+ bcmcli_session_print(
+ session,
+ "Sending %u proxy packet%s to %u GEM port%s...\n",
+ count,
+ count == 1 ? "" : "s",
+ gem_count,
+ gem_count == 1 ? "" : "s");
+
+ /* build the proxy API message, except the GEM port list */
+ BCMOLT_PROXY_INIT(&proxy, xgpon_ni, cpu_packets, pon_ni_key);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_ni, cpu_packets, packet_type, saved_packet_type);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_ni, cpu_packets, calc_crc, saved_packet_calc_crc);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_ni, cpu_packets, buffer, buffer);
+
+ /* call the proxy API <count> times */
+ for (i = 0; i < count; i++)
+ {
+ /* we may need to split this up into multiple API calls (only 16 GEM ports are allowed per proxy API) */
+ for (gem_idx = 0; gem_idx < gem_count; gem_idx += 16)
+ {
+ gem_port_list.len = MIN(16, gem_count - gem_idx);
+ gem_port_list.val = &gems[gem_idx];
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_ni, cpu_packets, gem_port_list, gem_port_list);
+
+ err = bcmolt_proxy_send(device, &proxy.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(
+ session,
+ "[Error] proxy send: packet index %u GEM index %u: %s\n",
+ i,
+ gem_idx,
+ bcmos_strerror(err));
+ return err;
+ }
+ }
+ }
+
+ bcmcli_session_print(session, "%u proxy packet%s sent successfully!\n", count, count == 1 ? "" : "s");
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno send_to_gem_cb(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmcli_cmd_parm *gem_parm = bcmcli_find_named_parm(session, "gem");
+ bcmolt_pon_ni pon_id = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "pon_ni")->value.unumber;
+ uint16_t i;
+ pon_mode mode = pon_mode_get(pon_id);
+
+ if (mode == PON_MODE_GPON)
+ {
+ static bcmolt_gpon_gem_id gem_id_buf[MAX_GEM_PORTS_FOR_SEND];
+ for (i = 0; i < gem_parm->array_size; i++)
+ {
+ gem_id_buf[i] = (bcmolt_gpon_gem_id)gem_parm->values[i].unumber;
+ }
+ return send_packet_to_gpon_gem_list(session, (uint16_t)gem_parm->array_size, gem_id_buf);
+ }
+ else if (mode == PON_MODE_XGPON)
+ {
+ static bcmolt_xgpon_gem_id gem_id_buf[MAX_GEM_PORTS_FOR_SEND];
+ for (i = 0; i < gem_parm->array_size; i++)
+ {
+ gem_id_buf[i] = (bcmolt_xgpon_gem_id)gem_parm->values[i].unumber;
+ }
+ return send_packet_to_xgpon_gem_list(session, (uint16_t)gem_parm->array_size, gem_id_buf);
+ }
+ else
+ {
+ bcmcli_session_print(session, "[Error] Invalid system mode\n");
+ return BCM_ERR_INTERNAL;
+ }
+}
+
+static bcmos_errno send_packet_to_gpon_onu_list(bcmcli_session *session, uint16_t onu_count, bcmolt_gpon_onu_id *onus)
+{
+ uint16_t count = 1;
+ uint16_t i;
+ uint16_t onu_idx;
+ bcmos_errno err;
+ bcmcli_cmd_parm *parm;
+ bcmolt_devid device = (bcmolt_devid)bcmcli_find_named_parm(session, "device")->value.unumber;
+ bcmolt_pon_ni pon_ni = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "pon_ni")->value.unumber;
+
+ if (saved_packet_len == 0)
+ {
+ bcmcli_session_print(session, "[Error] you must build/save a packet before you can send it\n");
+ return BCM_ERR_PARM;
+ }
+
+ parm = bcmcli_find_named_parm(session, "count");
+ if (parm != NULL)
+ {
+ count = (uint16_t)parm->value.unumber;
+ }
+
+ bcmcli_session_print(
+ session,
+ "Sending %u proxy packet%s to %u ONU%s...\n",
+ count,
+ count == 1 ? "" : "s",
+ onu_count,
+ onu_count == 1 ? "" : "s");
+
+ /* call the proxy API (potentially several times) for each ONU */
+ for (onu_idx = 0; onu_idx < onu_count; onu_idx++)
+ {
+ bcmolt_gpon_onu_key onu_key = { .pon_ni = pon_ni, .onu_id = onus[onu_idx] };
+ bcmolt_u8_list_u32_max_2048 buffer = { .val = saved_packet };
+ bcmolt_gpon_onu_cpu_packets proxy;
+ uint16_t packets_per_proxy;
+
+ /* build the proxy API message, except the buffer/number of packets */
+ BCMOLT_PROXY_INIT(&proxy, gpon_onu, cpu_packets, onu_key);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, packet_type, saved_packet_type);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, calc_crc, saved_packet_calc_crc);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, packet_size, saved_packet_len);
+
+ /* calculate the number of packets we can fit per invocation of the proxy API */
+ packets_per_proxy = MIN(count, 2048 / saved_packet_len);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, number_of_packets, packets_per_proxy);
+
+ /* prepare the buffer data with several copies of the same packet in a row */
+ for (i = 1; i < packets_per_proxy; i++)
+ {
+ memcpy(&saved_packet[i * saved_packet_len], saved_packet, saved_packet_len);
+ }
+
+ /* call the proxy API enough times to send the requested number of packets */
+ for (i = 0; i < count; i += packets_per_proxy)
+ {
+ uint16_t number_of_packets = MIN(packets_per_proxy, count - i);
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, number_of_packets, number_of_packets);
+
+ buffer.len = number_of_packets * saved_packet_len;
+ BCMOLT_PROXY_PROP_SET(&proxy, gpon_onu, cpu_packets, buffer, buffer);
+
+ err = bcmolt_proxy_send(device, &proxy.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(
+ session,
+ "[Error] proxy send: ONU %u packet index %u: %s\n",
+ onus[onu_idx],
+ i,
+ bcmos_strerror(err));
+ return err;
+ }
+ }
+ }
+
+ bcmcli_session_print(session, "%u proxy packet%s sent successfully!\n", count, count == 1 ? "" : "s");
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno send_packet_to_xgpon_onu_list(bcmcli_session *session, uint16_t onu_count, bcmolt_xgpon_onu_id *onus)
+{
+ uint16_t count = 1;
+ uint16_t i;
+ uint16_t onu_idx;
+ bcmos_errno err;
+ bcmcli_cmd_parm *parm;
+ bcmolt_devid device = (bcmolt_devid)bcmcli_find_named_parm(session, "device")->value.unumber;
+ bcmolt_pon_ni pon_ni = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "pon_ni")->value.unumber;
+
+ if (saved_packet_len == 0)
+ {
+ bcmcli_session_print(session, "[Error] you must build/save a packet before you can send it\n");
+ return BCM_ERR_PARM;
+ }
+
+ parm = bcmcli_find_named_parm(session, "count");
+ if (parm != NULL)
+ {
+ count = (uint16_t)parm->value.unumber;
+ }
+
+ bcmcli_session_print(
+ session,
+ "Sending %u proxy packet%s to %u ONU%s...\n",
+ count,
+ count == 1 ? "" : "s",
+ onu_count,
+ onu_count == 1 ? "" : "s");
+
+ /* call the proxy API (potentially several times) for each ONU */
+ for (onu_idx = 0; onu_idx < onu_count; onu_idx++)
+ {
+ bcmolt_xgpon_onu_key onu_key = { .pon_ni = pon_ni, .onu_id = onus[onu_idx] };
+ bcmolt_u8_list_u32_max_2048 buffer = { .val = saved_packet };
+ bcmolt_xgpon_onu_cpu_packets proxy;
+ uint16_t packets_per_proxy;
+
+ /* build the proxy API message, except the buffer/number of packets */
+ BCMOLT_PROXY_INIT(&proxy, xgpon_onu, cpu_packets, onu_key);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_onu, cpu_packets, packet_type, saved_packet_type);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_onu, cpu_packets, calc_crc, saved_packet_calc_crc);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_onu, cpu_packets, packet_size, saved_packet_len);
+
+ /* calculate the number of packets we can fit per invocation of the proxy API */
+ packets_per_proxy = MIN(count, 2048 / saved_packet_len);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_onu, cpu_packets, number_of_packets, packets_per_proxy);
+
+ /* prepare the buffer data with several copies of the same packet in a row */
+ for (i = 1; i < packets_per_proxy; i++)
+ {
+ memcpy(&saved_packet[i * saved_packet_len], saved_packet, saved_packet_len);
+ }
+
+ /* call the proxy API enough times to send the requested number of packets */
+ for (i = 0; i < count; i += packets_per_proxy)
+ {
+ uint16_t number_of_packets = MIN(packets_per_proxy, count - i);
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_onu, cpu_packets, number_of_packets, number_of_packets);
+
+ buffer.len = number_of_packets * saved_packet_len;
+ BCMOLT_PROXY_PROP_SET(&proxy, xgpon_onu, cpu_packets, buffer, buffer);
+
+ err = bcmolt_proxy_send(device, &proxy.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(
+ session,
+ "[Error] proxy send: ONU %u packet index %u: %s\n",
+ onus[onu_idx],
+ i,
+ bcmos_strerror(err));
+ return err;
+ }
+ }
+ }
+
+ bcmcli_session_print(session, "%u proxy packet%s sent successfully!\n", count, count == 1 ? "" : "s");
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno send_to_onu_cb(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmcli_cmd_parm *onu_parm = bcmcli_find_named_parm(session, "onu");
+ bcmolt_pon_ni pon_id = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "pon_ni")->value.unumber;
+ uint16_t i;
+ pon_mode mode = pon_mode_get(pon_id);
+
+ if (mode == PON_MODE_GPON)
+ {
+ static bcmolt_gpon_onu_id onu_id_buf[MAX_ONUS_FOR_SEND];
+ for (i = 0; i < onu_parm->array_size; i++)
+ {
+ onu_id_buf[i] = (bcmolt_gpon_onu_id)onu_parm->values[i].unumber;
+ }
+ return send_packet_to_gpon_onu_list(session, (uint16_t)onu_parm->array_size, onu_id_buf);
+ }
+ else if (mode == PON_MODE_XGPON)
+ {
+ static bcmolt_xgpon_onu_id onu_id_buf[MAX_ONUS_FOR_SEND];
+ for (i = 0; i < onu_parm->array_size; i++)
+ {
+ onu_id_buf[i] = (bcmolt_xgpon_onu_id)onu_parm->values[i].unumber;
+ }
+ return send_packet_to_xgpon_onu_list(session, (uint16_t)onu_parm->array_size, onu_id_buf);
+ }
+ else
+ {
+ bcmcli_session_print(session, "[Error] Invalid system mode\n");
+ return BCM_ERR_STATE;
+ }
+}
+
+static void dump_saved_mac(bcmcli_session *session, uint16_t offset)
+{
+ uint16_t i;
+ for (i = 0; i < 6; i++)
+ {
+ if (i != 0)
+ {
+ bcmcli_session_print(session, ":");
+ }
+ bcmcli_session_print(session, "%02X", saved_packet[offset + i]);
+ }
+}
+
+static bcmos_errno dump_packet_cb(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ uint16_t i;
+ uint16_t data_offset;
+
+ if (saved_packet_len == 0)
+ {
+ bcmcli_session_print(session, "No packet has been built yet\n");
+ return BCM_ERR_OK;
+ }
+
+ bcmcli_session_print(session, "Saved packet parameters:\n");
+ bcmcli_session_print(session, " Type: %s\n", saved_packet_type == BCMOLT_PACKET_TYPE_CPU ? "CPU" : "OMCI");
+ bcmcli_session_print(session, " Length: %u\n", saved_packet_len);
+ bcmcli_session_print(session, " Auto-calculate CRC: %s\n", saved_packet_calc_crc ? "yes" : "no");
+
+ if (saved_packet_type == BCMOLT_PACKET_TYPE_CPU)
+ {
+ bcmcli_session_print(session, " Ethernet DA: ");
+ dump_saved_mac(session, 0);
+ bcmcli_session_print(session, "\n");
+
+ bcmcli_session_print(session, " Ethernet SA: ");
+ dump_saved_mac(session, 6);
+ bcmcli_session_print(session, "\n");
+
+ bcmcli_session_print(session, " EtherType: 0x%02X%02X\n", saved_packet[12], saved_packet[13]);
+ data_offset = 14;
+ }
+ else
+ {
+ data_offset = 0;
+ }
+
+ bcmcli_session_print(session, "Data bytes (starting at offset %u):", data_offset);
+ for (i = data_offset; i < saved_packet_len; i++)
+ {
+ if ((i - data_offset) % 16 == 0)
+ {
+ bcmcli_session_print(session, "\n ");
+ }
+ else
+ {
+ bcmcli_session_print(session, " ");
+ }
+ bcmcli_session_print(session, "%02X", saved_packet[i]);
+ }
+ bcmcli_session_print(session, "\n");
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno bcmolt_user_appl_gpon_utils_init(bcmcli_entry *top_dir)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ if (!gpon_utils_is_registered)
+ {
+ gpon_utils_is_registered = BCMOS_TRUE;
+
+ /* Subscribe for device change indication */
+ err = bcmolt_dev_sel_ind_register(bcmolt_user_appl_gpon_utils_device_change_ind);
+ }
+ return err ? err : bcmolt_user_appl_gpon_utils_create(top_dir);
+}
+
+static bcmos_errno bcmolt_user_appl_gpon_utils_create(bcmcli_entry *top_dir)
+{
+ static bcmcli_enum_val generated_packet_pattern_table[] =
+ {
+ { .name = "byte_cycle", .val = GENERATED_PACKET_PATTERN_BYTE_CYCLE },
+ { .name = "word_cycle", .val = GENERATED_PACKET_PATTERN_WORD_CYCLE },
+ { .name = "const", .val = GENERATED_PACKET_PATTERN_CONST },
+ BCMCLI_ENUM_LAST
+ };
+
+ static bcmcli_parm_value gem_port_ids[MAX_GEM_PORTS_FOR_SEND];
+ static bcmcli_parm_value onu_ids[MAX_ONUS_FOR_SEND];
+
+ bcmcli_entry *dir;
+
+ if (bcmcli_dir_find(top_dir, "gpon"))
+ {
+ return BCM_ERR_OK;
+ }
+
+ gpon_utils_cli_dir_cli_destroy();
+
+ dir = bcmcli_dir_add(top_dir, "gpon", "Common GPON/XGPON functions", BCMCLI_ACCESS_ADMIN, NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+
+ BCMCLI_MAKE_CMD(
+ dir,
+ "build_cpu_packet",
+ "Build and save CPU packet",
+ build_cpu_packet_cb,
+ BCMCLI_MAKE_PARM_ENUM("calc_crc", "Auto-calculate CRC", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_RANGE(
+ "packet_size", "Full packet size", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE, 0, MAX_PACKET_SIZE),
+ BCMCLI_MAKE_PARM_ENUM("pattern", "Background pattern", generated_packet_pattern_table, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_RANGE("const_value", "Const value", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE, 0, 0xFF),
+ BCMCLI_MAKE_PARM("eth_da", "Ethernet destination address", BCMCLI_PARM_MAC, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("eth_sa", "Ethernet source address", BCMCLI_PARM_MAC, BCMCLI_PARM_FLAG_NONE));
+
+ BCMCLI_MAKE_CMD(
+ dir,
+ "build_omci_packet",
+ "Build and save OMCI packet",
+ build_omci_packet_cb,
+ BCMCLI_MAKE_PARM_ENUM("calc_crc", "Auto-calculate CRC", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_RANGE(
+ "packet_size", "Full packet size", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE, 0, MAX_PACKET_SIZE),
+ BCMCLI_MAKE_PARM_ENUM("pattern", "Background pattern", generated_packet_pattern_table, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_RANGE("const_value", "Const value", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE, 0, 0xFF));
+
+ BCMCLI_MAKE_CMD(
+ dir,
+ "send_to_gem",
+ "Send saved CPU/OMCI packet to GEM port(s)",
+ send_to_gem_cb,
+ BCMCLI_MAKE_PARM("device", "Device ID", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("pon_ni", "PON NI", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_ARRAY(
+ "gem",
+ "GEM port IDs, comma separated",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_NONE,
+ MAX_GEM_PORTS_FOR_SEND,
+ gem_port_ids),
+ BCMCLI_MAKE_PARM("count", "Number of packets (default 1)", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_OPTIONAL));
+
+ BCMCLI_MAKE_CMD(
+ dir,
+ "send_to_onu",
+ "Send saved CPU/OMCI packet to ONU(s) (OMCI GEM port)",
+ send_to_onu_cb,
+ BCMCLI_MAKE_PARM("device", "Device ID", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("pon_ni", "PON NI", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_ARRAY(
+ "onu",
+ "ONU IDs, comma separated",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_NONE,
+ MAX_ONUS_FOR_SEND,
+ onu_ids),
+ BCMCLI_MAKE_PARM("count", "Number of packets (default 1)", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_OPTIONAL));
+
+ BCMCLI_MAKE_CMD_NOPARM(
+ dir,
+ "dump_packet",
+ "Print saved CPU/OMCI packet data",
+ dump_packet_cb);
+
+ gpon_utils_cli_dir = dir;
+ gpon_utils_cli_top_dir = top_dir;
+ return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_gpon_utils.h b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_gpon_utils.h
new file mode 100644
index 0000000..b1380c9
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/bcmolt_user_appl_gpon_utils.h
@@ -0,0 +1,37 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_GPON_UTILS_H_
+#define _BCMOLT_USER_APPL_GPON_UTILS_H_
+
+#include <bcmolt_host_api.h>
+
+bcmos_errno bcmolt_user_appl_gpon_utils_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/Makefile b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/Makefile
new file mode 100644
index 0000000..8167e23
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/Makefile
@@ -0,0 +1,12 @@
+# DPoE Security
+
+ifeq ("$(ENABLE_CLI)", "y")
+
+ MOD_NAME = bcm_user_appl_dpoe_sec
+ MOD_TYPE = lib
+ MOD_DEPS = os host_api bcm_user_appl_epon_oam
+
+ srcs = bcmolt_user_appl_dpoe_sec.c dpoe_eap_tls.c dpoe_sec_fsm.c dpoe_sec_mka_fsm.c dpoe_sec_util.c mka.c
+
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/bcmolt_user_appl_dpoe_sec.c b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/bcmolt_user_appl_dpoe_sec.c
new file mode 100644
index 0000000..14bcb12
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/bcmolt_user_appl_dpoe_sec.c
@@ -0,0 +1,136 @@
+/*
+ <: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 "bcmolt_model_types.h"
+#include "bcmolt_epon_oam_types.h"
+#include "bcmolt_user_appl_dpoe_sec.h"
+#include "dpoe_sec_fsm.h"
+
+static bcmos_bool is_running = BCMOS_FALSE;
+
+static bcmos_bool cert_authority_trust_placeholder(dpoe_sec_link_rec* link_rec)
+{
+ BCM_LOG(INFO, dpoe_sec_log_id[link_rec->device], "This function should validate the certificate information stored in the link record and decide whether to accept this authentication.\n");
+ return BCMOS_TRUE;
+}
+
+static void fsm_complete_placeholder(bcmolt_devid device, bcmolt_epon_link_key link_key, bcmos_errno status)
+{
+ if (status == BCM_ERR_OK)
+ {
+ BCM_LOG(INFO, dpoe_sec_log_id[device], "The DPoE Security FSM completed successfully. This link can now be configured for service.\n");
+ }
+ else
+ {
+ BCM_LOG(INFO, dpoe_sec_log_id[device], "The DPoE Security FSM failed due to %s (%d). Appropriate action should be taken to correct this error.\n", bcmos_strerror(status), status);
+ }
+}
+
+#ifdef ENABLE_CLI
+static bcmos_errno dpoe_sec_cli_auth(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmolt_epon_link_key link_key;
+ dpoe_sec_fsm_start_data cfg;
+
+ link_key.epon_ni = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "pon")->value.unumber;
+ link_key.mac_address = bcmcli_find_named_parm(session, "onu")->value.mac;
+ cfg.enc_mode = (bcmolt_epon_oam_dpoe_encryption_mode)bcmcli_find_named_parm(session, "mode")->value.enum_val;
+ cfg.auth = BCMOS_TRUE;
+
+ return dpoe_sec_fsm_link_start(current_device, link_key, &cfg);
+}
+#endif
+
+void bcmolt_user_appl_dpoe_sec_cli_init(bcmcli_entry *top_dir)
+{
+#ifdef ENABLE_CLI
+ static const char *dir_name = "dpoe_sec";
+
+ if (bcmcli_dir_find(top_dir, dir_name))
+ {
+ return;
+ }
+
+ bcmcli_entry *dpoe_sec_dir = bcmcli_dir_add(top_dir, dir_name, "DPoE Security", BCMCLI_ACCESS_ADMIN, NULL);
+ BUG_ON(NULL == dpoe_sec_dir);
+
+ static bcmcli_enum_val enc_mode_table[] =
+ {
+ {"none", (long)BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_NONE},
+ {"1down", (long)BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_ONE_DOWN},
+ {"10down", (long)BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_DOWN},
+ {"10bi", (long)BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI},
+ BCMCLI_ENUM_LAST
+ };
+
+ BCMCLI_MAKE_CMD(dpoe_sec_dir, "auth", "Authenticate ONU", dpoe_sec_cli_auth,
+ BCMCLI_MAKE_PARM("pon", "PON NI", BCMCLI_PARM_UDECIMAL, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("onu", "ONU MAC", BCMCLI_PARM_MAC, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_ENUM("mode", "Encryption mode", enc_mode_table, BCMCLI_PARM_FLAG_NONE));
+#endif
+}
+
+void bcmolt_user_appl_dpoe_sec_process_proxy_rx(bcmolt_devid device_id, bcmolt_proxy_rx *proxy_rx)
+{
+ const bcmolt_epon_link_frame_captured *cap = (bcmolt_epon_link_frame_captured*)proxy_rx;
+
+ if ((BCMOLT_OBJ_ID_EPON_LINK == proxy_rx->hdr.obj_type) &&
+ (BCMOLT_EPON_LINK_PROXY_RX_ID_FRAME_CAPTURED == proxy_rx->hdr.subgroup))
+ {
+ if ((cap->data.frame.val[12] == 0x88) &&
+ (cap->data.frame.val[13] == 0x09) &&
+ (cap->data.frame.val[14] == 0x03))
+ {
+ uint8_t *frame_copy;
+
+ frame_copy = bcmos_alloc(cap->data.frame.len);
+ memcpy(frame_copy, cap->data.frame.val, cap->data.frame.len);
+
+ dpoe_sec_fsm_link_rx_oam(device_id, cap->key, frame_copy, cap->data.frame.len);
+ }
+ }
+ else
+ {
+ return; /* not a frame captured on a link - ignore */
+ }
+}
+
+void bcmolt_user_appl_dpoe_sec_init(void)
+{
+ if (is_running)
+ {
+ return;
+ }
+
+ dpoe_sec_fsm_init(cert_authority_trust_placeholder, fsm_complete_placeholder);
+
+ is_running = BCMOS_TRUE;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/bcmolt_user_appl_dpoe_sec.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/bcmolt_user_appl_dpoe_sec.h
new file mode 100644
index 0000000..6872f8c
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/bcmolt_user_appl_dpoe_sec.h
@@ -0,0 +1,43 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_DPOE_SEC_H_
+#define _BCMOLT_USER_APPL_DPOE_SEC_H_
+
+#include "bcmcli.h"
+
+void bcmolt_user_appl_dpoe_sec_process_proxy_rx(bcmolt_devid device_id, bcmolt_proxy_rx *proxy_rx);
+
+void bcmolt_user_appl_dpoe_sec_cli_init(bcmcli_entry *top_dir);
+
+void bcmolt_user_appl_dpoe_sec_init(void);
+
+#endif /* _BCMOLT_USER_APPL_DPOE_SEC_H_ */
+
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_eap_tls.c b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_eap_tls.c
new file mode 100644
index 0000000..22ea80a
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_eap_tls.c
@@ -0,0 +1,1483 @@
+/*
+ <: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 "dpoe_eap_tls.h"
+#include "bcmolt_buf.h"
+#include "dpoe_sec_fsm.h"
+
+/* The destination multicast MAC address for EAPOL packets. */
+static const bcmos_mac_address eapol_dst_mac = { { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 } };
+
+/* The length of the RSA key used in the server_key_exchange TLS message sent to the ONU during DPoE bi-directional
+ authentication. */
+#define DPOE_BI_RSA_KEY_SIZE 2048
+
+/* 2 1200 byte certs + some overhead.... */
+#define EAPOL_PKT_SIZE 5000
+
+typedef enum
+{
+ EAP_CODE_REQUEST = 1,
+ EAP_CODE_RESPONSE,
+ EAP_CODE_SUCCESS,
+ EAP_CODE_FAILURE
+} eap_code;
+
+typedef struct
+{
+ uint8_t eap_code;
+ uint8_t id;
+ uint16_t length;
+} eap_frame;
+
+#define EAP_FRAME_SIZE 4
+
+typedef enum
+{
+ EAP_TYPE_IDENTITY = 1,
+ EAP_TYPE_NOTIFICATION,
+ EAP_TYPE_NAK,
+ EAP_TYPE_MD5,
+ EAP_TYPE_OTP,
+ EAP_TYPE_GENERIC_TOKEN_CARD,
+ EAP_TYPE_TLS = 13
+} eap_type;
+
+#define TLS_MAJOR_VERSION 3
+#define TLS_MINOR_VERSION 2
+
+/* First the EAP structures.... */
+typedef struct
+{
+ uint8_t auth_sub_type;
+ uint8_t eap_tls_flags;
+} eap_tls_hdr;
+
+#define EAP_TLS_HDR_SIZE 2
+
+#define EAP_HDR_SIZE (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE)
+
+typedef enum
+{
+ TLS_CHANGE_CIPHER_SPEC_ID = 20,
+ TLS_ALERT_ID = 21,
+ TLS_HANDSHAKE_ID = 22,
+ TLS_APPLICATION_DATA_ID = 23,
+} tls_content_type;
+
+typedef struct
+{
+ uint8_t content_type;
+ tls_protocol_version tls_version;
+ uint16_t tls_record_length;
+} tls_record_hdr;
+
+#define TLS_RECORD_HDR_SIZE (3 + PROTOCOL_VERSION_SIZE)
+
+#define EAP_TLS_FLAG_LENGTH_OFFSET 7
+#define EAP_TLS_FLAG_MORE_OFFSET 6
+#define EAP_TLS_FLAG_START_OFFSET 5
+#define EAP_TLS_FLAG_LENGTH_BIT (1U << EAP_TLS_FLAG_LENGTH_OFFSET)
+#define EAP_TLS_FLAG_MORE_BIT (1U << EAP_TLS_FLAG_MORE_OFFSET)
+#define EAP_TLS_FLAG_START_BIT (1U << EAP_TLS_FLAG_START_OFFSET)
+
+typedef struct
+{
+ eap_frame hdr;
+ eap_tls_hdr tls;
+} eap_msg_buf;
+
+typedef struct
+{
+ eapol_msg_hdr hdr;
+ eap_msg_buf eap_msg;
+} eapol_msg;
+
+/* Now the TLS records. */
+typedef struct tls_session_id
+{
+ uint8_t length;
+ uint8_t session_id[SIZE_OF_TLS_SESSION_ID];
+} tls_session_id;
+
+typedef enum
+{
+ cm_null = 0
+} compression_method;
+
+typedef struct
+{
+ uint8_t length;
+ uint8_t compression_method;
+} compression_methods;
+
+typedef enum
+{
+ RSA_SIGN = 1,
+ DSS_SIGN = 2,
+ RSA_FIXED_DH = 3,
+ DSS_FIXED_DH = 4,
+ RSA_EPHEMERAL_DH_RESERVED = 5,
+ DSS_EPHEMERAL_DH_RESERVED = 6,
+ FORTEZZA_DMS_RESERVED = 20,
+} client_certificate_type;
+
+/* For 2048 bit RSA key */
+#define SIZE_OF_RSA_MODULUS 256
+#define SIZE_OF_RSA_EXPONENT 3
+
+typedef enum
+{
+ HS_HELLO_REQUEST = 0,
+ HS_CLIENT_HELLO = 1,
+ HS_SERVER_HELLO = 2,
+ HS_CERTIFICATE = 11,
+ HS_SERVER_KEY_EXCHANGE = 12,
+ HS_CERTIFICATE_REQUEST = 13,
+ HS_SERVER_HELLO_DONE = 14,
+ HS_CERTIFICATE_VERIFY = 15,
+ HS_CLIENT_KEY_EXCHANGE = 16,
+ HS_FINISHED = 20,
+} handshake_type;
+
+typedef struct
+{
+ uint8_t msg_id;
+ uint32_t msg_length; /* actually U24 */
+} tls_handshake;
+
+#define TLS_HANDSHAKE_SIZE 4
+
+typedef struct
+{
+ uint8_t *eapol;
+ uint8_t *eap;
+ uint8_t *eap_tls;
+ uint8_t *tls_rec;
+} eap_length_fields;
+
+static f_dpoe_sec_cert_trust_cb _cert_trust;
+static f_dpoe_sec_auth_cb _auth_complete;
+
+static void _buffer_hash_data(dpoe_sec_link_rec *link_rec, const uint8_t *buf, uint32_t len)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ dpoe_sec_sha1_update(&link_rec->auth_ctrl.trans_data.sha1_hash, buf, len);
+ dpoe_sec_md5_update(&link_rec->auth_ctrl.trans_data.md5_hash, buf, len);
+}
+
+static void _dpoe_eap_tls_compute_master_session_key(auth_trans_data *trans_data)
+{
+ uint8_t seed[(2* COUNT_OF_RANDOM_BYTES)];
+ uint32_t seed_len = 0;
+
+ /* Parameter checks. */
+ BUG_ON(trans_data == NULL);
+
+ /* The seed to the PRF is the client random value concatenated with the server random value. */
+ memcpy(&seed[seed_len], trans_data->client_random, COUNT_OF_RANDOM_BYTES);
+ seed_len += COUNT_OF_RANDOM_BYTES;
+ memcpy(&seed[seed_len], trans_data->server_random, COUNT_OF_RANDOM_BYTES);
+ seed_len += COUNT_OF_RANDOM_BYTES;
+
+ dpoe_sec_prf_expand_4346(
+ trans_data->pre_master_secret,
+ sizeof(trans_data->pre_master_secret),
+ (const uint8_t *)"master secret",
+ strlen("master secret"),
+ seed,
+ seed_len,
+ trans_data->master_secret,
+ sizeof(trans_data->master_secret));
+ dpoe_sec_prf_expand_4346(
+ trans_data->master_secret,
+ sizeof(trans_data->master_secret),
+ (const uint8_t *)"client EAP encryption",
+ strlen("client EAP encryption"),
+ seed,
+ seed_len,
+ trans_data->key_material,
+ sizeof(trans_data->key_material));
+
+ memcpy(trans_data->master_session_key, trans_data->key_material, sizeof(trans_data->master_session_key));
+}
+
+static bcmos_bool _dpoe_eap_tls_prepare_security_data(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ /* If bidirectional, compute the master session key and generate the RSA public/private key pair */
+ if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI)
+ {
+ _dpoe_eap_tls_compute_master_session_key(&link_rec->auth_ctrl.trans_data);
+ }
+
+ return BCMOS_TRUE;
+}
+
+static bcmos_bool _dpoe_eap_tls_skip_length(bcmolt_buf *buf, uint32_t skip, uint8_t** len)
+{
+ *len = bcmolt_buf_snap_get(buf);
+ return bcmolt_buf_skip(buf, skip);
+}
+
+static bcmos_bool _dpoe_eap_tls_init_eap_hdr(
+ dpoe_sec_link_rec *link_rec,
+ bcmolt_buf *buf,
+ eap_code code,
+ eap_length_fields *lf)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ return
+ bcmolt_buf_write_mac_address(buf, eapol_dst_mac) &&
+ bcmolt_buf_write_mac_address(buf, link_rec->ni_mac) &&
+ bcmolt_buf_write_u16_be(buf, ETHERTYPE_EAPOL) &&
+ bcmolt_buf_write_u8(buf, EAPOL_PROTOCOL_VERSION_DPOE) &&
+ bcmolt_buf_write_u8(buf, EAPOL_TYPE_PACKET) &&
+ /* Eapol Length - we re-compute this after building the packet */
+ _dpoe_eap_tls_skip_length(buf, sizeof(uint16_t), &lf->eapol) &&
+ /* EAP layer headers */
+ bcmolt_buf_write_u8(buf, code) &&
+ bcmolt_buf_write_u8(buf, link_rec->auth_ctrl.current_packet_id++) &&
+ /* EAP length - again, computed when the packet is packed! */
+ _dpoe_eap_tls_skip_length(buf, sizeof(uint16_t), &lf->eap);
+}
+
+static bcmos_bool _dpoe_eap_tls_init_tls_hdr(bcmolt_buf *buf, uint8_t eapTlsFlags, eap_length_fields *lf)
+{
+ bcmos_bool rc = BCMOS_FALSE;
+
+ /* Parameter checks. */
+ BUG_ON(buf == NULL);
+
+ if (bcmolt_buf_write_u8(buf, EAP_TYPE_TLS) &&
+ bcmolt_buf_write_u8(buf, eapTlsFlags))
+ {
+ if ((eapTlsFlags & EAP_TLS_FLAG_LENGTH_BIT) == EAP_TLS_FLAG_LENGTH_BIT)
+ {
+ /* EAP-TLS length - computed at pack completion only allocate room for this if EapTlsFlagLengthBit is set
+ But if the EapTlsFlagLengthBit is set, we also write the TLS Record header. */
+ if (_dpoe_eap_tls_skip_length(buf, sizeof(uint32_t), &lf->eap_tls) &&
+ bcmolt_buf_write_u8(buf, TLS_HANDSHAKE_ID) &&
+ bcmolt_buf_write_u8(buf, TLS_MAJOR_VERSION) &&
+ bcmolt_buf_write_u8(buf, TLS_MINOR_VERSION) &&
+ /* Another length field, the TLS record length */
+ _dpoe_eap_tls_skip_length(buf, sizeof(uint16_t), &lf->tls_rec))
+ {
+ rc = BCMOS_TRUE;
+ }
+ }
+ else
+ {
+ rc = BCMOS_TRUE;
+ }
+ }
+
+ return rc;
+}
+
+static void _dpoe_eap_tls_pack_handshake_length(
+ dpoe_sec_link_rec *link_rec,
+ bcmolt_buf *buf,
+ uint8_t *start,
+ uint8_t *len)
+{
+ uint32_t rec_len;
+ uint8_t *end;
+
+ /* Now go back and fill in the length field. This is the length of the TlsHandshake record */
+ end = bcmolt_buf_snap_get(buf);
+ rec_len = end - start;
+ bcmolt_buf_snap_restore(buf, len);
+ bcmolt_buf_write_u24(buf, uint32_to_24(rec_len - TLS_HANDSHAKE_SIZE));
+ bcmolt_buf_snap_restore(buf, end);
+ _buffer_hash_data(link_rec, start, rec_len);
+}
+
+static bcmos_bool _dpoe_eap_tls_pack_server_hello_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ uint8_t *start;
+ uint8_t *len;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ start = bcmolt_buf_snap_get(buf);
+ if (bcmolt_buf_write_u8(buf, HS_SERVER_HELLO) &&
+ (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))) &&
+ bcmolt_buf_write_u8(buf, link_rec->auth_ctrl.version.major) &&
+ bcmolt_buf_write_u8(buf, link_rec->auth_ctrl.version.minor) &&
+ bcmolt_buf_write(buf, link_rec->auth_ctrl.trans_data.server_random, COUNT_OF_RANDOM_BYTES) &&
+ bcmolt_buf_write_u8(buf, COUNT_OF_RANDOM_BYTES) &&
+ bcmolt_buf_write(buf, link_rec->auth_ctrl.trans_data.server_random, COUNT_OF_RANDOM_BYTES) &&
+
+ /* Cipher Suites - we write 0 as the length of the Cipher Suites
+ Short circuit if using pre-release authentication */
+ ((link_rec->auth_ctrl.version.minor == 3) || bcmolt_buf_write_u16_be(buf, 0)) &&
+
+ /* CompressionMethods also write 0 as the length */
+ bcmolt_buf_write_u8(buf, 0))
+ {
+ _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len);
+ return BCMOS_TRUE;
+ }
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_pack_server_key_exchange_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ uint8_t *start;
+ uint8_t *len;
+ uint8_t rsa_modulus[SIZE_OF_RSA_MODULUS] = {};
+ uint8_t rsa_exponent[SIZE_OF_RSA_EXPONENT] = {};
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ /* If not bidirectional, just return BCMOS_TRUE */
+ if (link_rec->cfg.enc_mode != BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI)
+ {
+ return BCMOS_TRUE;
+ }
+
+ /* Verify that the length of the public key (modulus) of the private key matches the expected length. Also, verify
+ that the modulus is successfully read from the RSA key. */
+ start = bcmolt_buf_snap_get(buf);
+ if ((dpoe_sec_rsa_public_get(link_rec->auth_ctrl.trans_data.rsa, rsa_modulus, rsa_exponent)) &&
+ bcmolt_buf_write_u8(buf, HS_SERVER_KEY_EXCHANGE) &&
+ (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))) &&
+ bcmolt_buf_write(buf, rsa_modulus, sizeof(rsa_modulus)) &&
+ bcmolt_buf_write(buf, rsa_exponent, sizeof(rsa_exponent)))
+ {
+ _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len);
+ return BCMOS_TRUE;
+ }
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_pack_cert_req_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ uint8_t *start;
+ uint8_t *len;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ start = bcmolt_buf_snap_get(buf);
+ if (bcmolt_buf_write_u8(buf, HS_CERTIFICATE_REQUEST) &&
+ (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))) &&
+ bcmolt_buf_write_u8(buf, 1) && /* length of 'Certificate Type' */
+ bcmolt_buf_write_u8(buf, RSA_SIGN) && /* Certificate Type */
+ bcmolt_buf_write_u16_be(buf, 0)) /* zero length for cert authorities */
+ {
+ _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len);
+ return BCMOS_TRUE;
+ }
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_pack_server_hello_done_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ uint8_t *start;
+ uint8_t *len;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ start = bcmolt_buf_snap_get(buf);
+ if (bcmolt_buf_write_u8(buf, HS_SERVER_HELLO_DONE) &&
+ (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))))
+ {
+ _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len);
+ return BCMOS_TRUE;
+ }
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_pack_finished_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ uint8_t *start;
+ uint8_t *len;
+
+ start = bcmolt_buf_snap_get(buf);
+ if (bcmolt_buf_write_u8(buf, HS_FINISHED) &&
+ (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))))
+ {
+ _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len);
+ return BCMOS_TRUE;
+ }
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_pack_msg_lengths(bcmolt_buf *buf, uint8_t eap_tls_flags, eap_length_fields *lf)
+{
+ uint16_t eapol_length;
+ uint32_t eap_tls_length;
+ uint8_t *snap = bcmolt_buf_snap_get(buf);
+
+ /* Parameter checks. */
+ BUG_ON(buf == NULL);
+ BUG_ON(lf == NULL);
+
+ eapol_length = bcmolt_buf_get_used(buf) - EAPOL_MSG_HDR_SIZE;
+
+ /* Seems odd that these two would be the same. But eapolLength is defined as the length of the body fields of the
+ Eapol buffer, which does not include the Eapol header. EAP is different, the EAP length is define to include the
+ entire EAP packet, including the header. */
+ bcmolt_buf_snap_restore(buf, lf->eapol);
+ if (!bcmolt_buf_write_u16_be(buf, eapol_length))
+ {
+ bcmolt_buf_snap_restore(buf, snap);
+ return BCMOS_FALSE;
+ }
+
+ bcmolt_buf_snap_restore(buf, lf->eap);
+ if (!bcmolt_buf_write_u16_be(buf, eapol_length))
+ {
+ bcmolt_buf_snap_restore(buf, snap);
+ return BCMOS_FALSE;
+ }
+
+ if ((eap_tls_flags & EAP_TLS_FLAG_LENGTH_BIT) == EAP_TLS_FLAG_LENGTH_BIT)
+ {
+ /* The TLS message length - take away the EapTlsHdr, AND the EapMsgHdr which, surprisingly was not removed in
+ the previous step. */
+ eap_tls_length = eapol_length - (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE + sizeof(uint32_t));
+
+ bcmolt_buf_snap_restore(buf, lf->eap_tls);
+ if (!bcmolt_buf_write_u32_be(buf, eap_tls_length))
+ {
+ bcmolt_buf_snap_restore(buf, snap);
+ return BCMOS_FALSE;
+ }
+
+ bcmolt_buf_snap_restore(buf, lf->tls_rec);
+ if (!bcmolt_buf_write_u16_be(buf, (uint16_t)eap_tls_length - TLS_RECORD_HDR_SIZE))
+ {
+ bcmolt_buf_snap_restore(buf, snap);
+ return BCMOS_FALSE;
+ }
+ }
+
+ bcmolt_buf_snap_restore(buf, snap);
+ return BCMOS_TRUE;
+}
+
+static bcmos_errno _dpoe_eap_tls_pack_start(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ bcmos_errno rc = BCM_ERR_NOMEM;
+ uint8_t flags = EAP_TLS_FLAG_START_BIT;
+ eap_length_fields lf;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ if (_dpoe_eap_tls_init_eap_hdr(link_rec, buf, EAP_CODE_REQUEST, &lf) &&
+ _dpoe_eap_tls_init_tls_hdr(buf, flags, &lf) &&
+ _dpoe_eap_tls_pack_msg_lengths(buf, flags, &lf))
+ {
+ rc = BCM_ERR_OK;
+ }
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_send_cert_request(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_bool rc = BCMOS_FALSE;
+ bcmos_errno err;
+ bcmolt_buf buf;
+ uint8_t *msg = NULL;
+ eap_length_fields lf;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ msg = bcmos_calloc(EAPOL_PKT_SIZE);
+ if (msg == NULL)
+ {
+ return BCMOS_FALSE;
+ }
+
+ /* Initialize the maximum supported length EAP-TLS message buffer. The various TLS records contained in an EAP-TLS
+ message are variable size. However, this can only be a combined maximum length. */
+ bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED);
+ if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, EAP_CODE_REQUEST, &lf) &&
+ _dpoe_eap_tls_init_tls_hdr(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf))
+ {
+ /* The Cert request contains 3 or 4 TLS records (3 for DS-only and 4 for bi-directional). The TLS records are
+ ordered as:
+ - Server Hello
+ - Server Key Exchange (if bi-directional)
+ - Certificate Request
+ - Server Hello Done */
+ if (_dpoe_eap_tls_pack_server_hello_hs(link_rec, &buf) &&
+ _dpoe_eap_tls_pack_server_key_exchange_hs(link_rec, &buf) &&
+ _dpoe_eap_tls_pack_cert_req_hs(link_rec, &buf) &&
+ _dpoe_eap_tls_pack_server_hello_done_hs(link_rec, &buf))
+ {
+ _dpoe_eap_tls_pack_msg_lengths(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf);
+
+ /* Send the message */
+ err = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf));
+ if (err != BCM_ERR_OK)
+ {
+ bcmos_free(msg);
+ return BCMOS_FALSE;
+ }
+
+ /* Authentication has begun. */
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_EAP_START;
+ rc = BCMOS_TRUE;
+ }
+ }
+
+ /* Free the message. */
+ bcmos_free(msg);
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_eap_tls_send_finished_hs(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OVERFLOW;
+ bcmolt_buf buf;
+ uint8_t *msg = NULL;
+ eap_length_fields lf;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ msg = bcmos_calloc(EAPOL_PKT_SIZE);
+ if (msg == NULL)
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ /* Initialize the EAP-TLS message buffer. */
+ bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED);
+ if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, EAP_CODE_REQUEST, &lf) &&
+ _dpoe_eap_tls_init_tls_hdr(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf) &&
+ _dpoe_eap_tls_pack_finished_hs(link_rec, &buf))
+ {
+ _dpoe_eap_tls_pack_msg_lengths(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf);
+ /* No error detection here. We have already authenticated. this message is just for standards compliance */
+ rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf));
+ }
+
+ /* Free the message. */
+ bcmos_free(msg);
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_eap_tls_send_result(dpoe_sec_link_rec *link_rec, eap_code result)
+{
+ uint8_t *msg;
+ bcmolt_buf buf;
+ bcmos_errno rc = BCM_ERR_INTERNAL;
+ eap_length_fields lf;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+
+ msg = bcmos_calloc(EAPOL_PKT_SIZE);
+ if (msg == NULL)
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ /* Initialize the EAP-TLS message buffer. */
+ bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED);
+ /* Encode the EAP-Success message to the buffer. */
+ if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, result, &lf) &&
+ _dpoe_eap_tls_pack_msg_lengths(&buf, 0, &lf))
+ {
+ rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf));
+ }
+
+ bcmos_free(msg);
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_parse_certificate(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ bcmos_bool rc = BCMOS_FALSE;
+ uint24_t length;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ link_rec->auth_ctrl.certificate = bcmos_calloc(EAPOL_PKT_SIZE);
+ if (link_rec->auth_ctrl.certificate == NULL)
+ {
+ return BCMOS_FALSE;
+ }
+
+ if (bcmolt_buf_read_u24(buf, &length) &&
+ ((link_rec->auth_ctrl.certLen = (uint16_t)uint24_to_32(length)), (link_rec->auth_ctrl.certLen <= EAPOL_PKT_SIZE)) &&
+ bcmolt_buf_read(buf, link_rec->auth_ctrl.certificate, link_rec->auth_ctrl.certLen))
+ {
+ rc = BCMOS_TRUE;
+ }
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_parse_client_key_exchange(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ bcmos_bool rc = BCMOS_FALSE;
+ uint16_t length;
+ uint8_t encrypted_buf[SIZE_OF_RSA_ENCRYPTED_BLOCK] = {};
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ if (bcmolt_buf_read_u16_be(buf, &length) &&
+ (length == (SIZE_OF_RSA_ENCRYPTED_BLOCK)) &&
+ bcmolt_buf_read(buf, encrypted_buf, sizeof(encrypted_buf)))
+ {
+ int val;
+
+ val = dpoe_sec_rsa_private_decrypt(
+ sizeof(encrypted_buf),
+ encrypted_buf,
+ link_rec->auth_ctrl.trans_data.pre_master_secret,
+ link_rec->auth_ctrl.trans_data.rsa);
+
+ if (val == SIZE_OF_PRE_MASTER_SECRET)
+ {
+ /* We now need the security data - we have the "client random", "server random", and "pre master secret". */
+ _dpoe_eap_tls_prepare_security_data(link_rec);
+ rc = BCMOS_TRUE;
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "RSA decrypt pre-master secret failed: %d\n", val);
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to parse client key exchange: %u\n", length);
+ }
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_parse_cert_verify(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ uint16_t length;
+ bcmos_bool ret = BCMOS_FALSE;
+
+ if (bcmolt_buf_read_u16_be(buf, &length) &&
+ (length <= (2 * SIZE_OF_RSA_ENCRYPTED_BLOCK)))
+ {
+ bcmolt_buf cert_buf;
+ uint24_t temp;
+ const uint8_t *cert;
+ uint32_t cert_len = 0;
+ uint8_t cert_verify[sizeof(dpoe_sec_sha1_digest) + sizeof(dpoe_sec_md5_digest)];
+ uint8_t *enc_buf;
+ dpoe_sec_rsa_key *rsa;
+
+ bcmolt_buf_init(&cert_buf, link_rec->auth_ctrl.certLen, link_rec->auth_ctrl.certificate, BCMOLT_BUF_ENDIAN_FIXED);
+
+ /* Get the pointer to the ONU cert and its length */
+ bcmolt_buf_read_u24(&cert_buf, &temp);
+ cert_len = uint24_to_32(temp);
+ cert = bcmolt_buf_snap_get(&cert_buf);
+
+ enc_buf = bcmolt_buf_snap_get(buf);
+ bcmolt_buf_skip(buf, length);
+
+ /* Get the public key from the ONU cert. */
+ rsa = dpoe_sec_x509_pub_key_get(cert, cert_len);
+
+ /* decrypt the remainder of the buf with the ONU cert. */
+ if (rsa != NULL)
+ {
+ if (dpoe_sec_rsa_public_decrypt(length, enc_buf, cert_verify, rsa) >= 0)
+ {
+ /* RSA_size() returns the size of the modulus in bytes. Convert to bits. */
+ link_rec->auth_ctrl.onu_cert_key_size = dpoe_sec_rsa_size(rsa) * 8;
+
+ /* Compare the output (should be 36 bytes) to sha1/md5 messages digests If they match, we are good */
+ if ((memcmp(&cert_verify[0],
+ link_rec->auth_ctrl.trans_data.md5_digest,
+ sizeof(dpoe_sec_md5_digest)) == 0) &&
+ (memcmp(&cert_verify[sizeof(dpoe_sec_md5_digest)],
+ link_rec->auth_ctrl.trans_data.sha1_digest,
+ sizeof(dpoe_sec_sha1_digest)) == 0))
+ {
+ bcmolt_buf_init(&cert_buf, link_rec->auth_ctrl.certLen, link_rec->auth_ctrl.certificate, BCMOLT_BUF_ENDIAN_FIXED);
+
+ /* Get the pointer to the ONU certificate. */
+ bcmolt_buf_read_u24(&cert_buf, &temp);
+ link_rec->auth_ctrl.onu_cert_len = uint24_to_32(temp);
+ link_rec->auth_ctrl.onu_cert = bcmolt_buf_snap_get(&cert_buf);
+
+ bcmolt_buf_skip(&cert_buf, link_rec->auth_ctrl.onu_cert_len);
+
+ /* Get the pointer to the Manufacturer CA certificate. */
+ bcmolt_buf_read_u24(&cert_buf, &temp);
+ link_rec->auth_ctrl.mfg_cert_len = uint24_to_32(temp);
+ link_rec->auth_ctrl.mfg_cert = bcmolt_buf_snap_get(&cert_buf);
+
+ ret = BCMOS_TRUE;
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Cert Verify failed\n");
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "RSA public decrypt/verify failed\n");
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to retrieve RSA key\n");
+ }
+
+ dpoe_sec_rsa_key_free(rsa);
+ }
+
+ return ret;
+}
+
+static bcmos_bool _dpoe_eap_tls_parse_verify_data(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_bool rc = BCMOS_FALSE;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION)
+ {
+ /* This version of authentication does not parse verify data */
+ rc = BCMOS_TRUE;
+ }
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_parse_client_hello(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ tls_protocol_version version;
+ tls_session_id session_id;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ if (bcmolt_buf_read_u8(buf, &version.major) &&
+ (version.major == TLS_MAJOR_VERSION) &&
+ bcmolt_buf_read_u8(buf, &version.minor) &&
+ (version.minor >= TLS_MINOR_VERSION) &&
+ bcmolt_buf_read(buf, link_rec->auth_ctrl.trans_data.client_random, COUNT_OF_RANDOM_BYTES) &&
+ bcmolt_buf_read_u8(buf, &session_id.length) &&
+ (session_id.length <= sizeof(session_id.session_id)) &&
+ bcmolt_buf_read(buf, session_id.session_id, session_id.length))
+ {
+ compression_methods comp_methods;
+ uint16_t tlvLen;
+ uint8_t cipher_suites[2];
+ uint8_t *cm_snap = bcmolt_buf_snap_get(buf);
+
+ memset(&comp_methods, 0, sizeof(comp_methods));
+
+ /* Cipher Suites may be present -if so parse over it and it will be ignored. Still check that comp methods is
+ NULL */
+ if (bcmolt_buf_read_u16_be(buf, &tlvLen) && /* Cipher Suites */
+ (tlvLen <= sizeof(uint16_t)) &&
+ bcmolt_buf_read(buf, cipher_suites, tlvLen) &&
+ bcmolt_buf_read_u8(buf, &comp_methods.length) && /* Compression Methods */
+ (comp_methods.length == sizeof(comp_methods.compression_method)) &&
+ bcmolt_buf_read(buf, &comp_methods.compression_method, comp_methods.length) &&
+ (comp_methods.compression_method == cm_null) )
+ {
+ link_rec->auth_ctrl.version.major = version.major;
+ link_rec->auth_ctrl.version.minor = version.minor;
+ return BCMOS_TRUE;
+ }
+
+ /* No cipher suites, check that comp method is null */
+ memset(&comp_methods, 0, sizeof(comp_methods));
+ bcmolt_buf_snap_restore(buf, cm_snap);
+ if (bcmolt_buf_read_u8(buf, &comp_methods.length) && /* Compression Methods */
+ (comp_methods.length == sizeof(comp_methods.compression_method)) &&
+ bcmolt_buf_read(buf, &comp_methods.compression_method, comp_methods.length) &&
+ (comp_methods.compression_method == cm_null) )
+ {
+ link_rec->auth_ctrl.version.major = version.major;
+ link_rec->auth_ctrl.version.minor = version.minor;
+ return BCMOS_TRUE;
+ }
+
+ /* Ok we failed to decode what was sent as cipher suites and comp methods. Do we care? Absolutely Not! */
+ link_rec->auth_ctrl.version.major = version.major;
+ link_rec->auth_ctrl.version.minor = version.minor;
+ return BCMOS_TRUE;
+ }
+
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_handle_client_hello_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ if (_dpoe_eap_tls_parse_client_hello(link_rec, buf))
+ {
+ /* We now have the data, build the EAP session ID (see RFC 5216) */
+ link_rec->auth_ctrl.trans_data.session_id[0] = 0xd;
+ memcpy(
+ &link_rec->auth_ctrl.trans_data.session_id[1],
+ link_rec->auth_ctrl.trans_data.client_random,
+ COUNT_OF_RANDOM_BYTES);
+ memcpy(
+ &link_rec->auth_ctrl.trans_data.session_id[COUNT_OF_RANDOM_BYTES + 1],
+ link_rec->auth_ctrl.trans_data.server_random,
+ COUNT_OF_RANDOM_BYTES);
+
+ if (_dpoe_eap_tls_send_cert_request(link_rec))
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CERT_REQUEST;
+ return BCMOS_TRUE;
+ }
+ }
+
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_handle_certificate_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ if (_dpoe_eap_tls_parse_certificate(link_rec, buf))
+ {
+ if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION)
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CERT_RECEIVED;
+ return BCMOS_TRUE;
+ }
+ }
+ return BCMOS_FALSE;
+}
+
+/******************************************************************************/
+static bcmos_bool _dpoe_eap_tls_handle_client_key_exchange_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ if (_dpoe_eap_tls_parse_client_key_exchange(link_rec, buf))
+ {
+ if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION)
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CLIENT_KEY_RECEIVED;
+ return BCMOS_TRUE;
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Wrong TLS version in client key exchange: %u\n",
+ link_rec->auth_ctrl.version.minor);
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to parse client key exchange\n");
+ }
+
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool _dpoe_eap_tls_handle_cert_verify_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ bcmos_bool success;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+
+ success = _dpoe_eap_tls_parse_cert_verify(link_rec, buf);
+
+ if (success)
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CERT_VALIDATED;
+ }
+ else
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_FAILED;
+ if (_auth_complete != NULL)
+ {
+ _auth_complete(link_rec, BCM_ERR_ONU_ERR_RESP);
+ }
+ }
+
+ return success;
+}
+
+static bcmos_bool _dpoe_eap_tls_handle_finished_hs(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_bool success;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ success = _dpoe_eap_tls_parse_verify_data(link_rec);
+
+ /* And send the final Finished Hs to the ONU */
+ if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION)
+ {
+ _dpoe_eap_tls_send_finished_hs(link_rec);
+ }
+
+ if (success)
+ {
+ if ((_cert_trust == NULL) || (_cert_trust(link_rec)))
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_AUTHENTICATED;
+ _dpoe_eap_tls_send_result(link_rec, EAP_CODE_SUCCESS);
+ if (_auth_complete != NULL)
+ {
+ _auth_complete(link_rec, BCM_ERR_OK);
+ }
+ }
+ else
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_FAILED;
+ _dpoe_eap_tls_send_result(link_rec, EAP_CODE_FAILURE);
+ success = BCMOS_FALSE;
+ if (_auth_complete != NULL)
+ {
+ _auth_complete(link_rec, BCM_ERR_INTERNAL);
+ }
+ }
+ }
+ else
+ {
+ /* Reset ALL the state variables! */
+ memset(&link_rec->auth_ctrl, 0, sizeof(link_rec->auth_ctrl));
+ }
+
+ return success;
+}
+
+static bcmos_bool _dpoe_eap_tls_alloc_tls_frag_buffer(onu_auth_control *auth_ctrl)
+{
+ /* Parameter checks. */
+ BUG_ON(auth_ctrl == NULL);
+
+ if (auth_ctrl->tls_frag_buffer == NULL)
+ {
+ auth_ctrl->tls_frag_buffer = bcmos_calloc(EAPOL_PKT_SIZE);
+ auth_ctrl->tls_frag_length = 0;
+ auth_ctrl->tls_total_length = 0;
+ }
+
+ if (auth_ctrl->tls_frag_buffer == NULL)
+ {
+ return BCMOS_FALSE;
+ }
+
+ return BCMOS_TRUE;
+}
+
+static void _dpoe_eap_tls_free_tls_frag_buffer(onu_auth_control *auth_ctrl)
+{
+ /* Parameter checks. */
+ BUG_ON(auth_ctrl == NULL);
+
+ if (auth_ctrl->tls_frag_buffer != NULL)
+ {
+ bcmos_free(auth_ctrl->tls_frag_buffer);
+ auth_ctrl->tls_frag_buffer = NULL;
+ auth_ctrl->tls_frag_length = 0;
+ auth_ctrl->tls_total_length = 0;
+ }
+}
+
+static bcmos_errno _dpoe_eap_tls_send_tls_frag_ack_to_onu(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc;
+ bcmolt_buf buf;
+ uint8_t *msg;
+ eap_length_fields lf;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ /* Allocate a message buffer */
+ msg = bcmos_calloc(EAPOL_PKT_SIZE);
+ if (NULL == msg)
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ /* Initialize the EAP-TLS buffer. */
+ bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED);
+ if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, EAP_CODE_REQUEST, &lf) &&
+ _dpoe_eap_tls_init_tls_hdr(&buf, 0, &lf) &&
+ _dpoe_eap_tls_pack_msg_lengths(&buf, 0, &lf))
+ {
+ /* Send the message */
+ rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf));
+ if (rc != BCM_ERR_OK)
+ {
+ bcmos_free(msg);
+ return rc;
+ }
+ }
+ else
+ {
+ rc = BCM_ERR_OVERFLOW;
+ }
+
+ bcmos_free(msg);
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_unpack_tls_handshake(bcmolt_buf *buf, tls_handshake* hs)
+{
+ uint24_t temp;
+ bcmos_bool rc = BCMOS_FALSE;
+
+ if (bcmolt_buf_read_u8(buf, &hs->msg_id) &&
+ bcmolt_buf_read_u24(buf, &temp))
+ {
+ hs->msg_length = uint24_to_32(temp);
+ rc = BCMOS_TRUE;
+ }
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_eap_tls_run_auth_state_machine(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ uint8_t *hs_snap;
+ tls_handshake handshake;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ while (hs_snap = bcmolt_buf_snap_get(buf), _dpoe_eap_tls_unpack_tls_handshake(buf, &handshake))
+ {
+ bcmos_bool success = BCMOS_TRUE;
+
+ if (handshake.msg_length > EAPOL_PKT_SIZE)
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "Handshake length is insane: msg %u, len %u\n",
+ handshake.msg_id, handshake.msg_length);
+ return BCM_ERR_PARSE;
+ }
+
+ if ((handshake.msg_id == HS_CLIENT_HELLO) &&
+ (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_EAP_START))
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n",
+ handshake.msg_id, link_rec->auth_ctrl.onu_auth_state);
+ _buffer_hash_data(link_rec, hs_snap, handshake.msg_length + TLS_HANDSHAKE_SIZE);
+ success = _dpoe_eap_tls_handle_client_hello_hs(link_rec, buf);
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state);
+ }
+ else if ((handshake.msg_id == HS_CERTIFICATE) &&
+ (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_REQUEST))
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n",
+ handshake.msg_id, link_rec->auth_ctrl.onu_auth_state);
+ _buffer_hash_data(link_rec, hs_snap, handshake.msg_length + TLS_HANDSHAKE_SIZE);
+ success = _dpoe_eap_tls_handle_certificate_hs(link_rec, buf);
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state);
+ }
+ else if ((handshake.msg_id == HS_CLIENT_KEY_EXCHANGE) &&
+ (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_RECEIVED))
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n",
+ handshake.msg_id, link_rec->auth_ctrl.onu_auth_state);
+ _buffer_hash_data(link_rec, hs_snap, handshake.msg_length + TLS_HANDSHAKE_SIZE);
+ success = _dpoe_eap_tls_handle_client_key_exchange_hs(link_rec, buf);
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state);
+ }
+ else if ((handshake.msg_id == HS_CERTIFICATE_VERIFY) &&
+ ((link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_RECEIVED) ||
+ (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CLIENT_KEY_RECEIVED)))
+
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n",
+ handshake.msg_id, link_rec->auth_ctrl.onu_auth_state);
+ dpoe_sec_sha1_final(link_rec->auth_ctrl.trans_data.sha1_digest, &link_rec->auth_ctrl.trans_data.sha1_hash);
+ dpoe_sec_md5_final(link_rec->auth_ctrl.trans_data.md5_digest, &link_rec->auth_ctrl.trans_data.md5_hash);
+ success = _dpoe_eap_tls_handle_cert_verify_hs(link_rec, buf);
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state);
+ }
+ else if ((handshake.msg_id == HS_FINISHED) &&
+ (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_VALIDATED))
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n",
+ handshake.msg_id, link_rec->auth_ctrl.onu_auth_state);
+ success = _dpoe_eap_tls_handle_finished_hs(link_rec);
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state);
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n",
+ handshake.msg_id, link_rec->auth_ctrl.onu_auth_state);
+ success = BCMOS_FALSE;
+ }
+
+ if (!success)
+ {
+ /* Something went wrong, just abort... */
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS handshake processing failed: %u\n", handshake.msg_id);
+ rc = BCM_ERR_PARSE;
+ }
+ }
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_unpack_tls_record_hdr(bcmolt_buf *buf, tls_record_hdr *msg)
+{
+ return bcmolt_buf_read_u8(buf, &msg->content_type) &&
+ bcmolt_buf_read_u8(buf, &msg->tls_version.major) &&
+ bcmolt_buf_read_u8(buf, &msg->tls_version.minor) &&
+ bcmolt_buf_read_u16_be(buf, &msg->tls_record_length);
+}
+
+static bcmos_errno _dpoe_eap_tls_process_tls_records(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ bcmolt_buf buf;
+ tls_record_hdr tls_hdr;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ bcmolt_buf_init(&buf, link_rec->auth_ctrl.tls_frag_length, link_rec->auth_ctrl.tls_frag_buffer, BCMOLT_BUF_ENDIAN_FIXED);
+ while ((rc == BCM_ERR_OK) && _dpoe_eap_tls_unpack_tls_record_hdr(&buf, &tls_hdr))
+ {
+ rc = _dpoe_eap_tls_run_auth_state_machine(link_rec, &buf);
+ }
+
+ return rc;
+}
+
+static bcmos_bool _dpoe_eap_tls_check_tls_version(const tls_record_hdr *tls_rec_hdr)
+{
+ /* Parameter checks. */
+ BUG_ON(tls_rec_hdr == NULL);
+
+ if ((tls_rec_hdr->content_type != TLS_HANDSHAKE_ID) ||
+ (tls_rec_hdr->tls_version.major != TLS_MAJOR_VERSION) ||
+ (tls_rec_hdr->tls_version.minor < TLS_MINOR_VERSION))
+ {
+ return BCMOS_TRUE;
+ }
+ return BCMOS_FALSE;
+}
+
+static void _dpoe_eap_tls_buffer_tls_data(dpoe_sec_link_rec *link_rec, const void *data, uint32_t length)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(data == NULL);
+
+ /* do not overrun the buffer, no matter how much data received */
+ if ((link_rec->auth_ctrl.tls_frag_length + length) > EAPOL_PKT_SIZE)
+ {
+ length = EAPOL_PKT_SIZE - link_rec->auth_ctrl.tls_frag_length;
+ }
+
+ memcpy(&link_rec->auth_ctrl.tls_frag_buffer[link_rec->auth_ctrl.tls_frag_length], data, length);
+ link_rec->auth_ctrl.tls_frag_length += length;
+}
+
+static bcmos_bool _dpoe_eap_tls_unpack_msg(bcmolt_buf *buf, eapol_msg *msg)
+{
+ return dpoe_sec_eapol_unpack(buf, &msg->hdr) &&
+ bcmolt_buf_read_u8(buf, &msg->eap_msg.hdr.eap_code) &&
+ bcmolt_buf_read_u8(buf, &msg->eap_msg.hdr.id) &&
+ bcmolt_buf_read_u16_be(buf, &msg->eap_msg.hdr.length) &&
+ bcmolt_buf_read_u8(buf, &msg->eap_msg.tls.auth_sub_type) &&
+ bcmolt_buf_read_u8(buf, &msg->eap_msg.tls.eap_tls_flags);
+}
+
+static bcmos_errno _dpoe_eap_tls_gen_key(auth_trans_data *trans_data)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ dpoe_sec_rsa_key *rsa = NULL;
+
+ /* Parameter checks */
+ BUG_ON(trans_data == NULL);
+
+ /* Generate the RSA key to be used in the ServerKeyExchange and ClientKeyExchange */
+ rsa = dpoe_sec_rsa_generate_key(DPOE_BI_RSA_KEY_SIZE);
+ if (rsa == NULL)
+ {
+ rc = BCM_ERR_INTERNAL;
+ }
+ else
+ {
+ /* Store the pointer to the key. */
+ trans_data->rsa = rsa;
+ }
+
+ return rc;
+}
+
+bcmos_errno dpoe_eap_tls_send_start(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ bcmolt_buf buf;
+ uint8_t *msg;
+ time_t unix_time;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+
+ /* Initialize the authentication control data. */
+ memset(&link_rec->auth_ctrl, 0, sizeof(link_rec->auth_ctrl));
+ dpoe_sec_sha1_init(&link_rec->auth_ctrl.trans_data.sha1_hash);
+ dpoe_sec_md5_init(&link_rec->auth_ctrl.trans_data.md5_hash);
+
+ /* Per standard, the first four bytes of the server random value should be "unix time" */
+ unix_time = time(NULL);
+ memcpy(link_rec->auth_ctrl.trans_data.server_random, &unix_time, sizeof(unix_time));
+ dpoe_sec_generate_n_byte_random_number(&link_rec->auth_ctrl.trans_data.server_random[sizeof(unix_time)], COUNT_OF_RANDOM_BYTES - sizeof(unix_time));
+
+ /* If bidirectional, generate the RSA public/private key pair for the ServerKeyExchange and ClientKeyExchange. */
+ if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI)
+ {
+ rc = _dpoe_eap_tls_gen_key(&link_rec->auth_ctrl.trans_data);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+ }
+
+ /* Allocate a message buffer */
+ msg = bcmos_calloc(EAPOL_PKT_SIZE);
+ if (NULL == msg)
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ /* Initialize the EAP-TLS buffer. */
+ bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED);
+
+ /* Encode the EAP-TLS Start message to the buffer. */
+ _dpoe_eap_tls_pack_start(link_rec, &buf);
+
+ /* Send the message */
+ rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf));
+ bcmos_free(msg);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+
+ /* Authentication has begun. */
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_EAP_START;
+
+ return rc;
+}
+
+bcmos_errno dpoe_eap_tls_process_eapol_pkt(dpoe_sec_link_rec *link_rec, uint8_t *msg, uint32_t msg_len)
+{
+ bcmos_errno rc = BCM_ERR_INTERNAL;
+ uint32_t eap_tls_length;
+ tls_record_hdr tls_rec_hdr;
+ eapol_msg unpacked_msg;
+ bcmolt_buf buf;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(msg == NULL);
+
+ bcmolt_buf_init(&buf, msg_len, msg, BCMOLT_BUF_ENDIAN_FIXED);
+ _dpoe_eap_tls_unpack_msg(&buf, &unpacked_msg);
+
+ if (unpacked_msg.eap_msg.tls.auth_sub_type == EAP_TYPE_NAK)
+ {
+ link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_FAILED;
+ return BCM_ERR_ONU_ERR_RESP;
+ }
+
+ if (unpacked_msg.hdr.eapol_length != unpacked_msg.eap_msg.hdr.length)
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ /* We should only read the length if the length bit is set. */
+ if ((unpacked_msg.eap_msg.tls.eap_tls_flags & EAP_TLS_FLAG_LENGTH_BIT) != 0)
+ {
+ bcmolt_buf_read_u32_be(&buf, &eap_tls_length);
+ uint8_t *tls_data = bcmolt_buf_snap_get(&buf);
+ _dpoe_eap_tls_unpack_tls_record_hdr(&buf, &tls_rec_hdr);
+ if (_dpoe_eap_tls_check_tls_version(&tls_rec_hdr))
+ {
+ /* Invalid TLS record, discard... */
+ return BCM_ERR_PARSE;
+ }
+
+ /* Allocate the fragmentation buffer. Multiple calls to the frag alloc function is allowed since it allocates a
+ new buffer only if there is no frag buffer currently allocated. The frag buffer is also freed if the EAP-TLS
+ session goes down for some reason. */
+ if (!_dpoe_eap_tls_alloc_tls_frag_buffer(&link_rec->auth_ctrl))
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ /* If the length bit is set, and the more bit is not, this is likely a single fragment packet. In that case
+ the TlsMsg length must be present, and match the size of the buffer. It is also possible that this is a
+ subsequent fragment and the client always adds the length (which per standard is the length of the set of
+ fragments), in which case we cannot validate this now. The best we can do is save this size, and validate
+ when all fragments have been received. */
+ if (link_rec->auth_ctrl.tls_total_length == 0)
+ {
+ link_rec->auth_ctrl.tls_total_length = eap_tls_length;
+ }
+
+ if (link_rec->auth_ctrl.tls_total_length != eap_tls_length)
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ _dpoe_eap_tls_buffer_tls_data(
+ link_rec,
+ tls_data,
+ unpacked_msg.hdr.eapol_length - (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE + sizeof(uint32_t)));
+
+ if ((unpacked_msg.eap_msg.tls.eap_tls_flags & EAP_TLS_FLAG_MORE_BIT) != 0)
+ {
+ /* Need to acknowledge the fragment */
+ _dpoe_eap_tls_send_tls_frag_ack_to_onu(link_rec);
+ rc = BCM_ERR_IN_PROGRESS;
+ }
+ else
+ {
+ /* Either a single fragment packet, or the last fragment of a multi-fragment packet. If it is the last
+ fragment of a multi-fragment, then all we can check is that eapTlsLength is one TlsRecordHdr bigger than
+ the buffered (authCtrl->tlsFragLen) data stream. The same logic works on a single frag packet. */
+ if ((eap_tls_length != link_rec->auth_ctrl.tls_total_length) ||
+ (eap_tls_length != link_rec->auth_ctrl.tls_frag_length))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ rc = _dpoe_eap_tls_process_tls_records(link_rec);
+ _dpoe_eap_tls_free_tls_frag_buffer(&link_rec->auth_ctrl);
+ }
+ }
+ else
+ {
+ uint8_t *tls_data = bcmolt_buf_snap_get(&buf);
+
+ /* No length field - must be fragmented, and not first */
+ if (link_rec->auth_ctrl.tls_frag_buffer == NULL)
+ {
+ return BCM_ERR_OUT_OF_SYNC;
+ }
+
+ _dpoe_eap_tls_buffer_tls_data(
+ link_rec,
+ tls_data,
+ unpacked_msg.hdr.eapol_length - (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE));
+
+ /* If the more bit is set, wait for the next fragment. Otherwise dump what we have into the state machine. */
+ if ((unpacked_msg.eap_msg.tls.eap_tls_flags & EAP_TLS_FLAG_MORE_BIT) != 0)
+ {
+ /* Need to acknowledge the fragment? The standard does not define a message set that will get us here. */
+ _dpoe_eap_tls_send_tls_frag_ack_to_onu(link_rec);
+ rc = BCM_ERR_IN_PROGRESS;
+ }
+ else
+ {
+ if (link_rec->auth_ctrl.tls_total_length != link_rec->auth_ctrl.tls_frag_length)
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ rc = _dpoe_eap_tls_process_tls_records(link_rec);
+ _dpoe_eap_tls_free_tls_frag_buffer(&link_rec->auth_ctrl);
+ }
+ }
+
+ return rc;
+}
+
+void dpoe_eap_tls_cleanup(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+
+ // We are done with this.
+ if (link_rec->auth_ctrl.certificate != NULL)
+ {
+ bcmos_free(link_rec->auth_ctrl.certificate);
+ }
+
+ if (link_rec->auth_ctrl.trans_data.rsa != NULL)
+ {
+ dpoe_sec_rsa_key_free(link_rec->auth_ctrl.trans_data.rsa);
+ link_rec->auth_ctrl.trans_data.rsa = NULL;
+ }
+
+ link_rec->auth_ctrl.certLen = 0;
+
+ _dpoe_eap_tls_free_tls_frag_buffer(&link_rec->auth_ctrl);
+
+ memset(&link_rec->auth_ctrl, 0, sizeof(link_rec->auth_ctrl));
+}
+
+bcmos_errno dpoe_eap_tls_init(f_dpoe_sec_auth_cb auth_cb, f_dpoe_sec_cert_trust_cb cert_trust_cb)
+{
+ _auth_complete = auth_cb;
+ _cert_trust = cert_trust_cb;
+
+ return dpoe_sec_rng_seed();
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_eap_tls.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_eap_tls.h
new file mode 100644
index 0000000..984d555
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_eap_tls.h
@@ -0,0 +1,122 @@
+/*
+<: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.
+
+:>
+ */
+
+#if !defined(_DPOE_EAP_TLS_H_)
+#define _DPOE_EAP_TLS_H_
+
+#include "bcmos_system.h"
+#include "dpoe_sec_util.h"
+
+#define COUNT_OF_RANDOM_BYTES 32
+#define SIZE_OF_MASTER_SECRET 48
+#define SIZE_OF_MASTER_SESSION_KEY 64
+#define SIZE_OF_KEY_MATERIAL 128
+#define SIZE_OF_RSA_ENCRYPTED_BLOCK 256
+#define SIZE_OF_EAP_SESSION_ID 65
+#define SIZE_OF_TLS_SESSION_ID 32
+
+/* Total length of the PreMasterSecret */
+#define SIZE_OF_PRE_MASTER_SECRET 48
+
+typedef enum
+{
+ DPOE_ONU_AUTH_STATE_UNAUTHENTICATED,
+ DPOE_ONU_AUTH_STATE_EAP_START,
+ DPOE_ONU_AUTH_STATE_CERT_REQUEST,
+ DPOE_ONU_AUTH_STATE_CLIENT_KEY_RECEIVED,
+ DPOE_ONU_AUTH_STATE_CERT_RECEIVED,
+ DPOE_ONU_AUTH_STATE_CERT_VALIDATED,
+ DPOE_ONU_AUTH_STATE_AUTHENTICATED,
+ DPOE_ONU_AUTH_STATE_FAILED
+} dpoe_onu_auth_state;
+
+typedef struct
+{
+ uint8_t major;
+ uint8_t minor;
+} tls_protocol_version;
+
+#define PROTOCOL_VERSION_SIZE 2
+
+/* DPoE Authentication transient data */
+typedef struct
+{
+ uint8_t client_random[COUNT_OF_RANDOM_BYTES];
+ uint8_t server_random[COUNT_OF_RANDOM_BYTES];
+ uint8_t session_id[SIZE_OF_EAP_SESSION_ID];
+ uint8_t master_secret[SIZE_OF_MASTER_SECRET];
+ uint8_t key_material[SIZE_OF_KEY_MATERIAL];
+ uint8_t pre_master_secret[SIZE_OF_PRE_MASTER_SECRET];
+ dpoe_sec_sha1_hash sha1_hash;
+ dpoe_sec_sha1_digest sha1_digest;
+ dpoe_sec_md5_hash md5_hash;
+ dpoe_sec_md5_digest md5_digest;
+ /* Allow the key to double in size before we break. */
+ uint8_t encrypted_cak[2 * SIZE_OF_RSA_ENCRYPTED_BLOCK];
+ uint8_t master_session_key[SIZE_OF_MASTER_SESSION_KEY];
+ dpoe_sec_rsa_key *rsa;
+} auth_trans_data;
+
+typedef struct onu_auth_control
+{
+ dpoe_onu_auth_state onu_auth_state;
+ tls_protocol_version version;
+ uint32_t current_packet_id;
+ uint8_t *certificate;
+ uint16_t certLen;
+ uint8_t *onu_cert;
+ uint16_t onu_cert_len;
+ uint32_t onu_cert_key_size;
+ uint8_t *mfg_cert;
+ uint16_t mfg_cert_len;
+ uint8_t *tls_frag_buffer;
+ uint32_t tls_frag_length;
+ uint32_t tls_total_length;
+ auth_trans_data trans_data;
+} onu_auth_control;
+
+struct dpoe_sec_link_rec;
+
+/* callbacks */
+
+typedef void (*f_dpoe_sec_auth_cb)(struct dpoe_sec_link_rec*, bcmos_errno status);
+
+typedef bcmos_bool (*f_dpoe_sec_cert_trust_cb)(struct dpoe_sec_link_rec*);
+
+/* functions */
+
+bcmos_errno dpoe_eap_tls_send_start(struct dpoe_sec_link_rec *link);
+
+bcmos_errno dpoe_eap_tls_process_eapol_pkt(struct dpoe_sec_link_rec *link, uint8_t *msg, uint32_t msg_len);
+
+void dpoe_eap_tls_cleanup(struct dpoe_sec_link_rec *link);
+
+bcmos_errno dpoe_eap_tls_init(f_dpoe_sec_auth_cb auth_cb, f_dpoe_sec_cert_trust_cb cert_trust_cb);
+
+#endif /* _DPOE_EAP_TLS_H_ */
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_fsm.c b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_fsm.c
new file mode 100644
index 0000000..a6c96de
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_fsm.c
@@ -0,0 +1,826 @@
+/*
+ <: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 "dpoe_sec_fsm.h"
+#include "dpoe_sec_util.h"
+#include "bcmos_hash_table.h"
+#include "bcmolt_epon_oam_types.h"
+#include "bcmolt_utils.h"
+#include "bcmolt_user_appl_epon_oam.h"
+#include "bcmolt_api.h"
+
+#define DPOE_SEC_FSM_MAX_LINKS 64
+#define DPOE_SEC_FSM_TASK_MSG_Q_SIZE 64
+
+static const uint16_t DPOE_SEC_FSM_DEFAULT_OAM_KEY_EXCHANGE_PERIOD = 600; /* 10 minutes */
+/**
+ * @brief Maximum ONU Authentication timeout value
+ * DPoE specification indicates that this value is 5 minutes.
+ */
+static const uint32_t DPOE_SEC_AUTH_TIMEOUT = 5 * 60 * 1000 * 1000;
+
+typedef struct
+{
+ bcmos_task task;
+ char task_name[MAX_TASK_NAME_SIZE];
+ char msg_queue_name[MAX_MSG_QUEUE_NAME_SIZE];
+ hash_table *link_map;
+} dpoe_sec_fsm_ctxt;
+
+dpoe_sec_fsm_ctxt ctxt[BCMTR_MAX_OLTS];
+
+typedef void (*f_dpoe_sec_fsm)(dpoe_sec_link_rec*, dpoe_sec_fsm_event_data*);
+
+/* DPoE Security FSM Event type to string definitions used for debug output */
+static const char *dpoe_sec_fsm_event_name[DPOE_SEC_FSM_EVENT__COUNT] =
+{
+ [DPOE_SEC_FSM_EVENT_ENCRYPT_START] = "Start Encryption",
+ [DPOE_SEC_FSM_EVENT_AUTH_START] = "Start Authentication",
+ [DPOE_SEC_FSM_EVENT_RX_OAM] = "Receive OAM",
+ [DPOE_SEC_FSM_EVENT_RX_EAPOL] = "Receive EAPOL",
+ [DPOE_SEC_FSM_EVENT_AUTH_COMPLETE] = "Authentication Complete",
+ [DPOE_SEC_FSM_EVENT_MKA_COMPLETE] = "MKA Complete",
+ [DPOE_SEC_FSM_EVENT_TIMEOUT] = "Timeout"
+};
+
+
+/* DPoE Security FSM State to string definitions used for debug output */
+static const char *dpoe_sec_fsm_state_name[DPOE_SEC_FSM_STATE__COUNT] =
+{
+ [DPOE_SEC_FSM_STATE_IDLE] = "Idle",
+ [DPOE_SEC_FSM_STATE_AUTH_WAIT] = "Authentication in progress",
+ [DPOE_SEC_FSM_STATE_ENCRYPT_DOWN_WAIT] = "OAM Key Exchange in progress",
+ [DPOE_SEC_FSM_STATE_ENCRYPT_BI_WAIT] = "MKA in progress",
+ [DPOE_SEC_FSM_STATE_COMPLETE] = "Authentication/Encryption Complete"
+};
+
+static f_dpoe_sec_fsm_cb _fsm_complete;
+
+static bcmos_bool _dpoe_sec_fsm_event_type_is_valid(dpoe_sec_fsm_event type)
+{
+ return type < DPOE_SEC_FSM_EVENT__COUNT;
+}
+
+static bcmos_bool _dpoe_sec_fsm_state_is_valid(dpoe_sec_fsm_state state)
+{
+ return state < DPOE_SEC_FSM_STATE__COUNT;
+}
+
+static const char *_dpoe_sec_fsm_event_name(dpoe_sec_fsm_event type)
+{
+ if (_dpoe_sec_fsm_event_type_is_valid(type))
+ {
+ return dpoe_sec_fsm_event_name[type];
+ }
+ else
+ {
+ return "Unknown Event";
+ }
+}
+
+static const char *_dpoe_sec_fsm_state_name(dpoe_sec_fsm_state state)
+{
+ if (_dpoe_sec_fsm_state_is_valid(state))
+ {
+ return dpoe_sec_fsm_state_name[state];
+ }
+ else
+ {
+ return "Unknown State";
+ }
+}
+
+static void _dpoe_sec_fsm_complete(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+
+ if (_fsm_complete != NULL)
+ {
+ _fsm_complete(link_rec->device, link_rec->key, link_rec->status);
+ }
+}
+
+static void _dpoe_sec_fsm_success(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+
+ link_rec->fsm_state = DPOE_SEC_FSM_STATE_COMPLETE;
+ link_rec->status = BCM_ERR_OK;
+
+ switch (link_rec->cfg.enc_mode)
+ {
+ case BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_NONE:
+ case BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI:
+ break;
+ case BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_ONE_DOWN:
+ case BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_DOWN:
+ bcmos_timer_start(
+ &link_rec->timeout,
+ (DPOE_SEC_FSM_DEFAULT_OAM_KEY_EXCHANGE_PERIOD * 1000 * 1000) + DPOE_SEC_OAM_TIMEOUT);
+ break;
+ default:
+ BUG();
+ }
+
+ _dpoe_sec_fsm_complete(link_rec);
+}
+
+static void _dpoe_sec_fsm_stop(dpoe_sec_link_rec *link_rec, bcmos_errno reason)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+
+ link_rec->fsm_state = DPOE_SEC_FSM_STATE_IDLE;
+ link_rec->status = reason;
+
+ bcmos_timer_stop(&link_rec->timeout);
+
+ /* Cleanup any EAP-TLS state. */
+ dpoe_eap_tls_cleanup(link_rec);
+
+ _dpoe_sec_fsm_complete(link_rec);
+}
+
+static void _dpoe_sec_fsm_auth_start(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ bcmos_errno rc;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ /* Start ONU Authentication */
+ rc = dpoe_eap_tls_send_start(link_rec);
+ if (BCM_ERR_OK != rc)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to start authentication: %s (%d)\n", bcmos_strerror(rc), rc);
+ _dpoe_sec_fsm_stop(link_rec, rc);
+ }
+ else
+ {
+ link_rec->fsm_state = DPOE_SEC_FSM_STATE_AUTH_WAIT;
+ bcmos_timer_start(&link_rec->timeout, DPOE_SEC_AUTH_TIMEOUT);
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "Starting authentication\n");
+ }
+}
+
+static void _dpoe_sec_fsm_mka_start(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = dpoe_sec_mka_fsm_start(link_rec);
+ if (BCM_ERR_OK != rc)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to start MKA: %s (%d)\n", bcmos_strerror(rc), rc);
+ _dpoe_sec_fsm_stop(link_rec, rc);
+ }
+ else
+ {
+ link_rec->fsm_state = DPOE_SEC_FSM_STATE_ENCRYPT_BI_WAIT;
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "Starting MKA\n");
+ }
+}
+
+static void _dpoe_sec_fsm_encrypt_start(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ bcmos_errno rc;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI)
+ {
+ _dpoe_sec_fsm_mka_start(link_rec);
+ }
+ else
+ {
+ rc = epon_oam_dpoe_set_encryption(
+ link_rec->device,
+ link_rec->key.epon_ni,
+ &link_rec->key.mac_address,
+ link_rec->cfg.enc_mode,
+ DPOE_SEC_FSM_DEFAULT_OAM_KEY_EXCHANGE_PERIOD);
+ if (BCM_ERR_OK != rc)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to set encryption: %s (%d)\n", bcmos_strerror(rc), rc);
+ _dpoe_sec_fsm_stop(link_rec, rc);
+ }
+ else
+ {
+ link_rec->fsm_state = DPOE_SEC_FSM_STATE_ENCRYPT_DOWN_WAIT;
+ bcmos_timer_start(&link_rec->timeout, DPOE_SEC_OAM_TIMEOUT);
+ }
+ }
+}
+
+static void _dpoe_sec_fsm_auth_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ bcmos_errno rc;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ rc = dpoe_eap_tls_process_eapol_pkt(link_rec, evt->data.rx_frame.val, evt->data.rx_frame.len);
+ if ((BCM_ERR_OK != rc) && (BCM_ERR_IN_PROGRESS != rc))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Error processing EAPOL packet: %s (%d)\n", bcmos_strerror(rc), rc);
+ _dpoe_sec_fsm_stop(link_rec, rc);
+ }
+}
+
+static void _dpoe_sec_fsm_auth_complete(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ bcmos_timer_stop(&link_rec->timeout);
+ if (BCM_ERR_OK == evt->data.sub_status)
+ {
+ if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI)
+ {
+ _dpoe_sec_fsm_mka_start(link_rec);
+ }
+ else
+ {
+ _dpoe_sec_fsm_success(link_rec);
+ }
+ }
+ else
+ {
+ _dpoe_sec_fsm_stop(link_rec, evt->data.sub_status);
+ }
+}
+
+static bcmos_errno _dpoe_sec_fsm_install_dn_key(
+ dpoe_sec_link_rec *link_rec,
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe)
+{
+ bcmolt_encryption_information_container key_info;
+
+ if (BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_ONE_DOWN == link_rec->cfg.enc_mode)
+ {
+ key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CFB;
+ memcpy(key_info.u.cfb.key, dpoe->u.key_exchange.key_data, sizeof(key_info.u.cfb.key));
+ }
+ else
+ {
+ key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR;
+ memcpy(key_info.u.ctr.key, dpoe->u.key_exchange.key_data, sizeof(key_info.u.ctr.key));
+ memcpy(key_info.u.ctr.sci, link_rec->ni_mac.u8, BCMOS_ETH_ALEN);
+ key_info.u.ctr.sci[6] = (link_rec->llid >> 8) & 0xff;
+ key_info.u.ctr.sci[7] = (link_rec->llid >> 0) & 0xff;
+ }
+
+ return bcmolt_epon_link_encryption_key_set(
+ link_rec->device,
+ &link_rec->key,
+ BCMOS_FALSE,
+ BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES,
+ (bcmolt_epon_key_choice)dpoe->u.key_exchange.key_number,
+ &key_info);
+}
+
+static void _dpoe_sec_fsm_enc_dn_rx_oam(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ bcmolt_epon_oam_ethernet_frame *oam_frame;
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ oam_frame = epon_oam_unpack(link_rec->device, evt->data.rx_frame.len, evt->data.rx_frame.val);
+ if (NULL == oam_frame)
+ {
+ DPOE_SEC_LINK_LOG(WARNING, link_rec, "Failed to parse OAM frame\n");
+ return;
+ }
+
+ if (!epon_oam_is_dpoe(oam_frame))
+ {
+ bcmos_free(oam_frame);
+ return;
+ }
+
+ dpoe = &oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value;
+
+ if (epon_oam_is_dpoe_encrypt_response(dpoe))
+ {
+ if ((dpoe->u.set_response.vars[0].u.extended_attribute.attribute.width == 0x80) &&
+ (dpoe->u.set_response.vars[1].u.extended_attribute.attribute.width == 0x80))
+ {
+ bcmos_timer_start(&link_rec->timeout, DPOE_SEC_OAM_TIMEOUT); /* restart timer to wait for key */
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "ONU rejected encryption: [0] = 0x%x, [1] = 0x%x\n",
+ dpoe->u.set_response.vars[0].u.extended_attribute.attribute.width,
+ dpoe->u.set_response.vars[1].u.extended_attribute.attribute.width);
+ _dpoe_sec_fsm_stop(link_rec, BCM_ERR_ONU_ERR_RESP);
+ }
+ }
+ else if (BCMOLT_EPON_OAM_DPOE_OPCODE_KEY_EXCHANGE == dpoe->op)
+ {
+ _dpoe_sec_fsm_install_dn_key(link_rec, dpoe);
+ if (link_rec->cfg.auth)
+ {
+ _dpoe_sec_fsm_auth_start(link_rec, evt);
+ }
+ else
+ {
+ _dpoe_sec_fsm_success(link_rec);
+ }
+ }
+ else
+ {
+ /* ignore other OAM */
+ }
+
+ bcmos_free(oam_frame);
+}
+
+static void _dpoe_sec_fsm_enc_bi_rx_oam(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ dpoe_sec_mka_fsm_rx_oam(link_rec, evt->data.rx_frame.val, evt->data.rx_frame.len);
+}
+
+static void _dpoe_sec_fsm_enc_bi_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ dpoe_sec_mka_fsm_rx_eapol(link_rec, evt->data.rx_frame.val, evt->data.rx_frame.len);
+}
+
+static void _dpoe_sec_fsm_mka_complete(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ if (BCM_ERR_OK == evt->data.sub_status)
+ {
+ _dpoe_sec_fsm_success(link_rec);
+ }
+ else
+ {
+ _dpoe_sec_fsm_stop(link_rec, evt->data.sub_status);
+ }
+}
+
+static void _dpoe_sec_fsm_comp_rx_oam(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ if ((link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_ONE_DOWN) ||
+ (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_DOWN))
+ {
+ bcmolt_epon_oam_ethernet_frame *oam_frame;
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe;
+
+ oam_frame = epon_oam_unpack(link_rec->device, evt->data.rx_frame.len, evt->data.rx_frame.val);
+ if (NULL == oam_frame)
+ {
+ DPOE_SEC_LINK_LOG(WARNING, link_rec, "Failed to parse OAM frame\n");
+ return;
+ }
+
+ if (!epon_oam_is_dpoe(oam_frame))
+ {
+ bcmos_free(oam_frame);
+ return;
+ }
+
+ dpoe = &oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value;
+
+ if (BCMOLT_EPON_OAM_DPOE_OPCODE_KEY_EXCHANGE == dpoe->op)
+ {
+ _dpoe_sec_fsm_install_dn_key(link_rec, dpoe);
+ }
+ }
+}
+
+static void _dpoe_sec_fsm_comp_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI)
+ {
+ dpoe_sec_mka_fsm_rx_eapol(link_rec, evt->data.rx_frame.val, evt->data.rx_frame.len);
+ }
+}
+
+static void _dpoe_sec_fsm_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ _dpoe_sec_fsm_stop(link_rec, BCM_ERR_TIMEOUT);
+}
+
+static f_dpoe_sec_fsm dpoe_sec_fsm[DPOE_SEC_FSM_STATE__COUNT][DPOE_SEC_FSM_EVENT__COUNT] =
+{
+ [DPOE_SEC_FSM_STATE_IDLE] =
+ {
+ [DPOE_SEC_FSM_EVENT_AUTH_START] = _dpoe_sec_fsm_auth_start,
+ [DPOE_SEC_FSM_EVENT_ENCRYPT_START] = _dpoe_sec_fsm_encrypt_start
+ },
+ [DPOE_SEC_FSM_STATE_AUTH_WAIT] =
+ {
+ [DPOE_SEC_FSM_EVENT_RX_EAPOL] = _dpoe_sec_fsm_auth_rx_eapol,
+ [DPOE_SEC_FSM_EVENT_AUTH_COMPLETE] = _dpoe_sec_fsm_auth_complete,
+ [DPOE_SEC_FSM_EVENT_TIMEOUT] = _dpoe_sec_fsm_timeout
+ },
+ [DPOE_SEC_FSM_STATE_ENCRYPT_DOWN_WAIT] =
+ {
+ [DPOE_SEC_FSM_EVENT_RX_OAM] = _dpoe_sec_fsm_enc_dn_rx_oam,
+ [DPOE_SEC_FSM_EVENT_TIMEOUT] = _dpoe_sec_fsm_timeout
+ },
+ [DPOE_SEC_FSM_STATE_ENCRYPT_BI_WAIT] =
+ {
+ [DPOE_SEC_FSM_EVENT_RX_OAM] = _dpoe_sec_fsm_enc_bi_rx_oam,
+ [DPOE_SEC_FSM_EVENT_RX_EAPOL] = _dpoe_sec_fsm_enc_bi_rx_eapol,
+ [DPOE_SEC_FSM_EVENT_MKA_COMPLETE] = _dpoe_sec_fsm_mka_complete,
+ [DPOE_SEC_FSM_EVENT_TIMEOUT] = _dpoe_sec_fsm_timeout
+ },
+ [DPOE_SEC_FSM_STATE_COMPLETE] =
+ {
+ [DPOE_SEC_FSM_EVENT_RX_OAM] = _dpoe_sec_fsm_comp_rx_oam,
+ [DPOE_SEC_FSM_EVENT_RX_EAPOL] = _dpoe_sec_fsm_comp_rx_eapol,
+ [DPOE_SEC_FSM_EVENT_TIMEOUT] = _dpoe_sec_fsm_timeout
+ }
+};
+
+static void _dpoe_sec_fsm_unexpected(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ DPOE_SEC_LINK_LOG(WARNING, link_rec, "Unexpected event %s (%u) in state %s (%u)\n",
+ _dpoe_sec_fsm_event_name(evt->type), evt->type, _dpoe_sec_fsm_state_name(link_rec->fsm_state), link_rec->fsm_state);
+}
+
+static void _dpoe_sec_fsm_exec(dpoe_sec_link_rec *link_rec, dpoe_sec_fsm_event_data *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ if (evt->type < DPOE_SEC_FSM_EVENT__COUNT)
+ {
+ dpoe_sec_fsm_state pre_state = link_rec->fsm_state;
+
+ if (dpoe_sec_fsm[link_rec->fsm_state][evt->type] != NULL)
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "processing event %s in state %s\n",
+ _dpoe_sec_fsm_event_name(evt->type), _dpoe_sec_fsm_state_name(link_rec->fsm_state));
+ dpoe_sec_fsm[link_rec->fsm_state][evt->type](link_rec, evt);
+ }
+ else
+ {
+ _dpoe_sec_fsm_unexpected(link_rec, evt);
+ }
+
+ if (link_rec->fsm_state != pre_state)
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "transition from %s to %s\n",
+ _dpoe_sec_fsm_state_name(pre_state), _dpoe_sec_fsm_state_name(link_rec->fsm_state));
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Unknown event: %u", evt->type);
+ }
+}
+
+static bcmos_timer_rc _dpoe_sec_fsm_link_timer_expired(bcmos_timer *timer, long data)
+{
+ dpoe_sec_link_rec *link_rec = (dpoe_sec_link_rec*)data;
+ dpoe_sec_fsm_event_data evt;
+
+ evt.type = DPOE_SEC_FSM_EVENT_TIMEOUT;
+
+ _dpoe_sec_fsm_exec(link_rec, &evt);
+
+ return BCMOS_TIMER_OK;
+}
+
+static void _dpoe_sec_fsm_link_start_handle(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ dpoe_sec_fsm_msg *msg = (dpoe_sec_fsm_msg*)os_msg;
+ bcmolt_devid device = os_msg->instance;
+ dpoe_sec_link_rec *link_rec;
+ dpoe_sec_fsm_event_data evt;
+ bcmos_errno err;
+
+ link_rec = hash_table_get(ctxt[device].link_map, (const uint8_t*)&msg->link);
+ if (link_rec == NULL)
+ {
+ dpoe_sec_link_rec new_link;
+ bcmolt_epon_ni_cfg pon_cfg;
+ bcmolt_epon_link_cfg link_cfg;
+ bcmolt_epon_ni_key pon_key = { .epon_ni = msg->link.epon_ni };
+ bcmos_timer_parm tp =
+ {
+ .name = "dpoe_sec_fsm_timer",
+ .owner = BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + device,
+ .periodic = BCMOS_FALSE,
+ .handler = _dpoe_sec_fsm_link_timer_expired,
+ .flags = BCMOS_TIMER_PARM_FLAGS_NON_URGENT
+ };
+
+ /* retrieve the EPON NI MAC address */
+ BCMOLT_CFG_INIT(&pon_cfg, epon_ni, pon_key);
+ BCMOLT_CFG_PROP_GET(&pon_cfg, epon_ni, mac_address);
+ err = bcmolt_cfg_get(device, &pon_cfg.hdr);
+ BUG_ON(BCM_ERR_OK != err);
+
+ BCMOLT_CFG_INIT(&link_cfg, epon_link, msg->link);
+ BCMOLT_CFG_PROP_GET(&link_cfg, epon_link, llid);
+ err = bcmolt_cfg_get(device, &link_cfg.hdr);
+ if (BCM_ERR_OK != err)
+ {
+ BCM_LOG(ERROR, dpoe_sec_log_id[device], "Failed to get LLID\n");
+ }
+
+ new_link.device = device;
+ new_link.key = msg->link;
+ new_link.ni_mac = pon_cfg.data.mac_address;
+ new_link.llid = link_cfg.data.llid;
+ new_link.fsm_state = DPOE_SEC_FSM_STATE_IDLE;
+ new_link.status = BCM_ERR_IN_PROGRESS;
+
+ /* add new rec to hash table */
+ link_rec = hash_table_put(ctxt[device].link_map, (uint8_t*)&msg->link, &new_link);
+ if (link_rec == NULL)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, &new_link, "Failed to store link record\n");
+ }
+
+ tp.data = (long)link_rec;
+ err = bcmos_timer_create(&link_rec->timeout, &tp);
+ BUG_ON(BCM_ERR_OK != err);
+ }
+
+ link_rec->cfg = msg->data.start;
+
+ if (!link_rec->cfg.auth || (link_rec->cfg.enc_mode != BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI))
+ {
+ evt.type = DPOE_SEC_FSM_EVENT_ENCRYPT_START;
+ }
+ else
+ {
+ evt.type = DPOE_SEC_FSM_EVENT_AUTH_START;
+ }
+
+ _dpoe_sec_fsm_exec(link_rec, &evt);
+
+ bcmos_free(os_msg);
+}
+
+static void _dpoe_sec_fsm_link_rx_oam_handle(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ dpoe_sec_fsm_msg *msg = (dpoe_sec_fsm_msg*)os_msg;
+ bcmolt_devid device = os_msg->instance;
+ dpoe_sec_link_rec *link_rec;
+ dpoe_sec_fsm_event_data evt;
+
+ link_rec = hash_table_get(ctxt[device].link_map, (uint8_t*)&msg->link);
+ if (link_rec == NULL)
+ {
+ BCM_LOG(DEBUG, dpoe_sec_log_id[device], "Failed to find link: PON %u, "BCMOS_MACADDR_FMT_STR"\n",
+ msg->link.epon_ni, BCMOS_MACADDR_PARAMS(&msg->link.mac_address));
+ }
+ else
+ {
+ evt.type = DPOE_SEC_FSM_EVENT_RX_OAM;
+ evt.data.rx_frame.val = msg->data.rx_frame.frame;
+ evt.data.rx_frame.len = msg->data.rx_frame.length;
+ _dpoe_sec_fsm_exec(link_rec, &evt);
+ }
+
+ bcmos_free(msg->data.rx_frame.frame);
+ bcmos_free(os_msg);
+}
+
+static void _dpoe_sec_fsm_link_rx_eapol_handle(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ dpoe_sec_fsm_msg *msg = (dpoe_sec_fsm_msg*)os_msg;
+ bcmolt_devid device = os_msg->instance;
+ dpoe_sec_link_rec *link_rec;
+ dpoe_sec_fsm_event_data evt;
+
+ link_rec = hash_table_get(ctxt[device].link_map, (uint8_t*)&msg->link);
+ if (link_rec == NULL)
+ {
+ BCM_LOG(INFO, dpoe_sec_log_id[device], "Failed to find link: PON %u, "BCMOS_MACADDR_FMT_STR"\n",
+ msg->link.epon_ni, BCMOS_MACADDR_PARAMS(&msg->link.mac_address));
+ }
+ else
+ {
+ evt.type = DPOE_SEC_FSM_EVENT_RX_EAPOL;
+ evt.data.rx_frame.val = msg->data.rx_frame.frame;
+ evt.data.rx_frame.len = msg->data.rx_frame.length;
+ _dpoe_sec_fsm_exec(link_rec, &evt);
+ }
+
+ bcmos_free(os_msg);
+}
+
+static void _dpoe_sec_fsm_link_auth_complete(dpoe_sec_link_rec* link_rec, bcmos_errno status)
+{
+ dpoe_sec_fsm_event_data evt;
+
+ evt.type = DPOE_SEC_FSM_EVENT_AUTH_COMPLETE;
+ evt.data.sub_status = status;
+
+ _dpoe_sec_fsm_exec(link_rec, &evt);
+}
+
+static void _dpoe_sec_fsm_link_mka_complete(dpoe_sec_link_rec* link_rec, bcmos_errno status)
+{
+ dpoe_sec_fsm_event_data evt;
+
+ evt.type = DPOE_SEC_FSM_EVENT_MKA_COMPLETE;
+ evt.data.sub_status = status;
+
+ _dpoe_sec_fsm_exec(link_rec, &evt);
+}
+
+static bcmos_errno _dpoe_sec_fsm_init_task(bcmolt_devid device)
+{
+ bcmos_task_parm task_params =
+ {
+ .name = ctxt[device].task_name,
+ .priority = TASK_PRIORITY_USER_APPL_DPOE_SEC,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+ snprintf(ctxt[device].task_name, sizeof(ctxt[device].task_name), "user_appl_dpoe_sec%u", device);
+ bcmos_errno rc = bcmos_task_create(&ctxt[device].task, &task_params);
+
+ return rc;
+}
+
+
+static bcmos_errno _dpoe_sec_fsm_init_module(bcmolt_devid device)
+{
+ bcmos_module_parm module_params =
+ {
+ .qparm = { .name = ctxt[device].msg_queue_name, .size = DPOE_SEC_FSM_TASK_MSG_Q_SIZE },
+ .init = NULL
+ };
+ snprintf(ctxt[device].msg_queue_name, sizeof(ctxt[device].msg_queue_name), "dpoe_sec_msg_q%u", device);
+ return bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + device, &ctxt[device].task, &module_params);
+}
+
+static bcmos_errno _dpoe_sec_fsm_send_os_msg(
+ bcmolt_devid device,
+ bcmolt_epon_link_key link_key,
+ bcmos_msg_id type,
+ dpoe_sec_fsm_msg_data *data)
+{
+ bcmos_errno rc;
+ dpoe_sec_fsm_msg *msg = bcmos_calloc(sizeof(*msg));
+ if (msg == NULL)
+ {
+ BCM_LOG(ERROR, dpoe_sec_log_id[device], "OS Message calloc failed\n");
+ return BCM_ERR_NOMEM;
+ }
+
+ msg->os_msg.instance = device;
+ msg->os_msg.type = type;
+ msg->link = link_key;
+ msg->data = *data;
+ rc = bcmos_msg_dispatch(&msg->os_msg, BCMOS_MSG_SEND_AUTO_FREE);
+ if (BCM_ERR_OK != rc)
+ {
+ BCM_LOG(ERROR, dpoe_sec_log_id[device], "OS Message send failed (%s)\n", bcmos_strerror(rc));
+ }
+
+ return rc;
+}
+
+bcmos_errno dpoe_sec_fsm_link_start(bcmolt_devid device, bcmolt_epon_link_key link_key, dpoe_sec_fsm_start_data *cfg)
+{
+ dpoe_sec_fsm_msg_data data;
+
+ data.start = *cfg;
+
+ return _dpoe_sec_fsm_send_os_msg(device, link_key, BCMOS_MSG_ID_DPOE_SEC_START, &data);
+}
+
+bcmos_errno dpoe_sec_fsm_link_rx_oam(bcmolt_devid device, bcmolt_epon_link_key link_key, uint8_t *oam, uint16_t length)
+{
+ dpoe_sec_fsm_msg_data data;
+
+ data.rx_frame.frame = oam;
+ data.rx_frame.length = length;
+
+ return _dpoe_sec_fsm_send_os_msg(device, link_key, BCMOS_MSG_ID_DPOE_SEC_RX_OAM, &data);
+}
+
+bcmos_errno dpoe_sec_fsm_link_rx_eapol(
+ bcmolt_devid device,
+ bcmolt_epon_link_key link_key,
+ uint8_t *eapol,
+ uint16_t length)
+{
+ dpoe_sec_fsm_msg_data data;
+
+ data.rx_frame.frame = eapol;
+ data.rx_frame.length = length;
+
+ return _dpoe_sec_fsm_send_os_msg(device, link_key, BCMOS_MSG_ID_DPOE_SEC_RX_EAPOL, &data);
+}
+
+bcmos_errno dpoe_sec_fsm_init(
+ f_dpoe_sec_cert_trust_cb cert_trust_cb,
+ f_dpoe_sec_fsm_cb fsm_complete_cb)
+{
+ bcmos_errno rc;
+
+ for (uint32_t device = 0; device < BCMTR_MAX_OLTS; device++)
+ {
+ rc = _dpoe_sec_fsm_init_task(device);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ rc = _dpoe_sec_fsm_init_module(device);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ char log_name[MAX_DEV_LOG_ID_NAME];
+ snprintf(log_name, sizeof(log_name), "user_appl_dpoe_sec%u", device);
+ dpoe_sec_log_id[device] = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
+
+ bcmos_msg_register(
+ BCMOS_MSG_ID_DPOE_SEC_START,
+ device,
+ BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + device,
+ _dpoe_sec_fsm_link_start_handle);
+ bcmos_msg_register(
+ BCMOS_MSG_ID_DPOE_SEC_RX_OAM,
+ device,
+ BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + device,
+ _dpoe_sec_fsm_link_rx_oam_handle);
+ bcmos_msg_register(
+ BCMOS_MSG_ID_DPOE_SEC_RX_EAPOL,
+ device,
+ BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + device,
+ _dpoe_sec_fsm_link_rx_eapol_handle);
+
+ ctxt[device].link_map = hash_table_create(
+ DPOE_SEC_FSM_MAX_LINKS,
+ sizeof(dpoe_sec_link_rec),
+ sizeof(bcmolt_epon_link_key),
+ "dpoe_sec_link_state");
+ BUG_ON(ctxt[device].link_map == NULL);
+ }
+
+ _fsm_complete = fsm_complete_cb;
+
+ dpoe_eap_tls_init(_dpoe_sec_fsm_link_auth_complete, cert_trust_cb);
+ dpoe_sec_mka_fsm_init(_dpoe_sec_fsm_link_mka_complete);
+
+ return rc;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_fsm.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_fsm.h
new file mode 100644
index 0000000..fa8b3ad
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_fsm.h
@@ -0,0 +1,130 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _DPOE_SEC_FSM_H_
+#define _DPOE_SEC_FSM_H_
+
+#include "bcmos_system.h"
+#include "bcmolt_model_types.h"
+#include "bcmolt_epon_oam_types.h"
+#include "dpoe_eap_tls.h"
+#include "dpoe_sec_mka_fsm.h"
+
+/* DPoE Security FSM Event Type definitions */
+typedef enum dpoe_sec_fsm_event
+{
+ DPOE_SEC_FSM_EVENT__FIRST,
+ DPOE_SEC_FSM_EVENT_ENCRYPT_START = DPOE_SEC_FSM_EVENT__FIRST,
+ DPOE_SEC_FSM_EVENT_AUTH_START,
+ DPOE_SEC_FSM_EVENT_RX_OAM,
+ DPOE_SEC_FSM_EVENT_RX_EAPOL,
+ DPOE_SEC_FSM_EVENT_AUTH_COMPLETE,
+ DPOE_SEC_FSM_EVENT_MKA_COMPLETE,
+ DPOE_SEC_FSM_EVENT_TIMEOUT,
+
+ DPOE_SEC_FSM_EVENT__COUNT
+} dpoe_sec_fsm_event;
+
+/* DPoE Security FSM State definitions */
+typedef enum dpoe_sec_fsm_state
+{
+ DPOE_SEC_FSM_STATE__FIRST,
+ DPOE_SEC_FSM_STATE_IDLE = DPOE_SEC_FSM_STATE__FIRST,
+ DPOE_SEC_FSM_STATE_AUTH_WAIT,
+ DPOE_SEC_FSM_STATE_ENCRYPT_DOWN_WAIT,
+ DPOE_SEC_FSM_STATE_ENCRYPT_BI_WAIT,
+ DPOE_SEC_FSM_STATE_COMPLETE,
+
+ DPOE_SEC_FSM_STATE__COUNT
+} dpoe_sec_fsm_state;
+
+/* DPoE Security FSM Event contents. This is a union of information required by the DPoE Security FSM. This data type
+ * permits a common function prototype for the state machine function table. */
+typedef struct dpoe_sec_fsm_event_data
+{
+ dpoe_sec_fsm_event type;
+ union
+ {
+ bcmolt_u8_list_u16 rx_frame;
+ bcmos_errno sub_status;
+ } data;
+} dpoe_sec_fsm_event_data;
+
+struct dpoe_sec_link_rec;
+
+typedef struct
+{
+ bcmolt_epon_oam_dpoe_encryption_mode enc_mode;
+ bcmos_bool auth;
+} dpoe_sec_fsm_start_data;
+
+typedef struct dpoe_sec_link_rec
+{
+ bcmolt_devid device;
+ bcmolt_epon_link_key key;
+ dpoe_sec_fsm_start_data cfg;
+ bcmos_mac_address ni_mac;
+ bcmolt_epon_llid llid;
+ dpoe_sec_fsm_state fsm_state;
+ bcmos_errno status;
+ bcmos_timer timeout;
+ onu_auth_control auth_ctrl;
+ dpoe_sec_mka_fsm_ctrl mka_ctrl;
+} dpoe_sec_link_rec;
+
+typedef struct
+{
+ uint8_t *frame;
+ uint16_t length;
+} dpoe_sec_fsm_rx_frame_data;
+
+typedef union
+{
+ dpoe_sec_fsm_start_data start;
+ dpoe_sec_fsm_rx_frame_data rx_frame;
+} dpoe_sec_fsm_msg_data;
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ bcmolt_epon_link_key link;
+ dpoe_sec_fsm_msg_data data;
+} dpoe_sec_fsm_msg;
+
+typedef void (*f_dpoe_sec_fsm_cb)(bcmolt_devid, bcmolt_epon_link_key, bcmos_errno);
+
+bcmos_errno dpoe_sec_fsm_link_start(bcmolt_devid device, bcmolt_epon_link_key link_key, dpoe_sec_fsm_start_data *cfg);
+
+bcmos_errno dpoe_sec_fsm_link_rx_oam(bcmolt_devid device, bcmolt_epon_link_key link, uint8_t *oam, uint16_t length);
+
+bcmos_errno dpoe_sec_fsm_link_rx_eapol(bcmolt_devid device, bcmolt_epon_link_key link, uint8_t *eapol, uint16_t length);
+
+bcmos_errno dpoe_sec_fsm_init(f_dpoe_sec_cert_trust_cb cert_trust_cb, f_dpoe_sec_fsm_cb fsm_complete_cb);
+
+#endif /* _DPOE_SEC_FSM_H_ */
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_mka_fsm.c b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_mka_fsm.c
new file mode 100644
index 0000000..3174d63
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_mka_fsm.c
@@ -0,0 +1,830 @@
+/*
+ <: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 "dpoe_sec_fsm.h"
+#include "dpoe_sec_mka_fsm.h"
+#include "bcmolt_user_appl_epon_oam.h"
+#include "dpoe_sec_util.h"
+
+typedef enum dpoe_sec_mka_fsm_event_type
+{
+ DPOE_SEC_MKA_FSM_EVENT__INVALID = -1, /**< permits event validation */
+
+ DPOE_SEC_MKA_FSM_EVENT_INIT = 0,
+ DPOE_SEC_MKA_FSM_EVENT_RX_OAM ,
+ DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL ,
+ DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT ,
+ DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT ,
+ DPOE_SEC_MKA_FSM_EVENT_STOP ,
+
+ DPOE_SEC_MKA_FSM_EVENT__COUNT
+} dpoe_sec_mka_fsm_event_type;
+
+/* MKA FSM Event contents. This is a union of information required by the MKA FSM. This data type permits a common
+ * function prototype for the state machine function table. */
+typedef struct dpoe_sec_mka_fsm_event
+{
+ dpoe_sec_mka_fsm_event_type type; /**< Type of event*/
+ union
+ {
+ bcmolt_u8_list_u16 rx_frame;
+ } data;
+} dpoe_sec_mka_fsm_event;
+
+typedef bcmos_errno (*f_dpoe_sec_mka_fsm)(dpoe_sec_link_rec *mka_ctrl, dpoe_sec_mka_fsm_event *evt);
+
+static const char *dpoe_sec_mka_fsm_event_name[DPOE_SEC_MKA_FSM_EVENT__COUNT] =
+{
+ [DPOE_SEC_MKA_FSM_EVENT_INIT] = "Initialization",
+ [DPOE_SEC_MKA_FSM_EVENT_RX_OAM] = "OAM Response",
+ [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = "RX EAPOL",
+ [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = "FSM Timeout",
+ [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = "MKA Timeout",
+ [DPOE_SEC_MKA_FSM_EVENT_STOP] = "Stop"
+};
+
+static const char *dpoe_sec_mka_fsm_state_name[DPOE_SEC_MKA_FSM_STATE__COUNT] =
+{
+ [DPOE_SEC_MKA_FSM_STATE_NULL] = "Null",
+ [DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION] = "Set Encryption",
+ [DPOE_SEC_MKA_FSM_STATE_START_MKA ] = "Start MKA",
+ [DPOE_SEC_MKA_FSM_STATE_SEND_SAK] = "Send SAK",
+ [DPOE_SEC_MKA_FSM_STATE_OPERATIONAL] = "Operational",
+ [DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK] = "Send New SAK"
+};
+
+static f_dpoe_sec_mka_cb _mka_done_cb;
+
+static bcmos_errno _dpoe_sec_mka_fsm_exec(dpoe_sec_link_rec *link, dpoe_sec_mka_fsm_event *evt);
+
+static bcmos_bool _dpoe_sec_mka_fsm_event_type_is_valid(dpoe_sec_mka_fsm_event_type type)
+{
+ return (type > DPOE_SEC_MKA_FSM_EVENT__INVALID) && (type < DPOE_SEC_MKA_FSM_EVENT__COUNT);
+}
+
+static bcmos_bool _dpoe_sec_mka_fsm_state_is_valid(dpoe_sec_mka_fsm_state state)
+{
+ return (state > DPOE_SEC_MKA_FSM_STATE__INVALID) && (state < DPOE_SEC_MKA_FSM_STATE__COUNT);
+}
+
+static const char *_dpoe_sec_mka_fsm_event_name(dpoe_sec_mka_fsm_event_type type)
+{
+ if (_dpoe_sec_mka_fsm_event_type_is_valid(type))
+ {
+ return dpoe_sec_mka_fsm_event_name[type];
+ }
+ else
+ {
+ return "Unknown Event";
+ }
+}
+
+static const char *_dpoe_sec_mka_fsm_state_name(dpoe_sec_mka_fsm_state state)
+{
+ if (_dpoe_sec_mka_fsm_state_is_valid(state))
+ {
+ return dpoe_sec_mka_fsm_state_name[state];
+ }
+ else
+ {
+ return "Unknown State";
+ }
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_error(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ /* TODO: log unexpected event */
+
+ return BCM_ERR_OK;
+}
+
+static void _dpoe_sec_mka_fsm_cleanup(dpoe_sec_mka_fsm_ctrl *mka_ctrl)
+{
+ /* Parameter checks. */
+ BUG_ON(mka_ctrl == NULL);
+
+ if (mka_ctrl->mka_info != NULL)
+ {
+ /* Clear all MKA information before freeing. */
+ memset(mka_ctrl->mka_info, 0, sizeof(*mka_ctrl->mka_info));
+ bcmos_free(mka_ctrl->mka_info);
+ mka_ctrl->mka_info = NULL;
+ }
+
+ if (mka_ctrl->mka_fsm_info != NULL)
+ {
+ /* Clear all MKA FSM information before freeing. */
+ memset(mka_ctrl->mka_fsm_info, 0, sizeof(*mka_ctrl->mka_fsm_info));
+ bcmos_free(mka_ctrl->mka_fsm_info);
+ mka_ctrl->mka_fsm_info = NULL;
+ }
+
+ mka_ctrl->mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_NULL;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_stop(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ /* The stop function is called each time the Link deregisters regardless of whether the MKA FSM is running or not.
+ Handle the case where it is not running. */
+ if (link_rec->mka_ctrl.mka_fsm_info == NULL)
+ {
+ return BCM_ERR_OK;
+ }
+
+ /* Stop any timers that may be running. */
+ bcmos_timer_stop(&link_rec->mka_ctrl.mka_fsm_info->rx_timer);
+ bcmos_timer_stop(&link_rec->mka_ctrl.mka_fsm_info->tx_timer);
+
+ /* Change to the NULL state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_NULL;
+
+ /* Free dynamic MKA resources associated with the link. */
+ _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_timer_rc _dpoe_sec_mka_fsm_rx_timer_expire(bcmos_timer *timer, long data)
+{
+ dpoe_sec_link_rec *link_rec = (dpoe_sec_link_rec*)data;
+ dpoe_sec_mka_fsm_event evnt = {};
+
+ /* Parameter Checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+
+ /* Set up the event */
+ evnt.type = DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT;
+
+ /* Execute the state machine */
+ _dpoe_sec_mka_fsm_exec(link_rec, &evnt);
+
+ return BCMOS_TIMER_OK;
+}
+
+static bcmos_timer_rc _dpoe_sec_mka_fsm_tx_timer_expire(bcmos_timer *timer, long data)
+{
+ dpoe_sec_link_rec *link_rec = (dpoe_sec_link_rec*)data;
+ dpoe_sec_mka_fsm_event evnt = {};
+
+ /* Parameter Checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+
+ /* Set up the event */
+ evnt.type = DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT;
+
+ /* Execute the state machine */
+ _dpoe_sec_mka_fsm_exec(link_rec, &evnt);
+
+ return BCMOS_TIMER_OK;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_initialize(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno err;
+ bcmos_timer_parm rx_tp =
+ {
+ .name = "dpoe_sec_mka_fsm_timer",
+ .owner = BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + link_rec->device,
+ .periodic = BCMOS_FALSE,
+ .handler = _dpoe_sec_mka_fsm_rx_timer_expire,
+ .data = (long)link_rec,
+ .flags = BCMOS_TIMER_PARM_FLAGS_NON_URGENT
+ };
+ bcmos_timer_parm tx_tp =
+ {
+ .name = "dpoe_sec_mka_mka_timer",
+ .owner = BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + link_rec->device,
+ .periodic = BCMOS_TRUE,
+ .handler = _dpoe_sec_mka_fsm_tx_timer_expire,
+ .data = (long)link_rec,
+ .flags = BCMOS_TIMER_PARM_FLAGS_NON_URGENT
+ };
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ /* Allocate the MKA information structure. */
+ link_rec->mka_ctrl.mka_info = bcmos_calloc(sizeof(*link_rec->mka_ctrl.mka_info));
+ if (link_rec->mka_ctrl.mka_info == NULL)
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ /* Allocate the MKA FSM information structure. */
+ link_rec->mka_ctrl.mka_fsm_info = bcmos_calloc(sizeof(*link_rec->mka_ctrl.mka_fsm_info));
+ if (link_rec->mka_ctrl.mka_fsm_info == NULL)
+ {
+ _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl);
+ return BCM_ERR_NOMEM;
+ }
+
+ err = bcmos_timer_create(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, &rx_tp);
+ if (BCM_ERR_OK != err)
+ {
+ _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl);
+ return err;
+ }
+
+ err = bcmos_timer_create(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, &tx_tp);
+ if (BCM_ERR_OK != err)
+ {
+ _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl);
+ return err;
+ }
+
+ /* Change to the SET_LINK_ENCRYPTION_MODE state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION;
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, DPOE_SEC_OAM_TIMEOUT);
+
+ return epon_oam_dpoe_set_encryption(
+ link_rec->device,
+ link_rec->key.epon_ni,
+ &link_rec->key.mac_address,
+ link_rec->cfg.enc_mode,
+ 0);
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_send_success(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(_mka_done_cb == NULL);
+
+ _mka_done_cb(link_rec, BCM_ERR_OK);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_send_failure(dpoe_sec_link_rec *link_rec, bcmos_errno reason)
+{
+ dpoe_sec_mka_fsm_event event = { .type = DPOE_SEC_MKA_FSM_EVENT_STOP };
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(_mka_done_cb == NULL);
+
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "MKA stopping!\n");
+
+ /* Notify the DPoE Security FSM of the MKA error. */
+ _mka_done_cb(link_rec, reason);
+
+ /* Stop the FSM due to the error. */
+ return _dpoe_sec_mka_fsm_stop(link_rec, &event);
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_rx_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "TIMEOUT error!\n");
+
+ /* Notify the DPoE Security FSM of the MKA error. */
+ return _dpoe_sec_mka_fsm_send_failure(link_rec, BCM_ERR_TIMEOUT);
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_rx_oam(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ bcmolt_epon_oam_ethernet_frame *oam_frame;
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+ BUG_ON(evt->data.rx_frame.val == NULL);
+
+ oam_frame = epon_oam_unpack(link_rec->device, evt->data.rx_frame.len, evt->data.rx_frame.val);
+ if (NULL == oam_frame)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "OAM unpack failed!\n");
+ return BCM_ERR_OK;
+ }
+
+ if (!epon_oam_is_dpoe(oam_frame))
+ {
+ bcmos_free(oam_frame);
+ return BCM_ERR_OK;
+ }
+
+ dpoe = &oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value;
+
+ if (epon_oam_is_dpoe_encrypt_response(dpoe))
+ {
+ if ((dpoe->u.set_response.vars[0].u.extended_attribute.attribute.width == 0x80) &&
+ (dpoe->u.set_response.vars[1].u.extended_attribute.attribute.width == 0x80))
+ {
+ bcmos_timer_stop(&link_rec->mka_ctrl.mka_fsm_info->rx_timer);
+
+ /* Change to the START_MKA state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_START_MKA;
+
+ /* Send the initial MKA packet to the ONU for the specified link. */
+ rc = mka_start(link_rec);
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "ONU rejected encryption: [0] = 0x%x, [1] = 0x%x\n",
+ dpoe->u.set_response.vars[0].u.extended_attribute.attribute.width,
+ dpoe->u.set_response.vars[1].u.extended_attribute.attribute.width);
+ /* Notify the DPoE Security FSM of the MKA error. */
+ rc = _dpoe_sec_mka_fsm_send_failure(link_rec, BCM_ERR_ONU_ERR_RESP);
+ }
+ }
+ else
+ {
+ /* ignore other OAM */
+ }
+
+ bcmos_free(oam_frame);
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_start_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ return mka_process_timeout(link_rec, MKA_OP_START_TIMEOUT);
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_start_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_START_RSP);
+ if (rc == BCM_ERR_OK)
+ {
+ bcmolt_encryption_information_container key_info;
+
+ /* Re-start the rx timer */
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000);
+
+ /* Generate the initial SAK. */
+ mka_generate_sak(link_rec, BCMOS_TRUE);
+
+ key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR;
+ memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key));
+ memcpy(key_info.u.ctr.sci, link_rec->mka_ctrl.mka_info->onu_sci, sizeof(key_info.u.ctr.sci));
+
+ /* Install the SAK as the US encryption key for this link. */
+ rc = bcmolt_epon_link_encryption_key_set(
+ link_rec->device,
+ &link_rec->key,
+ BCMOS_TRUE,
+ BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES,
+ (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2),
+ &key_info);
+ if (rc == BCM_ERR_OK)
+ {
+ rc = mka_send_sak(link_rec, link_rec->mka_ctrl.mka_info->sak);
+ if (rc == BCM_ERR_OK)
+ {
+ /* Change to the SEND_SAK state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_SEND_SAK;
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "send SAK failed!\n");
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "key set failed %d!\n", rc);
+ }
+ }
+ else
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "MKA process packet failed!\n");
+ }
+
+ if (rc != BCM_ERR_OK)
+ {
+ /* Notify the DPoE Security FSM of the MKA error. */
+ _dpoe_sec_mka_fsm_send_failure(link_rec, rc);
+ }
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_sak_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ return mka_process_timeout(link_rec, MKA_OP_SAK_TIMEOUT);
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_sak_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_SAK_RSP);
+ if (rc == BCM_ERR_OK)
+ {
+ bcmolt_encryption_information_container key_info;
+
+ /* Re-start the rx timer */
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000);
+
+ key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR;
+ memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key));
+ memcpy(key_info.u.ctr.sci, link_rec->ni_mac.u8, BCMOS_ETH_ALEN);
+ key_info.u.ctr.sci[6] = (link_rec->llid >> 8) & 0xff;
+ key_info.u.ctr.sci[7] = (link_rec->llid >> 0) & 0xff;
+
+ /* Install the SAK as the US and DS encryption key for this link. */
+ rc = bcmolt_epon_link_encryption_key_set(
+ link_rec->device,
+ &link_rec->key,
+ BCMOS_FALSE,
+ BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES,
+ (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2),
+ &key_info);
+ if (rc == BCM_ERR_OK)
+ {
+ rc = mka_send_sak_confirm(link_rec);
+ if (rc == BCM_ERR_OK)
+ {
+ /* Notify the DPoE Security FSM of the MKA result. */
+ rc = _dpoe_sec_mka_fsm_send_success(link_rec);
+
+ /* Change to the OPERATIONAL state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_OPERATIONAL;
+ }
+ }
+ }
+
+ if (rc != BCM_ERR_OK)
+ {
+ /* Notify the DPoE Security FSM of the MKA error. */
+ _dpoe_sec_mka_fsm_send_failure(link_rec, rc);
+ }
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_send_keep_alive(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ return mka_process_timeout(link_rec, MKA_OP_SEND_KEEP_ALIVE);
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_sak_refresh(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(evt == NULL);
+
+ bcmolt_encryption_information_container key_info;
+
+ /* Generate the new SAK. */
+ mka_generate_sak(link_rec, BCMOS_FALSE);
+
+ key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR;
+ memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key));
+ memcpy(key_info.u.ctr.sci, link_rec->mka_ctrl.mka_info->onu_sci, sizeof(key_info.u.ctr.sci));
+
+ /* Install the new SAK as the US encryption key for this link. */
+ rc = bcmolt_epon_link_encryption_key_set(
+ link_rec->device,
+ &link_rec->key,
+ BCMOS_TRUE,
+ BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES,
+ (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2),
+ &key_info);
+ if (rc == BCM_ERR_OK)
+ {
+ rc = mka_send_sak(link_rec, link_rec->mka_ctrl.mka_info->new_sak);
+ if (rc == BCM_ERR_OK)
+ {
+ /* Change to the SEND_SAK state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK;
+ }
+ }
+
+ if (BCM_ERR_OK != rc)
+ {
+ /* Notify the DPoE Security FSM of the MKA error. */
+ _dpoe_sec_mka_fsm_send_failure(link_rec, rc);
+ }
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_handle_keep_alive(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+ BUG_ON(evt == NULL);
+
+ /* Pass the keep alive message to the MKA proper code. It's okay to ignore the return status. Failure to maintain
+ the MKA connection through MKA keep alive messages will tear down the MKA connection. */
+ rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_KEEP_ALIVE);
+
+ /* Check if a SAK refresh is needed. */
+ if (rc == BCM_ERR_OK)
+ {
+ /* Re-start the rx timer */
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000);
+
+ if (link_rec->mka_ctrl.mka_info->sak_refresh_needed)
+ {
+ /* Start the SAK refresh process. */
+ _dpoe_sec_mka_fsm_sak_refresh(link_rec, evt);
+
+ /* Clear the SAK refresh flag since the SAK refresh process has been started. */
+ link_rec->mka_ctrl.mka_info->sak_refresh_needed = BCMOS_FALSE;
+ }
+ }
+
+ return rc;
+}
+
+static bcmos_errno _dpoe_sec_mka_fsm_new_sak_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+ BUG_ON(evt == NULL);
+
+ rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_SAK_RSP);
+ if (rc == BCM_ERR_OK)
+ {
+ /* Re-start the rx timer */
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000);
+
+ bcmolt_encryption_information_container key_info;
+
+ key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR;
+ memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key));
+ memcpy(key_info.u.ctr.sci, link_rec->ni_mac.u8, BCMOS_ETH_ALEN);
+ key_info.u.ctr.sci[6] = (link_rec->llid >> 8) & 0xff;
+ key_info.u.ctr.sci[7] = (link_rec->llid >> 0) & 0xff;
+
+ /* Install the new SAK as the US and DS encryption key for this link. */
+ rc = bcmolt_epon_link_encryption_key_set(
+ link_rec->device,
+ &link_rec->key,
+ BCMOS_FALSE,
+ BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES,
+ (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2),
+ &key_info);
+ if (rc == BCM_ERR_OK)
+ {
+ rc = mka_send_sak_confirm(link_rec);
+ if (rc == BCM_ERR_OK)
+ {
+ /* Update the current SAK with the new SAK */
+ memcpy(link_rec->mka_ctrl.mka_info->sak, link_rec->mka_ctrl.mka_info->new_sak,
+ sizeof(link_rec->mka_ctrl.mka_info->sak));
+
+ /* Change to the OPERATIONAL state */
+ link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_OPERATIONAL;
+ }
+ }
+ }
+
+ if (rc != BCM_ERR_OK)
+ {
+ /* Notify the DPoE Security FSM of the MKA error. */
+ _dpoe_sec_mka_fsm_send_failure(link_rec, rc);
+ }
+
+ return rc;
+}
+
+/** This is the ONU FSM Jump Table, indexed by STATE and EVENT. */
+static f_dpoe_sec_mka_fsm dpoe_sec_mka_fsm[DPOE_SEC_MKA_FSM_STATE__COUNT][DPOE_SEC_MKA_FSM_EVENT__COUNT] =
+{
+ [DPOE_SEC_MKA_FSM_STATE_NULL] =
+ {
+ [DPOE_SEC_MKA_FSM_EVENT_INIT] = _dpoe_sec_mka_fsm_initialize
+ },
+ [DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION] =
+ {
+ [DPOE_SEC_MKA_FSM_EVENT_RX_OAM] = _dpoe_sec_mka_fsm_rx_oam,
+ [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop,
+ },
+ [DPOE_SEC_MKA_FSM_STATE_START_MKA] =
+ {
+ [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_start_rx_eapol,
+ [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_start_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop
+ },
+ [DPOE_SEC_MKA_FSM_STATE_SEND_SAK] =
+ {
+ [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_sak_rx_eapol,
+ [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_sak_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop
+ },
+ [DPOE_SEC_MKA_FSM_STATE_OPERATIONAL] =
+ {
+ [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_handle_keep_alive,
+ [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_send_keep_alive,
+ [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop
+ },
+ [DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK] =
+ {
+ [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_new_sak_rx_eapol,
+ [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout,
+ [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_send_keep_alive,
+ [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop
+ }
+};
+
+static bcmos_errno _dpoe_sec_mka_fsm_exec(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt)
+{
+ bcmos_errno rc = BCM_ERR_PARM;
+
+ /* Parameter checks */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(evt == NULL);
+
+ if (_dpoe_sec_mka_fsm_event_type_is_valid(evt->type))
+ {
+ dpoe_sec_mka_fsm_state pre_state = link_rec->mka_ctrl.mka_fsm_state;
+
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "MKA: processing event %s in state %s\n",
+ _dpoe_sec_mka_fsm_event_name(evt->type), _dpoe_sec_mka_fsm_state_name(pre_state));
+
+ /* call the FSM */
+ if (NULL != dpoe_sec_mka_fsm[link_rec->mka_ctrl.mka_fsm_state][evt->type])
+ {
+ rc = dpoe_sec_mka_fsm[link_rec->mka_ctrl.mka_fsm_state][evt->type](link_rec, evt);
+ }
+ else
+ {
+ rc = _dpoe_sec_mka_fsm_error(link_rec, evt);
+ }
+
+ if (pre_state != link_rec->mka_ctrl.mka_fsm_state)
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "MKA: transitioning from %s to %s\n",
+ _dpoe_sec_mka_fsm_state_name(pre_state),
+ _dpoe_sec_mka_fsm_state_name(link_rec->mka_ctrl.mka_fsm_state));
+ }
+ }
+
+ return rc;
+}
+
+void dpoe_sec_mka_fsm_rx_oam(dpoe_sec_link_rec *link_rec, uint8_t *frame, uint16_t length)
+{
+ dpoe_sec_mka_fsm_event evnt = {};
+
+ BUG_ON(link_rec == NULL);
+
+ /* Send the OAM PDU to the state machine. */
+ evnt.type = DPOE_SEC_MKA_FSM_EVENT_RX_OAM;
+ evnt.data.rx_frame.val = frame;
+ evnt.data.rx_frame.len = length;
+
+ /* Execute the state machine */
+ _dpoe_sec_mka_fsm_exec(link_rec, &evnt);
+}
+
+void dpoe_sec_mka_fsm_rx_eapol(dpoe_sec_link_rec *link_rec, uint8_t *frame, uint16_t length)
+{
+ dpoe_sec_mka_fsm_event evnt = {};
+
+ BUG_ON(link_rec == NULL);
+
+ /* Send the OAM PDU to the state machine. */
+ evnt.type = DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL;
+ evnt.data.rx_frame.val = frame;
+ evnt.data.rx_frame.len = length;
+
+ /* Execute the state machine */
+ _dpoe_sec_mka_fsm_exec(link_rec, &evnt);
+}
+
+bcmos_errno dpoe_sec_mka_fsm_start(dpoe_sec_link_rec *link_rec)
+{
+ dpoe_sec_mka_fsm_event event = {};
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ /* Inject an INIT_EVENT into the FSM. */
+ event.type = DPOE_SEC_MKA_FSM_EVENT_INIT;
+
+ return _dpoe_sec_mka_fsm_exec(link_rec, &event);
+}
+
+bcmos_bool dpoe_sec_mka_fsm_running(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_bool rc = BCMOS_FALSE;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ if ((link_rec->mka_ctrl.mka_fsm_info != NULL) &&
+ (link_rec->mka_ctrl.mka_fsm_state > DPOE_SEC_MKA_FSM_STATE_NULL))
+ {
+ rc = BCMOS_TRUE;
+ }
+
+ return rc;
+}
+
+bcmos_errno dpoe_sec_mka_fsm_deregister(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ dpoe_sec_mka_fsm_event evnt = {};
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+
+ /* The deregister function is called each time the Link deregisters regardless of whether the MKA FSM is running or
+ not. Handle the case where it is not running. */
+ if (link_rec->mka_ctrl.mka_fsm_info != NULL)
+ {
+ /* Inject a stop into the MKA FSM. */
+ evnt.type = DPOE_SEC_MKA_FSM_EVENT_STOP;
+ rc = _dpoe_sec_mka_fsm_exec(link_rec, &evnt);
+ }
+
+ return rc;
+}
+
+void dpoe_sec_mka_fsm_init(f_dpoe_sec_mka_cb mka_cb)
+{
+ _mka_done_cb = mka_cb;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_mka_fsm.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_mka_fsm.h
new file mode 100644
index 0000000..f7209bb
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_mka_fsm.h
@@ -0,0 +1,90 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _DPOE_SEC_MKA_FSM_H_
+#define _DPOE_SEC_MKA_FSM_H_
+
+#include "bcmos_system.h"
+#include "dpoe_sec_util.h"
+#include "mka.h"
+
+/* --- Types --- */
+
+typedef enum dpoe_sec_mka_fsm_state
+{
+ DPOE_SEC_MKA_FSM_STATE__INVALID = -1, /**< permits event validation */
+
+ DPOE_SEC_MKA_FSM_STATE_NULL = 0,
+ DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION ,
+ DPOE_SEC_MKA_FSM_STATE_START_MKA ,
+ DPOE_SEC_MKA_FSM_STATE_SEND_SAK ,
+ DPOE_SEC_MKA_FSM_STATE_OPERATIONAL ,
+ DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK ,
+
+ DPOE_SEC_MKA_FSM_STATE__COUNT
+} dpoe_sec_mka_fsm_state;
+
+/* DPoE Security MKA information stored with each link. */
+typedef struct dpoe_sec_mka_fsm_info
+{
+ bcmos_timer rx_timer; /**< Timer entry for MKA FSM */
+ bcmos_timer tx_timer; /**< Timer entry for MKA messages */
+} dpoe_sec_mka_fsm_info;
+
+typedef struct
+{
+ mka_link_info *mka_info; /**< MKA information */
+ dpoe_sec_mka_fsm_info *mka_fsm_info; /**< MKA FSM information */
+ dpoe_sec_mka_fsm_state mka_fsm_state; /**< State of the DPoE Security MKA FSM */
+} dpoe_sec_mka_fsm_ctrl;
+
+struct dpoe_sec_link_rec;
+
+typedef void (*f_dpoe_sec_mka_cb)(struct dpoe_sec_link_rec*, bcmos_errno status);
+
+/* --- Function prototypes --- */
+
+/* Start MKA on a link */
+bcmos_errno dpoe_sec_mka_fsm_start(struct dpoe_sec_link_rec *link);
+
+/* Handle reception of an OAM frame */
+void dpoe_sec_mka_fsm_rx_oam(struct dpoe_sec_link_rec *link, uint8_t *frame, uint16_t length);
+
+/* Handle reception of an EAPOL frame */
+void dpoe_sec_mka_fsm_rx_eapol(struct dpoe_sec_link_rec *link, uint8_t *frame, uint16_t length);
+
+/* Is the MKA FSM running on a link? */
+bcmos_bool dpoe_sec_mka_fsm_running(struct dpoe_sec_link_rec *link);
+
+/* Handle link de-registration (stop the MKA FSM) */
+bcmos_errno dpoe_sec_mka_fsm_deregister(struct dpoe_sec_link_rec *link);
+
+void dpoe_sec_mka_fsm_init(f_dpoe_sec_mka_cb mka_cb);
+
+#endif /* _DPOE_SEC_MKA_FSM_H_ */
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_user_cfg.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_user_cfg.h
new file mode 100644
index 0000000..e06d3e9
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_user_cfg.h
@@ -0,0 +1,200 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _DPOE_SEC_USER_CFG_H_
+#define _DPOE_SEC_USER_CFG_H_
+
+#include "bcm_dev_log.h"
+
+extern dev_log_id dpoe_sec_log_id[];
+
+typedef void* dpoe_sec_sha1_hash; /* A data type for holding an SHA1 hash context */
+typedef uint8_t dpoe_sec_sha1_digest[20]; /* A data type for holding an SHA1 hash */
+typedef void* dpoe_sec_md5_hash; /* A data type for holding an MD5 hash context */
+typedef uint8_t dpoe_sec_md5_digest[16]; /* A data type for holding an MD5 hash */
+typedef void* dpoe_sec_rsa_key; /* A data type for holding an RSA key */
+typedef void* dpoe_sec_aes_key; /* A data type for holding an AES key */
+
+static inline void dpoe_sec_sha1_init(dpoe_sec_sha1_hash *sha1)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should initialize/reset an SHA1 hash context\n");
+ BUG();
+}
+
+static inline void dpoe_sec_sha1_update(dpoe_sec_sha1_hash *sha1, const void* data, size_t len)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should update an SHA1 hash context\n");
+ BUG();
+}
+
+static inline void dpoe_sec_sha1_final(dpoe_sec_sha1_digest md, dpoe_sec_sha1_hash *sha1)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should finalize an SHA1 hash context\n");
+ BUG();
+}
+
+static inline void dpoe_sec_md5_init(dpoe_sec_md5_hash *md5)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should initialize/reset an MD5 hash context\n");
+ BUG();
+}
+
+static inline void dpoe_sec_md5_update(dpoe_sec_md5_hash *md5, const void* data, size_t len)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should update an MD5 hash context\n");
+ BUG();
+}
+
+static inline void dpoe_sec_md5_final(dpoe_sec_md5_digest md, dpoe_sec_md5_hash *md5)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should finalize an MD5 hash context\n");
+ BUG();
+}
+
+static inline void dpoe_sec_prf_expand_4346(
+ uint8_t *secret,
+ uint32_t secret_len,
+ const uint8_t *label,
+ uint32_t label_len,
+ const uint8_t *seed,
+ uint32_t seed_len,
+ uint8_t *output,
+ uint32_t output_len)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should perform the pseudo-random function for expansion of secrets as defined in RFC 4346, section 5\n");
+ BUG();
+}
+
+static inline dpoe_sec_rsa_key *dpoe_sec_rsa_generate_key(int bits)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should generate an RSA key\n");
+ BUG();
+}
+
+static inline void dpoe_sec_rsa_key_free(dpoe_sec_rsa_key *rsa)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should release any resources allocated for an RSA key by either dpoe_sec_rsa_generate_key or dpoe_sec_x509_pub_key_get\n");
+ BUG();
+}
+
+static inline bcmos_bool dpoe_sec_rsa_public_get(dpoe_sec_rsa_key *rsa, uint8_t *rsa_modulus, uint8_t *rsa_exp)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should retrieve the public modulus and exponent of an RSA key\n");
+ BUG();
+}
+
+static inline int dpoe_sec_rsa_size(dpoe_sec_rsa_key *rsa)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should return the size of an RSA key in bytes\n");
+ BUG();
+}
+
+static inline int dpoe_sec_rsa_private_decrypt(
+ int flen,
+ const uint8_t *from,
+ uint8_t *to,
+ dpoe_sec_rsa_key *rsa)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should perform RSA decryption using a private key\n");
+ BUG();
+}
+
+static inline int dpoe_sec_rsa_public_decrypt(
+ int flen,
+ const uint8_t *from,
+ uint8_t *to,
+ dpoe_sec_rsa_key *rsa)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should perform RSA decryption using a public key (also known as signature verification)\n");
+ BUG();
+}
+
+static inline dpoe_sec_rsa_key *dpoe_sec_x509_pub_key_get(const uint8_t *cert, uint32_t cert_len)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should retrieve an RSA public key from an X.509 cetificate\n");
+ BUG();
+}
+
+static inline void dpoe_sec_aes_set_encrypt_key(const uint8_t *user_key, const int bits, dpoe_sec_aes_key *key)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should initialize the AES key with the provided data\n");
+ BUG();
+}
+
+static inline void dpoe_sec_aes_wrap_key(
+ dpoe_sec_aes_key *key,
+ uint8_t *out,
+ const uint8_t *in,
+ uint32_t len)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should perform an AES key wrap on in\n");
+ BUG();
+}
+
+static inline void dpoe_sec_aes_cmac(uint8_t *key, uint8_t *msg, uint32_t len, uint8_t *code)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should perform AES CMAC\n");
+ BUG();
+}
+
+static inline void dpoe_sec_aes_cmac_kdf(
+ uint8_t *key,
+ const char *label,
+ uint8_t *ctx,
+ uint32_t ctx_len,
+ uint8_t *out_buf)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should perform the AES CMAC key derivation function\n");
+ BUG();
+}
+
+static inline bcmos_errno dpoe_sec_send_eapol(
+ bcmolt_devid device,
+ bcmolt_epon_link_key *link_key,
+ uint8_t *eapol,
+ uint16_t length)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should transmit an EAPOL frame to the specified link\n");
+ BUG();
+}
+
+static inline bcmos_errno dpoe_sec_rng_seed(void)
+{
+ BCM_LOG(INFO, dpoe_sec_log_id[current_device], "This function should seed a cryptographically valid pseudo-random number generator\n");
+ return BCM_ERR_OK;
+}
+
+static inline void dpoe_sec_generate_n_byte_random_number(uint8_t *output, int length)
+{
+ BCM_LOG(ERROR, dpoe_sec_log_id[current_device], "This function should generate a pseudo-random number of the specified length\n");
+ BUG();
+}
+
+#endif /* _DPOE_SEC_USER_CFG_H_ */
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_util.c b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_util.c
new file mode 100644
index 0000000..593047a
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_util.c
@@ -0,0 +1,77 @@
+/*
+ <: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 "dpoe_sec_util.h"
+#include "bcmolt_api.h"
+
+dev_log_id dpoe_sec_log_id[BCMTR_MAX_OLTS];
+
+bcmos_bool dpoe_sec_eapol_unpack(bcmolt_buf *buf, eapol_msg_hdr *eapol)
+{
+ return
+ bcmolt_buf_read_mac_address(buf, &eapol->da) &&
+ bcmolt_buf_read_mac_address(buf, &eapol->sa) &&
+ bcmolt_buf_read_u16_be(buf, &eapol->ether_type) &&
+ bcmolt_buf_read_u8(buf, &eapol->eapol_version) &&
+ bcmolt_buf_read_u8(buf, &eapol->eapol_packet_type) &&
+ bcmolt_buf_read_u16_be(buf, &eapol->eapol_length);
+}
+
+bcmos_errno bcmolt_epon_link_encryption_key_set(
+ bcmolt_devid device,
+ bcmolt_epon_link_key *link_key,
+ bcmos_bool up, /* TODO: create explicit up/down type for clarity */
+ bcmolt_epon_encryption_mode mode,
+ bcmolt_epon_key_choice key_choice,
+ bcmolt_encryption_information_container *key_info)
+{
+ bcmos_errno rc;
+ bcmolt_epon_link_cfg cfg;
+
+ BCMOLT_CFG_INIT(&cfg, epon_link, *link_key);
+ BCMOLT_CFG_PROP_GET(&cfg, epon_link, epon_encryption);
+ rc = bcmolt_cfg_get(device, &cfg.hdr);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+
+ if (up)
+ {
+ cfg.data.epon_encryption.upstream_mode = mode;
+ cfg.data.epon_encryption.upstream_key_choice = key_choice;
+ cfg.data.epon_encryption.upstream_encryption_information = *key_info;
+ }
+ else
+ {
+ cfg.data.epon_encryption.downstream_mode = mode;
+ cfg.data.epon_encryption.downstream_key_choice = key_choice;
+ cfg.data.epon_encryption.downstream_encryption_information = *key_info;
+ }
+
+ return bcmolt_cfg_set(device, &cfg.hdr);
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_util.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_util.h
new file mode 100644
index 0000000..b42fddc
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/dpoe_sec_util.h
@@ -0,0 +1,86 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _DPOE_SEC_UTIL_H_
+#define _DPOE_SEC_UTIL_H_
+
+#include "bcmos_system.h"
+#include "bcm_dev_log.h"
+#include "bcmolt_buf.h"
+#include "bcmolt_utils.h"
+#include "bcmolt_model_types.h"
+#include "dpoe_sec_user_cfg.h"
+
+/* the EAPOL Ethernet type */
+#define ETHERTYPE_EAPOL 0x888e
+
+#define EAPOL_PROTOCOL_VERSION_DPOE 3
+
+typedef enum
+{
+ EAPOL_TYPE_PACKET,
+ EAPOL_TYPE_START,
+ EAPOL_TYPE_LOG_OFF,
+ EAPOL_TYPE_KEY,
+ EAPOL_TYPE_ASF_ALERT,
+ EAPOL_TYPE_MKA
+} eapol_type;
+
+typedef struct
+{
+ bcmos_mac_address da;
+ bcmos_mac_address sa;
+ uint16_t ether_type;
+ uint8_t eapol_version;
+ uint8_t eapol_packet_type;
+ uint16_t eapol_length;
+} eapol_msg_hdr;
+
+#define EAPOL_MSG_HDR_SIZE 18
+
+#define LINK_KEY_FMT_STR "PON %u, "BCMOS_MACADDR_FMT_STR
+#define LINK_KEY_DATA(lr) (lr)->key.epon_ni, BCMOS_MACADDR_PARAMS(&(lr)->key.mac_address)
+
+#define DPOE_SEC_LINK_LOG(level, lr, fmt, args...) \
+ BCM_LOG(level, dpoe_sec_log_id[(lr)->device], LINK_KEY_FMT_STR": "fmt, LINK_KEY_DATA(lr), ##args)
+
+static const uint32_t DPOE_SEC_OAM_TIMEOUT = 2 * 1000 * 1000; /* 2 seconds */
+
+bcmos_bool dpoe_sec_eapol_unpack(bcmolt_buf *buf, eapol_msg_hdr *eapol);
+
+bcmos_errno bcmolt_epon_link_encryption_key_set(
+ bcmolt_devid device,
+ bcmolt_epon_link_key *link_key,
+ bcmos_bool up,
+ bcmolt_epon_encryption_mode mode,
+ bcmolt_epon_key_choice key_choice,
+ bcmolt_encryption_information_container *key_info);
+
+#endif /* _DPOE_SEC_UTIL_H_ */
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/mka.c b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/mka.c
new file mode 100644
index 0000000..999eb23
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/mka.c
@@ -0,0 +1,1434 @@
+/*
+ <: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.
+
+ :>
+*/
+
+/** @file mka.c
+ * @brief Publicly accessible APIs provided to process MKA request/responses.
+ *
+ * This file contains all of the public/private APIs used to process MKA frames.
+ *
+ * MKA Exchange Sequence :
+ *
+ * 1. If bidirectional encryption is enabled, the OLT will generate an MKPDU
+ * containing the basic parameter set and empty live and potential peer
+ * lists.
+ * 2. When the ONU receives this, it will add the OLT to its potential peers
+ * list.
+ * 3. Having discovered a new potential peer, the ONU will transmit an MKPDU
+ * containing the basic parameter set, and empty live peers list, and a
+ * potential peer list containing the OLT.
+ * 4. When the OLT receives this, it will add the ONU to its live peers list.
+ * 5. Having added a new live peer, the OLT will generate a new SAK and install
+ * it for receive (using the ONUs SCI).
+ * 6. The OLT then transmits an MKPDU containing the basic parameter set, a
+ * live peer list containing the ONU, an empty potential peer list, a
+ * distributed SAK, and a SAK usage parameter set indicating that the new
+ * SAK has been installed for receive.
+ * 7. When the ONU receives this, it will:
+ * a. Add the OLT to its live peers list
+ * b. Install the new SAK for receive (using the OLTs SCI)
+ * c. Because the OLT reported the SAK in use for receive, the ONU will
+ * install the SAK for transmit (using the ONUs SCI).
+ * 8. The ONU will then transmit an MKPDU containing the basic parameter set,
+ * a live peers list containing the OLT, an empty potential peer list,
+ * and a SAK usage parameter set indicating that the SAK has been installed
+ * for receive and transmit.
+ * 9. When the OLT receives this, it will see that the ONU has installed the
+ * SAK for receive and will install the SAK for transmit
+ * (using the OLTs SCI).
+ * 10. The OLT will then transmit an MKPDU containing the basic parameter set,
+ * a live peer list containing the ONU, an empty potential peer list and
+ * a SAK usage parameter set indicating the SAK has been installed for
+ * receive and transmit. (I¡®m not sure if this step is necessary)
+ * 11. This message will produce no state change at the ONU, so it will not
+ * respond immediately. (both the ONU and OLT will need to start a 2 second
+ * timer every time an MKPDU is transmitted ? if they have not transmitted
+ * an MKPDU due to a state change when the timer expires, they will
+ * transmit an MKPDU containing the basic parameter set and live and
+ * potential peer lists. If either side does not receive an MKPDU from
+ * the other for 6 seconds they must remove them from their peer lists.)
+ * 12. When the OLT determines that a new SAK is needed this protocol will
+ * repeat steps 5-11. (modifications to the peer lists, such as 7a, should
+ * not be required)
+ *
+ * @defgroup mka MKA
+ * @ingroup cmCtrl
+ *
+ */
+
+#include "mka.h"
+#include "dpoe_sec_util.h"
+#include "dpoe_sec_fsm.h"
+
+/* The destination multicast MAC address for MKA packets. */
+static const bcmos_mac_address mka_dst_mac_addr = { .u8 = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 } };
+
+#if 0
+static const char *mka_state_names[MKA_STATE__COUNT] =
+{
+ "Initial",
+ "WaitingInitialPeerResp",
+ "SakSent",
+ "MkaDone"
+};
+#endif
+
+#define MKA_VERSION 1
+#define MKA_SERVER_PRIORITY 0x30
+#define MKA_INIT_RETRY_CNT 3
+#define MKA_BASIC_PARAM_SET_LEN 44 /* 48 - header len (4 byte) */
+#define MKA_BASIC_PARAM_SET_OPT 0x60 /* KeyServer:0, MACsecDesired:1, MACsec Capability:2 & high 4 bits of
+ Parameter set body length (0) */
+
+#define MKA_ALGORITHM_AGILITY 0x0080c201
+#define MKA_MN_LEN 4 /* Message Number */
+#define MKA_KN_LEN 4 /* Key Number */
+#define MKA_PDU_MAX_FRAME_LEN 300
+#define MKA_HELLO_TIME 2000 /* Ms, 2 seconds */
+#define MKA_MAX_HELLO_RETRIES 3 /* Max Hello retries */
+#define MKA_OLT_INIT_FRAME_LEN 76 /* basic param set(48) + null live peer list (4) + null potential peer list (4) +
+ Icv Indicator (4) + ICV(16) */
+#define MKA_ONE_PEER_LEN 16 /* one live peer len */
+#define MKA_DISTRIBUTED_SAK_LEN 32
+#define MKA_ICV_LEN 16
+#define MKA_PDU_MIN_LENGTH 32
+#define MKA_KEY_WRAPPED_SAK_LEN 24
+#define MKA_SAK_USE_PARAM_BODY_LEN 40
+#define MKA_SAK_USE_PARAM_LEM 44
+#define MKA_MAX_ACCEPTABLE_PN 0xC0000000
+
+#define MKA_SAK_USE_PARAM_AN_SHIFT 6
+#define MKA_SAK_USE_PARAM_LK_TX_SHIFT 5
+#define MKA_SAK_USE_PARAM_LK_RX_SHIFT 4
+#define MKA_SAK_USE_PARAM_OLD_KEY_AN_MASK 3
+#define MKA_SAK_USE_PARAM_OLD_KEY_AN_SFT 2
+#define MKA_SAK_USE_PARAM_OLD_KEY_TX_RX 3
+#define MKA_SAK_USE_PARAM_SECOND_OPT_BYTE 0xC0
+
+#define MKA_KEY_LEN 16 /* common key size */
+
+#define MKA_KEK_LABEL_LEN 12 /* label length */
+#define MKA_KEK_CONTEXT_LEN 16
+
+#define MKA_SAK_KEY_WRAPPED_LEN 24
+
+#define MKA_ICK_LABEL_LEN 12 /* label length */
+#define MKA_ICK_CONTEXT_LEN 16
+
+#define MKA_CKN_LABEL_LEN 16 /* label length */
+#define MKA_CKN_CONTEXT_LEN (SIZE_OF_EAP_SESSION_ID + (BCMOS_ETH_ALEN * 2)) /* 77 */
+
+#define MKA_CAK_LABEL_LEN 16 /* label length */
+#define MKA_CAK_CONTEXT_LEN 12 /* context length */
+
+/* MKPDU Parameter set type */
+typedef enum
+{
+ MKPDU_PARAM_SET_LIVE_PEER_LIST = 1,
+ MKPDU_PARAM_SET_POTENTIAL_PEER_LIST = 2,
+ MKPDU_PARAM_SET_MACSEC_SAK_USE = 3,
+ MKPDU_PARAM_SET_DIST_SAK = 4,
+ MKPDU_PARAM_SET_DIST_CAK = 5,
+ MKPDU_PARAM_SET_KMD = 6,
+ MKPDU_PARAM_SET_ANNOUNCEMENT = 7,
+ MKPDU_PARAM_SET_ICV_INDICATOR = 255,
+} mkpdu_param_set_type;
+
+typedef enum
+{
+ MKA_MSG_TIMEOUT,
+ MKA_MSG_DATA_IND
+} mka_msg_type;
+
+typedef struct
+{
+ mka_msg_type type;
+ eapol_msg_hdr *msg;
+ bcmolt_buf *buf;
+} mka_event;
+
+/* Calculate Key Encryption Key */
+static void _mka_calc_kek(uint8_t *kek, uint8_t *cak, uint8_t *ckn)
+{
+ char kek_label[MKA_KEK_LABEL_LEN + 1] = "IEEE8021 KEK";
+
+ /* Parameter checks. */
+ BUG_ON(kek == NULL);
+ BUG_ON(cak == NULL);
+ BUG_ON(ckn == NULL);
+
+ dpoe_sec_aes_cmac_kdf(cak, kek_label, ckn, MKA_KEK_CONTEXT_LEN, kek);
+}
+
+/* Calculate Integrity Check value Key */
+static void _mka_calc_ick(uint8_t *ick, uint8_t *cak, uint8_t *ckn)
+{
+ char ick_label[MKA_ICK_LABEL_LEN + 1] = "IEEE8021 ICK";
+
+ /* Parameter checks. */
+ BUG_ON(ick == NULL);
+ BUG_ON(cak == NULL);
+ BUG_ON(ckn == NULL);
+
+ dpoe_sec_aes_cmac_kdf(cak, ick_label, ckn, MKA_ICK_CONTEXT_LEN, ick);
+}
+
+/* calculate Integrity Check Value */
+static void _mka_calc_icv(uint8_t *icv, uint8_t *ick, uint8_t *msdu, uint16_t msdu_len)
+{
+ /* Parameter checks. */
+ BUG_ON(icv == NULL);
+ BUG_ON(ick == NULL);
+ BUG_ON(msdu == NULL);
+
+ dpoe_sec_aes_cmac(ick, msdu, msdu_len, icv);
+}
+
+/* Calculate Secure Connectivity Association Key Name */
+static void _mka_calc_ckn(dpoe_sec_link_rec *link_rec, uint8_t eap_sess_id_len)
+{
+ char ckn_label[MKA_CKN_LABEL_LEN + 1] = "IEEE8021 EAP CKN";
+ uint8_t ckn_context[MKA_CKN_CONTEXT_LEN];
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info->ckn == NULL);
+ BUG_ON(link_rec->auth_ctrl.trans_data.master_session_key == NULL);
+ BUG_ON(link_rec->auth_ctrl.trans_data.session_id == NULL);
+
+ memcpy(&ckn_context[0], link_rec->auth_ctrl.trans_data.session_id, eap_sess_id_len);
+ memcpy(&ckn_context[eap_sess_id_len], link_rec->mka_ctrl.mka_info->lesser_mac.u8, BCMOS_ETH_ALEN);
+ memcpy(&ckn_context[eap_sess_id_len + BCMOS_ETH_ALEN], link_rec->mka_ctrl.mka_info->greater_mac.u8, BCMOS_ETH_ALEN);
+ dpoe_sec_aes_cmac_kdf(link_rec->auth_ctrl.trans_data.master_session_key, ckn_label, ckn_context, MKA_CKN_CONTEXT_LEN, link_rec->mka_ctrl.mka_info->ckn);
+}
+
+/* Calculate Secure Connectivity Association Key */
+static void _mka_calc_cak(dpoe_sec_link_rec *link_rec)
+{
+ char cak_label[MKA_CAK_LABEL_LEN + 1] = "IEEE8021 EAP CAK";
+ uint8_t cak_context[MKA_CAK_CONTEXT_LEN];
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info->cak == NULL);
+ BUG_ON(link_rec->auth_ctrl.trans_data.master_session_key == NULL);
+
+ memcpy(&cak_context[0], link_rec->mka_ctrl.mka_info->lesser_mac.u8, BCMOS_ETH_ALEN);
+ memcpy(&cak_context[BCMOS_ETH_ALEN], link_rec->mka_ctrl.mka_info->greater_mac.u8, BCMOS_ETH_ALEN);
+ dpoe_sec_aes_cmac_kdf(link_rec->auth_ctrl.trans_data.master_session_key, cak_label, cak_context, MKA_CAK_CONTEXT_LEN, link_rec->mka_ctrl.mka_info->cak);
+}
+
+/* Get OLT port Mac and link Mac & compare. This is needed for calculating CAK */
+static void _mka_get_olt_and_link_mac(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ /* compare OLT and Link MAC addresses */
+ if (memcmp(link_rec->ni_mac.u8, link_rec->key.mac_address.u8, sizeof(BCMOS_ETH_ALEN)) >= 0)
+ {
+ link_rec->mka_ctrl.mka_info->lesser_mac = link_rec->key.mac_address;
+ link_rec->mka_ctrl.mka_info->greater_mac = link_rec->ni_mac;
+ }
+ else
+ {
+ link_rec->mka_ctrl.mka_info->lesser_mac = link_rec->ni_mac;
+ link_rec->mka_ctrl.mka_info->greater_mac = link_rec->key.mac_address;
+ }
+}
+
+/* Initialize link MKA info structure */
+static void _mka_init_link_info(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ /* initialize all local values */
+ memset(link_rec->mka_ctrl.mka_info, 0, sizeof(*link_rec->mka_ctrl.mka_info));
+ dpoe_sec_generate_n_byte_random_number(link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN);
+ link_rec->mka_ctrl.mka_info->curr_msg_num = 1; /* start from 1 */
+ link_rec->mka_ctrl.mka_info->key_number = 1; /* start from 1 */
+ _mka_get_olt_and_link_mac(link_rec);
+ _mka_calc_cak(link_rec);
+ _mka_calc_ckn(link_rec, SIZE_OF_EAP_SESSION_ID);
+ _mka_calc_ick(link_rec->mka_ctrl.mka_info->ick, link_rec->mka_ctrl.mka_info->cak, link_rec->mka_ctrl.mka_info->ckn);
+ _mka_calc_kek(link_rec->mka_ctrl.mka_info->kek, link_rec->mka_ctrl.mka_info->cak, link_rec->mka_ctrl.mka_info->ckn);
+}
+
+static void _mka_clear_link_info(dpoe_sec_link_rec *link_rec)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ memset(link_rec->mka_ctrl.mka_info, 0, sizeof(*link_rec->mka_ctrl.mka_info));
+}
+
+/* Build EAPOL header for MKPDU */
+static bcmos_bool _mka_build_eapol_header(dpoe_sec_link_rec *link_rec, bcmolt_buf *out_buf, uint16_t length)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(out_buf == NULL);
+
+ return
+ bcmolt_buf_write_mac_address(out_buf, mka_dst_mac_addr) &&
+ bcmolt_buf_write_mac_address(out_buf, link_rec->ni_mac) &&
+ bcmolt_buf_write_u16_be(out_buf, ETHERTYPE_EAPOL) &&
+ bcmolt_buf_write_u8(out_buf, EAPOL_PROTOCOL_VERSION_DPOE) &&
+ bcmolt_buf_write_u8(out_buf, EAPOL_TYPE_MKA) &&
+ bcmolt_buf_write_u16_be(out_buf, length);
+}
+
+/* Build Basic parameter set. This parameter set is required for every MKPDU */
+static bcmos_bool _mka_build_basic_parameter_set(dpoe_sec_link_rec *link_rec, bcmolt_buf *out_buf)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(out_buf == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ return
+ bcmolt_buf_write_u8(out_buf, MKA_VERSION) && /* Version Number, 802.1X Pg.98 */
+ bcmolt_buf_write_u8(out_buf, MKA_SERVER_PRIORITY) && /* Key Server Priority */
+ bcmolt_buf_write_u8(out_buf, MKA_BASIC_PARAM_SET_OPT) &&
+ bcmolt_buf_write_u8(out_buf, MKA_BASIC_PARAM_SET_LEN) &&
+ bcmolt_buf_write_mac_address(out_buf, link_rec->ni_mac) && /* 6 byte of SCI */
+ bcmolt_buf_write_u16_be(out_buf, link_rec->llid) && /* lower 2 byte SCI */
+ bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) &&
+ bcmolt_buf_write_u32_be(out_buf, link_rec->mka_ctrl.mka_info->curr_msg_num++) && /* Important !! */
+ bcmolt_buf_write_u32_be(out_buf, MKA_ALGORITHM_AGILITY) &&
+ bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->ckn, MKA_CKN_LEN / 8); /* CAK Name(CKN) */
+}
+
+/* Build an empty Live Peer or Potential Peer list parameter set */
+static bcmos_bool _mka_build_empty_peer_list(bcmolt_buf *out_buf, uint8_t type)
+{
+ /* Parameter checks. */
+ BUG_ON(out_buf == NULL);
+
+ return
+ bcmolt_buf_write_u8(out_buf, type) && /* Parameter set type */
+ bcmolt_buf_write_u8(out_buf, 0) &&
+ bcmolt_buf_write_u8(out_buf, 0) && /* options & 4 bit length */
+ bcmolt_buf_write_u8(out_buf, 0); /* Parameter set body length. */
+}
+
+/* Build Live Peer List or Potential Peer List parameter set */
+static bcmos_bool _mka_build_peer_list(
+ bcmolt_buf *out_buf,
+ uint8_t type,
+ const uint8_t *member_id,
+ uint32_t message_num)
+{
+ /* Parameter checks. */
+ BUG_ON(out_buf == NULL);
+ BUG_ON(member_id == NULL);
+
+ return
+ bcmolt_buf_write_u8(out_buf, type) && /* Parameter set type */
+ bcmolt_buf_write_u8(out_buf, 0) &&
+ bcmolt_buf_write_u8(out_buf, 0) && /* options & 4 bit length */
+ bcmolt_buf_write_u8(out_buf, MKA_MI_LEN + sizeof(uint32_t)) && /* Parameter set body length. */
+ bcmolt_buf_write(out_buf, member_id, MKA_MI_LEN) &&
+ bcmolt_buf_write_u32_be(out_buf, message_num);
+}
+
+/* Build ICV Indicator parameter set */
+static bcmos_bool _mka_build_icv_indicator(bcmolt_buf *out_buf)
+{
+ /* Parameter checks. */
+ BUG_ON(out_buf == NULL);
+
+ return
+ bcmolt_buf_write_u8(out_buf, MKPDU_PARAM_SET_ICV_INDICATOR) &&
+ bcmolt_buf_write_u8(out_buf, 0) &&
+ bcmolt_buf_write_u8(out_buf, 0) && /* options & 4 bit length */
+ bcmolt_buf_write_u8(out_buf, MKA_ICV_LEN); /* Parameter set body length. */
+}
+
+/* Build Distributed SAK parameter set (GCM-AES-128) */
+static bcmos_bool _mka_build_distributed_sak(
+ const dpoe_sec_link_rec *link_rec,
+ bcmolt_buf *out_buf,
+ const uint8_t *key_wrapped_sak)
+{
+ uint8_t option;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+ BUG_ON(out_buf == NULL);
+ BUG_ON(key_wrapped_sak == NULL);
+
+ /* need Distributed AN & Confidentiality Offset (bit 5,4. use 0) */
+ option = link_rec->mka_ctrl.mka_info->association_number;
+
+ option <<= MKA_SAK_USE_PARAM_AN_SHIFT; /* move to bit 7,6 */
+
+ /* key number & AN will be increased from the caller */
+ if (!bcmolt_buf_write_u8(out_buf, MKPDU_PARAM_SET_DIST_SAK) ||
+ !bcmolt_buf_write_u8(out_buf, option) ||
+ !bcmolt_buf_write_u8(out_buf, 0) || /* options & 4 bit length */
+ !bcmolt_buf_write_u8(out_buf, MKA_KEY_WRAPPED_SAK_LEN + MKA_KN_LEN) ||
+ !bcmolt_buf_write_u32_be(out_buf, link_rec->mka_ctrl.mka_info->key_number) ||
+ !bcmolt_buf_write(out_buf, key_wrapped_sak, MKA_KEY_WRAPPED_SAK_LEN))
+ {
+ return BCMOS_FALSE;
+ }
+ return BCMOS_TRUE;
+}
+
+/* Build MACsec SAK Use paramater set */
+static bcmos_bool _mka_build_sak_use_param(
+ const dpoe_sec_link_rec *link_rec,
+ bcmolt_buf *out_buf,
+ bcmos_bool set_tx,
+ bcmos_bool set_rx)
+{
+ uint8_t option1 = 0;
+ uint8_t option2 = 0;
+ uint8_t tmp = 0;
+ uint32_t old_kn;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+ BUG_ON(out_buf == NULL);
+
+ /* option 1: Latest Key AN(7:6), Latest Key Tx(5), Latest Key Rx(4) Old Key AN(3:2), Old Key Tx(1), Old Key Rx(0) */
+ option1 = link_rec->mka_ctrl.mka_info->association_number;
+ option1 <<= MKA_SAK_USE_PARAM_AN_SHIFT; /* move to bit 7,6 (AN) */
+
+ if (set_tx)
+ {
+ tmp = 1 << MKA_SAK_USE_PARAM_LK_TX_SHIFT; /* Latest Key TX */
+ }
+ if (set_rx)
+ {
+ tmp = tmp | (1 << MKA_SAK_USE_PARAM_LK_RX_SHIFT); /* Latest Key Rx */
+ }
+
+ /* for first frame, all 0 is used for old key info */
+ if (link_rec->mka_ctrl.mka_info->refresh_cnt != 0)
+ {
+ /* for OLD AN, tx, rx bits */
+ tmp = tmp | ((link_rec->mka_ctrl.mka_info->association_number & MKA_SAK_USE_PARAM_OLD_KEY_AN_MASK) << MKA_SAK_USE_PARAM_OLD_KEY_AN_SFT);
+ tmp = tmp | MKA_SAK_USE_PARAM_OLD_KEY_TX_RX; /* should have been tx & rx */
+ }
+ option1 |= tmp;
+
+ /* option 2: only set bit 7,6(plain tx & plain rx). all other bits including 4 bits length field should be 0 */
+ option2 = MKA_SAK_USE_PARAM_SECOND_OPT_BYTE;
+
+ /* need to decrement key number for old key (because key numer may be already incremented) */
+ if (link_rec->mka_ctrl.mka_info->key_number == 1) /* initial number */
+ {
+ old_kn = 1;
+ }
+ else
+ {
+ old_kn = link_rec->mka_ctrl.mka_info->key_number - 1;
+ }
+
+ if (!bcmolt_buf_write_u8(out_buf, MKPDU_PARAM_SET_MACSEC_SAK_USE) ||
+ !bcmolt_buf_write_u8(out_buf, option1) ||
+ !bcmolt_buf_write_u8(out_buf, option2) ||
+ !bcmolt_buf_write_u8(out_buf, MKA_SAK_USE_PARAM_BODY_LEN) ||
+ !bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) ||
+ !bcmolt_buf_write_u32_be(out_buf, link_rec->mka_ctrl.mka_info->key_number) ||
+ !bcmolt_buf_write_u32_be(out_buf, 1) || /* Lowest acceptable PN, start from 1 */
+ !bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) ||
+ !bcmolt_buf_write_u32_be(out_buf, old_kn) ||
+ !bcmolt_buf_write_u32_be(out_buf, 1))
+ {
+ return BCMOS_FALSE;
+ }
+
+ return BCMOS_TRUE;
+}
+
+/* Calculate ICV and attach it at the end of MKPDU */
+static bcmos_bool _mka_calc_and_attach_icv(mka_link_info *mka_info, bcmolt_buf *out_buf)
+{
+ uint8_t ick[MKA_KEY_LEN];
+ uint8_t icv[MKA_KEY_LEN];
+
+ /* Parameter checks. */
+ BUG_ON(mka_info == NULL);
+ BUG_ON(out_buf == NULL);
+
+ _mka_calc_ick(ick, mka_info->cak, mka_info->ckn);
+
+ /* ICV will be calculated from DA to end of ICV indicator */
+ _mka_calc_icv(icv, ick, out_buf->start, (uint16_t)bcmolt_buf_get_used(out_buf));
+
+ return bcmolt_buf_write(out_buf, icv, MKA_KEY_LEN);
+}
+
+/* Send the first MKA frame to ONU */
+static bcmos_bool _mka_send_init_frame(dpoe_sec_link_rec *link_rec)
+{
+ bcmolt_buf out_buf;
+ uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
+ bcmos_bool ret = BCMOS_FALSE;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
+ /* construct frame */
+ if (!_mka_build_eapol_header(link_rec, &out_buf, MKA_OLT_INIT_FRAME_LEN) ||
+ !_mka_build_basic_parameter_set(link_rec, &out_buf) ||
+ !_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_LIVE_PEER_LIST) ||
+ !_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
+ !_mka_build_icv_indicator(&out_buf) ||
+ !_mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf)) /* last important step. Calc & attach ICV */
+ {
+ return ret;
+ }
+
+ if (BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf)))
+ {
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000);
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, MKA_HELLO_TIME * 1000);
+ ret = BCMOS_TRUE;
+ }
+
+ return ret;
+}
+
+/* Generate a SAK using RNG */
+void mka_generate_sak(dpoe_sec_link_rec *link_rec, bcmos_bool initial)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ /* Generate SAK */
+ if (initial)
+ {
+ _mka_calc_kek(link_rec->mka_ctrl.mka_info->kek, link_rec->mka_ctrl.mka_info->cak, link_rec->mka_ctrl.mka_info->ckn);
+ dpoe_sec_generate_n_byte_random_number(link_rec->mka_ctrl.mka_info->sak, MKA_KEY_LEN);
+ }
+ else
+ {
+ dpoe_sec_generate_n_byte_random_number(link_rec->mka_ctrl.mka_info->new_sak, MKA_KEY_LEN);
+ }
+}
+
+/* Send the SAK to the ONU */
+bcmos_errno mka_send_sak(dpoe_sec_link_rec *link_rec, uint8_t *sak)
+{
+ bcmolt_buf out_buf;
+ uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
+ uint8_t key_wrapped_sak[MKA_KEY_LEN * 2] = {};
+ bcmos_errno rc = BCM_ERR_COMM_FAIL;
+ dpoe_sec_aes_key aes_key;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
+ /* key wrap sak using kek */
+ dpoe_sec_aes_set_encrypt_key(link_rec->mka_ctrl.mka_info->kek, MKA_KEK_LEN, &aes_key);
+ dpoe_sec_aes_wrap_key(&aes_key, key_wrapped_sak, sak, MKA_SAK_LEN);
+
+ /* construct frame */
+ if (!_mka_build_eapol_header(
+ link_rec,
+ &out_buf,
+ MKA_OLT_INIT_FRAME_LEN + MKA_ONE_PEER_LEN + MKA_DISTRIBUTED_SAK_LEN + MKA_SAK_USE_PARAM_LEM) ||
+ !_mka_build_basic_parameter_set(link_rec, &out_buf) ||
+ !_mka_build_peer_list(
+ &out_buf,
+ MKPDU_PARAM_SET_LIVE_PEER_LIST,
+ link_rec->mka_ctrl.mka_info->link_memeber_id,
+ link_rec->mka_ctrl.mka_info->link_msg_num) ||
+ !_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
+ !_mka_build_distributed_sak(link_rec, &out_buf, key_wrapped_sak) ||
+ !_mka_build_sak_use_param(link_rec, &out_buf, BCMOS_FALSE, BCMOS_TRUE) || /* set RX only */
+ !_mka_build_icv_indicator(&out_buf))
+ {
+ return BCM_ERR_OVERFLOW;
+ }
+
+ /* last important step. Calc & attach ICV */
+ _mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf);
+
+ if (BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf)))
+ {
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, MKA_HELLO_TIME * 1000);
+ rc = BCM_ERR_OK;
+ }
+
+ return rc;
+}
+
+/* Received SAK response. Send confirm frame to ONU */
+static bcmos_bool _mka_send_sak_confirm_frame(dpoe_sec_link_rec *link_rec)
+{
+ bcmolt_buf out_buf;
+ uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
+ if (!_mka_build_eapol_header(link_rec, &out_buf, MKA_OLT_INIT_FRAME_LEN + MKA_ONE_PEER_LEN + MKA_SAK_USE_PARAM_LEM) ||
+ !_mka_build_basic_parameter_set(link_rec, &out_buf) ||
+ !_mka_build_peer_list(
+ &out_buf, MKPDU_PARAM_SET_LIVE_PEER_LIST,
+ link_rec->mka_ctrl.mka_info->link_memeber_id,
+ link_rec->mka_ctrl.mka_info->link_msg_num) ||
+ !_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
+ !_mka_build_sak_use_param(link_rec, &out_buf, BCMOS_TRUE, BCMOS_TRUE) ||
+ !_mka_build_icv_indicator(&out_buf))
+ {
+ return BCMOS_FALSE;
+ }
+
+ /* last important step. Calc & attach ICV */
+ _mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf);
+
+ return BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf));
+}
+
+/* send keep-alive frame every 2 seconds */
+static bcmos_bool _mka_send_keepalive_frame(dpoe_sec_link_rec *link_rec)
+{
+ bcmolt_buf out_buf;
+ uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
+ if (!_mka_build_eapol_header(link_rec, &out_buf, MKA_OLT_INIT_FRAME_LEN + MKA_ONE_PEER_LEN) ||
+ !_mka_build_basic_parameter_set(link_rec, &out_buf) ||
+ !_mka_build_peer_list(
+ &out_buf,
+ MKPDU_PARAM_SET_LIVE_PEER_LIST,
+ link_rec->mka_ctrl.mka_info->link_memeber_id,
+ link_rec->mka_ctrl.mka_info->link_msg_num) ||
+ !_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
+ !_mka_build_icv_indicator(&out_buf))
+ {
+ return BCMOS_FALSE;
+ }
+
+ /* last important step. Calc & attach ICV */
+ _mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf);
+
+ return BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf));
+}
+
+/* Entry point for MKA process. It is called to start the MKA exchange with the ONU that owns the specified link. */
+bcmos_errno mka_start(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ /* initialize link MKA info */
+ _mka_init_link_info(link_rec);
+
+ /* send initial frame (basic param + empty live & potential peer list */
+ if (_mka_send_init_frame(link_rec))
+ {
+ rc = BCM_ERR_OK;
+ }
+ else
+ {
+ rc = BCM_ERR_COMM_FAIL;
+ }
+
+ return rc;
+}
+
+/* Calculate the ICV over the message and compare with the ICV in the message. */
+static bcmos_errno _mka_compare_icv(mka_link_info *info, const mka_event *msg, uint8_t *rx_icv)
+{
+ uint16_t len = 0;
+ uint8_t icv[MKA_ICV_LEN];
+
+ /* Parameter checks. */
+ BUG_ON(info == NULL);
+ BUG_ON(msg == NULL);
+
+ /* If the ICV pointer is NULL, then there was no ICV in the received MKA packet. Just return an error. */
+ if (rx_icv == NULL)
+ {
+ return BCM_ERR_PARM;
+ }
+
+ /* Calculate the length of the buffer over which to calculate the ICV. */
+ len = (EAPOL_MSG_HDR_SIZE + msg->msg->eapol_length) - MKA_ICV_LEN;
+
+ _mka_calc_icv(icv, info->ick, msg->buf->start, len);
+
+ if (memcmp(icv, rx_icv, sizeof(icv)) != 0)
+ {
+ return BCM_ERR_ONU_ERR_RESP;
+ }
+
+ return BCM_ERR_OK;
+}
+
+/* Parse the Basic Parameter Set in MKA PDUs */
+static bcmos_errno _mka_parse_basic_param_set(
+ dpoe_sec_link_rec *link_rec,
+ bcmolt_buf *buf,
+ uint16_t *bytes_read,
+ uint16_t body_len,
+ uint8_t *sci,
+ uint8_t *member_id,
+ uint32_t *msg_num)
+{
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(buf == NULL);
+ BUG_ON(bytes_read == NULL);
+
+ /* Read the ONU SCI */
+ if (!bcmolt_buf_read(buf, sci, MKA_SCI_LEN))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ /* Read the Member ID */
+ if (!bcmolt_buf_read(buf, member_id, MKA_MI_LEN))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ /* Read the ONU message number. */
+ if (!bcmolt_buf_read_u32_be(buf, msg_num))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ /* The SCI, MI, and MN were read from the packet. */
+ *bytes_read = MKA_SCI_LEN + MKA_MI_LEN + MKA_MN_LEN;
+
+ /* Skip the rest */
+ if (!bcmolt_buf_skip(buf, body_len - *bytes_read))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno _mka_waiting_initial_response_data_ind(mka_event *msg, dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_PARSE;
+ uint8_t *icv_ptr;
+ uint8_t param_set_type;
+ uint16_t body_len;
+ bcmos_bool basic_param_set_exist = BCMOS_FALSE;
+ bcmos_bool live_peer_list_exist = BCMOS_FALSE;
+ bcmos_bool potential_peer_list_exist = BCMOS_FALSE;
+ uint16_t bytes_read;
+
+ /* Validate the msg pointer. */
+ BUG_ON(msg->buf == NULL);
+ BUG_ON(msg->msg == NULL);
+
+ /* mark ICV field offset */
+ icv_ptr = msg->buf->curr + (msg->msg->eapol_length - MKA_ICV_LEN);
+
+ /* Validate the Integrity Check Value before continuing to process the packet. */
+ rc = _mka_compare_icv(link_rec->mka_ctrl.mka_info, msg, icv_ptr);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+
+ while (bcmolt_buf_get_remaining_size(msg->buf) > 0)
+ {
+ /* Don't parse past the ICV Parameter Set */
+ if (bcmolt_buf_snap_get(msg->buf) >= icv_ptr)
+ {
+ break;
+ }
+
+ /* Read what should be the Basic Parameter Set header. */
+ if (!bcmolt_buf_read_u8(msg->buf, ¶m_set_type) ||
+ !bcmolt_buf_skip(msg->buf, 1) ||
+ !bcmolt_buf_read_u16_be(msg->buf, &body_len))
+ {
+ break;
+ }
+
+ /* The body length is really 12-bits. */
+ body_len &= 0x0fff;
+
+ if (param_set_type == MKPDU_PARAM_SET_LIVE_PEER_LIST)
+ {
+ /* need to find out whether it is Basic parameter set OR Live Peer List */
+ if (body_len >= MKA_BASIC_PARAM_SET_LEN)
+ {
+ rc = _mka_parse_basic_param_set(
+ link_rec,
+ msg->buf,
+ &bytes_read,
+ body_len,
+ link_rec->mka_ctrl.mka_info->onu_sci,
+ link_rec->mka_ctrl.mka_info->link_memeber_id,
+ &link_rec->mka_ctrl.mka_info->link_msg_num);
+ if (rc != BCM_ERR_OK)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "parse basic param set failed!\n");
+ break;
+ }
+
+ /* Mark that the Basic Parameter Set was found. */
+ basic_param_set_exist = BCMOS_TRUE;
+ }
+ else
+ {
+ /* Live Peer List. skip */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "parse live peer list failed!\n");
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* Mark that the Live Peer List Parameter Set was found. */
+ live_peer_list_exist = BCMOS_TRUE;
+ }
+ }
+ else if (param_set_type == MKPDU_PARAM_SET_POTENTIAL_PEER_LIST)
+ {
+ uint8_t mi[MKA_MI_LEN];
+
+ if (!bcmolt_buf_read(msg->buf, mi, sizeof(mi)))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "parse potential peer list failed!\n");
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* Verify that the OLT member ID in the Potential Peer List Parameter Set matches the expected OLT member
+ ID. */
+ if (memcmp(mi, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) != 0)
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "wrong member ID!\n");
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ /* Skip the remaining bytes. */
+ if (!bcmolt_buf_skip(msg->buf, body_len - MKA_MI_LEN))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "buffer overflow!\n");
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* Mark that the Potential Peer List Parameter Set was found. */
+ potential_peer_list_exist = BCMOS_TRUE;
+ }
+ else
+ {
+ /* Skip other Parameter Set data. */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "buffer overflow!\n");
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+ }
+ }
+
+ if ((rc == BCM_ERR_OK) &&
+ basic_param_set_exist &&
+ live_peer_list_exist &&
+ potential_peer_list_exist &&
+ (link_rec->mka_ctrl.mka_info->peer_state != MKA_PEER_STATE_LIVE))
+ {
+ /* proceed to next state */
+ link_rec->mka_ctrl.mka_info->peer_state = MKA_PEER_STATE_LIVE;
+ }
+ else
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ }
+
+ return rc;
+}
+
+/* Handler for initial response from the ONU */
+static bcmos_errno _mka_waiting_initial_response(mka_event *msg, dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks. */
+ BUG_ON(msg == NULL);
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ switch (msg->type)
+ {
+ case MKA_MSG_TIMEOUT:
+ /* retry 3 times */
+ if (link_rec->mka_ctrl.mka_info->retry_cnt < MKA_INIT_RETRY_CNT)
+ {
+ link_rec->mka_ctrl.mka_info->retry_cnt++;
+ (void)_mka_send_init_frame(link_rec);
+ }
+ else
+ {
+ _mka_clear_link_info(link_rec);
+ rc = BCM_ERR_TIMEOUT;
+ }
+ break;
+
+ case MKA_MSG_DATA_IND:
+ rc = _mka_waiting_initial_response_data_ind(msg, link_rec);
+ break;
+
+ default:
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ return rc;
+}
+
+static bcmos_errno _mka_initial_sak_sent_data_ind(mka_event *msg, dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_PARSE;
+ uint8_t *icv_ptr;
+ uint8_t param_set_type;
+ uint8_t ks_priority;
+ uint16_t body_len;
+ bcmos_bool basic_param_ok = BCMOS_FALSE;
+ bcmos_bool live_peer_list_ok = BCMOS_FALSE;
+ bcmos_bool potential_peer_list_ok = BCMOS_FALSE;
+ bcmos_bool sak_use_param_ok = BCMOS_FALSE;
+ uint16_t bytes_read;
+
+ /* Validate the msg pointer. */
+ BUG_ON(msg->msg == NULL);
+ BUG_ON(msg->buf == NULL);
+
+ /* mark ICV field offset */
+ icv_ptr = msg->buf->curr + (msg->msg->eapol_length - MKA_ICV_LEN);
+
+ /* Validate the Integrity Check Value before continuing to process the packet. */
+ rc = _mka_compare_icv(link_rec->mka_ctrl.mka_info, msg, icv_ptr);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+
+ while (bcmolt_buf_get_remaining_size(msg->buf) > 0)
+ {
+ /* Don't parse past the ICV Parameter Set */
+ if (bcmolt_buf_snap_get(msg->buf) >= icv_ptr)
+ {
+ break;
+ }
+
+ /* Read what should be the Basic Parameter Set header. */
+ if (!bcmolt_buf_read_u8(msg->buf, ¶m_set_type) ||
+ !bcmolt_buf_read_u8(msg->buf, &ks_priority) ||
+ !bcmolt_buf_read_u16_be(msg->buf, &body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* The body length is really 12-bits. */
+ body_len &= 0x0fff;
+
+ if (param_set_type == MKPDU_PARAM_SET_LIVE_PEER_LIST)
+ {
+ /* need to find out whether it is Basic parameter set OR Live Peer List */
+ if (body_len >= MKA_BASIC_PARAM_SET_LEN)
+ {
+ /* in our environment, ONU can not have more than one live peer. So, just seeing param set body len
+ should be good enough. */
+ uint32_t link_mn;
+ uint8_t sci[MKA_SCI_LEN];
+ uint8_t mi[MKA_MI_LEN];
+
+ rc = _mka_parse_basic_param_set(link_rec, msg->buf, &bytes_read, body_len, sci, mi, &link_mn);
+ if (rc != BCM_ERR_OK)
+ {
+ break;
+ }
+
+ /* Verify that ONU SCI is what's expected. */
+ if (memcmp(link_rec->mka_ctrl.mka_info->onu_sci, sci, MKA_SCI_LEN) != 0)
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ /* Verify that ONU MI is what's expected. */
+ if (memcmp(link_rec->mka_ctrl.mka_info->link_memeber_id, mi, MKA_MI_LEN) != 0)
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ /* Verify that the MN is what's expected. */
+ if (link_rec->mka_ctrl.mka_info->link_msg_num < link_mn)
+ {
+ /* correct MN! */
+ link_rec->mka_ctrl.mka_info->link_msg_num = link_mn;
+ basic_param_ok = BCMOS_TRUE;
+ }
+ }
+ else
+ {
+ /* Live Peer List. skip */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* Mark that the Live Peer List Parameter Set was found. */
+ live_peer_list_ok = BCMOS_TRUE;
+ }
+ }
+ else if (param_set_type == MKPDU_PARAM_SET_POTENTIAL_PEER_LIST)
+ {
+ /* Skip Potential Peer List Parameter Set. */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* Mark that the Potential Peer List Parameter Set was found. */
+ potential_peer_list_ok = BCMOS_TRUE;
+ }
+ else if (param_set_type == MKPDU_PARAM_SET_MACSEC_SAK_USE)
+ {
+ /* check only Latest key tx & rx bits (bit 4 & 5) */
+ if ((ks_priority & 0x30) == 0x30)
+ {
+ sak_use_param_ok = BCMOS_TRUE;
+ }
+
+ /* Skip the MKA SAK Use Parameter Set. */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+ }
+ else
+ {
+ /* Skip all other Parameter Set data. */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+ }
+ }
+
+ /* Everything looks good so far? */
+ if ((rc == BCM_ERR_OK) &&
+ basic_param_ok &&
+ live_peer_list_ok &&
+ potential_peer_list_ok &&
+ sak_use_param_ok)
+ {
+ DPOE_SEC_LINK_LOG(DEBUG, link_rec, "Got good MKA SAK response\n");
+ }
+
+ return rc;
+}
+
+/* Response frame handler for the first SAK from the OLT */
+static bcmos_errno _mka_initial_sak_sent(mka_event *msg, dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks. */
+ BUG_ON(msg == NULL);
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ switch (msg->type)
+ {
+ case MKA_MSG_TIMEOUT:
+ _mka_clear_link_info(link_rec);
+ rc = BCM_ERR_TIMEOUT;
+ break;
+
+ case MKA_MSG_DATA_IND:
+ rc = _mka_initial_sak_sent_data_ind(msg, link_rec);
+ break;
+
+ default:
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ return rc;
+}
+
+static bcmos_errno _mka_sak_done_data_ind(mka_event *msg, dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_PARSE;
+ uint8_t *icv_ptr;
+ uint8_t param_set_type;
+ uint16_t body_len;
+ uint8_t ks_priority;
+ uint16_t bytes_read;
+
+ /* Validate the msg pointer. */
+ BUG_ON(msg->msg == NULL);
+ BUG_ON(msg->buf == NULL);
+
+ /* mark ICV field offset */
+ icv_ptr = msg->buf->curr + (msg->msg->eapol_length - MKA_ICV_LEN);
+
+ /* Validate the Integrity Check Value before continuing to process the packet. */
+ rc = _mka_compare_icv(link_rec->mka_ctrl.mka_info, msg, icv_ptr);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+
+ while (bcmolt_buf_get_remaining_size(msg->buf) > 0)
+ {
+ /* Don't parse past the ICV Parameter Set */
+ if (bcmolt_buf_snap_get(msg->buf) >= icv_ptr)
+ {
+ break;
+ }
+
+ /* Read what should be the Basic Parameter Set header. */
+ if (!bcmolt_buf_read_u8(msg->buf, ¶m_set_type) ||
+ !bcmolt_buf_read_u8(msg->buf, &ks_priority) ||
+ !bcmolt_buf_read_u16_be(msg->buf, &body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* The body length is really 12-bits. */
+ body_len &= 0x0fff;
+
+ if (param_set_type == MKPDU_PARAM_SET_LIVE_PEER_LIST)
+ {
+ /* need to find out whether it is Basic parameter set OR Live Peer List */
+ if (body_len >= MKA_BASIC_PARAM_SET_LEN)
+ {
+ /* in our environment, ONU can not have more than one live peer. So, just seeing param set body len
+ should be good enough. */
+ uint32_t link_mn;
+ uint8_t sci[MKA_SCI_LEN];
+ uint8_t mi[MKA_MI_LEN];
+
+ rc = _mka_parse_basic_param_set(link_rec, msg->buf, &bytes_read, body_len, sci, mi, &link_mn);
+ if (rc != BCM_ERR_OK)
+ {
+ break;
+ }
+
+ /* Verify that ONU SCI is what's expected. */
+ if (memcmp(link_rec->mka_ctrl.mka_info->onu_sci, sci, MKA_SCI_LEN) != 0)
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ /* Verify that ONU MI is what's expected. */
+ if (memcmp(link_rec->mka_ctrl.mka_info->link_memeber_id, mi, MKA_MI_LEN) != 0)
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ if (link_rec->mka_ctrl.mka_info->link_msg_num < link_mn)
+ {
+ /* correct MN! */
+ link_rec->mka_ctrl.mka_info->link_msg_num = link_mn;
+ }
+ }
+ else
+ {
+ /* Live Peer List. skip */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+ }
+ }
+ else if (param_set_type == MKPDU_PARAM_SET_MACSEC_SAK_USE)
+ {
+ uint32_t llpn;
+
+ /* Skip to the LLPN. */
+ if (!bcmolt_buf_skip(msg->buf, MKA_MI_LEN + MKA_KN_LEN))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ /* read Latest Lowest Acceptable PN */
+ if (!bcmolt_buf_read_u32_be(msg->buf, &llpn))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+
+ if (llpn > MKA_MAX_ACCEPTABLE_PN)
+ {
+ /* Set flag to indicate SAK refresh is needed. */
+ link_rec->mka_ctrl.mka_info->sak_refresh_needed = BCMOS_TRUE;
+ }
+
+ /* check only Latest key tx & rx bits (bit 4 & 5) */
+ if ((ks_priority & 0x30) != 0x30)
+ {
+ /* Wrong LK Flag */ /* TODO: why isn't this an error? */
+ }
+
+ /* The MI, KN, and LLPN were read. */
+ bytes_read = body_len - (MKA_MI_LEN + MKA_KN_LEN + sizeof(uint32_t));
+
+ /* Skip the rest */
+ if (!bcmolt_buf_skip(msg->buf, bytes_read))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+ }
+ else
+ {
+ /* Skip all other Parameter Set data. */
+ if (!bcmolt_buf_skip(msg->buf, body_len))
+ {
+ rc = BCM_ERR_PARSE;
+ break;
+ }
+ }
+ }
+
+ /* refresh retry count. This is a Critical counter */
+ link_rec->mka_ctrl.mka_info->retry_cnt = 0;
+
+ return rc;
+}
+
+/* MKA frame handler for stable state */
+static bcmos_errno _mka_sak_done(mka_event *msg, dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks. */
+ BUG_ON(msg == NULL);
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ switch (msg->type)
+ {
+ case MKA_MSG_TIMEOUT:
+ /* send keepalive frame at every 2 seconds */
+ (void)_mka_send_keepalive_frame(link_rec);
+ break;
+
+ case MKA_MSG_DATA_IND:
+ rc = _mka_sak_done_data_ind(msg, link_rec);
+ break;
+
+ default:
+ rc = BCM_ERR_ONU_ERR_RESP;
+ break;
+ }
+
+ return rc;
+}
+
+/* Validate the MKA packet. */
+static bcmos_bool _mka_validate_pkt(bcmolt_u8_list_u16 mka_msg, eapol_msg_hdr *eapol)
+{
+ /* Parameter checks. */
+ BUG_ON(eapol == NULL);
+
+ /* This had better be an MKA packet. */
+ if (eapol->eapol_packet_type != EAPOL_TYPE_MKA)
+ {
+ return BCMOS_FALSE;
+ }
+
+ /* The EAPOL length plus the size of the EapolMsgHdr must equal the length of the received packet. */
+ if (mka_msg.len != (eapol->eapol_length + EAPOL_MSG_HDR_SIZE))
+ {
+ return BCMOS_FALSE;
+ }
+
+ /* The EAPOL length must be greater than the minimum supported length, Ethernet + EAPOL header length. */
+ if (eapol->eapol_length < MKA_PDU_MIN_LENGTH)
+ {
+ return BCMOS_FALSE;
+ }
+
+ /* The EAPOL length must be a multiple of four. */
+ if ((eapol->eapol_length % 4) != 0)
+ {
+ return BCMOS_FALSE;
+ }
+
+ return BCMOS_TRUE;
+}
+
+/* Wrapper into the MKA code that sends an SAK Confirm response to the ONU. */
+bcmos_errno mka_send_sak_confirm(dpoe_sec_link_rec *link_rec)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
+
+ /* proceed to next state */
+ if (_mka_send_sak_confirm_frame(link_rec))
+ {
+ /* Everything looks good so far. Update AN and Key Number */
+ link_rec->mka_ctrl.mka_info->key_number += 1;
+ link_rec->mka_ctrl.mka_info->association_number += 1;
+ if (link_rec->mka_ctrl.mka_info->association_number == 4)
+ {
+ /* The association number is concatenated with the OLT SCI to identify a secure association between OLT and
+ ONU. The AN value starts at zero and cycles through values 0 - 3 as a new SAK is distributed to the ONU.
+ */
+ link_rec->mka_ctrl.mka_info->association_number = 0; /* AN = 0 ~ 3 */
+ }
+
+ link_rec->mka_ctrl.mka_info->state = MKA_STATE_MKA_DONE;
+ link_rec->mka_ctrl.mka_info->refresh_cnt += 1;
+ link_rec->mka_ctrl.mka_info->retry_cnt = 0;
+
+ /* start keepalive timer (2 second) */
+ bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, MKA_HELLO_TIME * 1000);
+ }
+ else
+ {
+ rc = BCM_ERR_COMM_FAIL;
+ }
+
+ return rc;
+}
+
+/* Wrapper into the MKA code that handles MKA PDUs from the ONU. */
+bcmos_errno mka_process_packet(dpoe_sec_link_rec *link_rec, bcmolt_u8_list_u16 rx_frame, mka_op_type op_type)
+{
+ bcmos_errno rc = BCM_ERR_PARM;
+ mka_event mka_msg;
+ bcmolt_buf buf;
+ eapol_msg_hdr eapol;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON(rx_frame.val == NULL);
+ BUG_ON((op_type <= MKA_OP__INVALID) || (op_type > MKA_OP_KEEP_ALIVE));
+
+ bcmolt_buf_init(&buf, rx_frame.len, rx_frame.val, BCMOLT_BUF_ENDIAN_FIXED);
+ if (!dpoe_sec_eapol_unpack(&buf, &eapol))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "failed to unpack EAPOL header!\n");
+ return BCM_ERR_PARSE;
+ }
+
+ /* Validate the MKA packet length before processing. */
+ if (!_mka_validate_pkt(rx_frame, &eapol))
+ {
+ DPOE_SEC_LINK_LOG(ERROR, link_rec, "Invalid MKA packet!\n");
+ return BCM_ERR_PARSE;
+ }
+
+ /* Pass the packet to the handler. */
+ mka_msg.type = MKA_MSG_DATA_IND;
+ mka_msg.msg = &eapol;
+ mka_msg.buf = &buf;
+
+ /* Process the MKA packet. */
+ switch (op_type)
+ {
+ case MKA_OP_START_RSP:
+ rc = _mka_waiting_initial_response(&mka_msg, link_rec);
+ break;
+ case MKA_OP_SAK_RSP:
+ rc = _mka_initial_sak_sent(&mka_msg, link_rec);
+ break;
+ case MKA_OP_KEEP_ALIVE:
+ rc = _mka_sak_done(&mka_msg, link_rec);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/* Wrapper into the MKA code that handles MKA timeouts. */
+bcmos_errno mka_process_timeout(dpoe_sec_link_rec *link_rec, mka_op_type op_type)
+{
+ bcmos_errno rc = BCM_ERR_TIMEOUT;
+ mka_event mka_msg;
+
+ /* Parameter checks. */
+ BUG_ON(link_rec == NULL);
+ BUG_ON((op_type < MKA_OP_START_TIMEOUT) || (op_type >= MKA_OP__COUNT));
+
+ /* Pass the packet to the handler. */
+ mka_msg.type = MKA_MSG_TIMEOUT;
+
+ /* Process the MKA packet. */
+ switch (op_type)
+ {
+ case MKA_OP_START_TIMEOUT:
+ rc = _mka_waiting_initial_response(&mka_msg, link_rec);
+ break;
+ case MKA_OP_SAK_TIMEOUT:
+ rc = _mka_initial_sak_sent(&mka_msg, link_rec);
+ break;
+ case MKA_OP_SEND_KEEP_ALIVE:
+ rc = _mka_sak_done(&mka_msg, link_rec);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/dpoe_sec/mka.h b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/mka.h
new file mode 100644
index 0000000..b195864
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/dpoe_sec/mka.h
@@ -0,0 +1,130 @@
+/*
+<: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.
+
+:>
+ */
+
+#if !defined(MKA_H)
+#define MKA_H
+
+#include "bcmos_system.h"
+#include "bcmolt_model_types.h"
+
+#define MKA_LIFE_TIME 6000 /* Ms, 6 seconds */
+
+#define MKA_SCI_LEN 8 /* common SCI size */
+#define MKA_MI_LEN 12 /* Member Identifier */
+
+/* KEK */
+#define MKA_KEK_LEN 128 /* bit length. 16 bytes */
+
+/* SAK */
+#define MKA_SAK_LEN 16
+
+/* ICK */
+#define MKA_ICK_LEN 128 /* bit length. 16 bytes */
+
+/* CKN */
+#define MKA_CKN_LEN 128 /* bit length. 16 bytes */
+
+/* CAK */
+#define MKA_CAK_LEN 128 /* bit length. 16 bytes */
+
+/* for MACSec Key Agreement */
+typedef enum
+{
+ MKA_STATE_INITIAL = 0,
+ MKA_STATE_WAITING_INITIAL_PEER_RESP = 1,
+ MKA_STATE_SAK_SENT = 2,
+ MKA_STATE_MKA_DONE = 3,
+
+ MKA_STATE__COUNT
+} mka_state;
+
+typedef enum
+{
+ MKA_PEER_STATE_NONE = 0,
+ MKA_PEER_STATE_LIVE = 1,
+ MKA_PEER_STATE_POTENTIAL = 2,
+} mka_peer_state;
+
+typedef struct
+{
+ uint8_t onu_sci[MKA_SCI_LEN]; /**< ONU's SCI */
+ uint8_t olt_member_id[MKA_MI_LEN]; /**< OLT MI */
+ uint32_t curr_msg_num; /**< Current OLT MN */
+ uint8_t link_memeber_id[MKA_MI_LEN]; /**< Link MI */
+ uint32_t link_msg_num; /**< Current Link MN */
+ uint8_t cak[MKA_CAK_LEN/8]; /**< Derived CAK */
+ uint8_t ckn[MKA_CKN_LEN/8]; /**< Derived CKN */
+ uint8_t sak[MKA_SAK_LEN]; /**< Derived SAK (a.k.a TEK) */
+ uint8_t new_sak[MKA_SAK_LEN]; /**< New derived SAK for key refresh */
+ uint8_t kek[MKA_KEK_LEN/8]; /**< Derived KEK */
+ uint8_t ick[MKA_ICK_LEN/8]; /**< Derived ICK */
+ uint32_t key_number; /**< Current KN */
+ uint8_t association_number; /**< AN */
+ bcmos_mac_address lesser_mac; /**< Lowest MAC address of MKA peers */
+ bcmos_mac_address greater_mac; /**< Greatest MAC address of MKA peers */
+ mka_state state; /**< State of MKA proper SM */
+ mka_peer_state peer_state; /**< State of MKA peer */
+ uint8_t retry_cnt; /**< MKA message retry count */
+ uint8_t refresh_cnt; /**< Important for building SAK Use Param set */
+ bcmos_bool sak_refresh_needed; /**< SAK refresh needed flag */
+} mka_link_info;
+
+/* The type of MKA operation to process. */
+typedef enum mka_op_type
+{
+ MKA_OP__INVALID = -1,
+
+ /* Packet operations. */
+ MKA_OP_START_RSP,
+ MKA_OP_SAK_RSP,
+ MKA_OP_KEEP_ALIVE,
+
+ /* Timeout operations. */
+ MKA_OP_START_TIMEOUT,
+ MKA_OP_SAK_TIMEOUT,
+ MKA_OP_SEND_KEEP_ALIVE,
+
+ MKA_OP__COUNT
+} mka_op_type;
+
+struct dpoe_sec_link_rec;
+
+bcmos_errno mka_start(struct dpoe_sec_link_rec *link);
+
+void mka_generate_sak(struct dpoe_sec_link_rec *link, bcmos_bool initial);
+
+bcmos_errno mka_send_sak(struct dpoe_sec_link_rec *link, uint8_t *sak);
+
+bcmos_errno mka_send_sak_confirm(struct dpoe_sec_link_rec *link);
+
+bcmos_errno mka_process_packet(struct dpoe_sec_link_rec *link, bcmolt_u8_list_u16 rx_frame, mka_op_type op_type);
+
+bcmos_errno mka_process_timeout(struct dpoe_sec_link_rec *link, mka_op_type op_type);
+
+#endif /* MKA_H */
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/Makefile b/bcm68620_release/release/host_reference/user_appl/eon/Makefile
new file mode 100644
index 0000000..ac4c3a5
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/Makefile
@@ -0,0 +1,33 @@
+# EPON OAM Negotiation user application
+
+ifeq ("$(ENABLE_CLI)", "y")
+
+ MOD_NAME = bcm_user_appl_eon
+ MOD_TYPE = lib
+ MOD_DEPS = host_api bcm_user_appl_epon_oam common_epon_oam
+
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+
+ srcs = bcmolt_eon.c oam_sets/oam_common.c
+
+ ENABLE_DPOE_OAM ?= y
+ ifeq ("$(ENABLE_DPOE_OAM)", "y")
+ EXTRA_DEFINES += -DEON_OAM_SET_DPOE_SUPPORTED
+ srcs += oam_sets/dpoe/dpoe.c
+ endif # ENABLE_DPOE_OAM
+
+ ENABLE_BRCM_OAM ?= y
+ ifeq ("$(ENABLE_BRCM_OAM)", "y")
+ EXTRA_DEFINES += -DEON_OAM_SET_BRCM_SUPPORTED
+ srcs += oam_sets/brcm/brcm.c
+ endif # ENABLE_BRCM_OAM
+
+ ENABLE_CTC_OAM ?= y
+ ifeq ("$(ENABLE_CTC_OAM)", "y")
+ EXTRA_DEFINES += -DEON_OAM_SET_CTC_SUPPORTED
+ srcs += oam_sets/ctc/ctc.c
+ endif # ENABLE_CTC_OAM
+
+endif
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon.c b/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon.c
new file mode 100644
index 0000000..e193148
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon.c
@@ -0,0 +1,783 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file is the master event handler for the EPON OAM negotiation task -
+ It accepts events from the embedded software and the host interface, supported events are
+ 1. Start OAM negotiaton (from host interface)
+ 2. Stop OAM negotiation (from host interface)
+ 3. RX Frame (from ONU via embedded firmware)
+ 4. Timeout
+
+ It dispatches these events to the OAM specific state machine. Currently we support
+ OAM negotiation for BRCM, DPoE, and CTC OAM standards.
+*/
+
+#include "bcmolt_eon_private.h"
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcmolt_msg_pack.h>
+#include "oam_sets/oam_common.h"
+
+#define EON_TASK_MSG_Q_SIZE 64
+
+typedef enum
+{
+ EON_EVENT_TYPE_FRAME_RX,
+ EON_EVENT_TYPE_OAM_NEG_START,
+ EON_EVENT_TYPE_OAM_NEG_STOP,
+ EON_EVENT_TYPE_OAM_TIMER_EXPIRE,
+
+ EON_EVENT_TYPE_COUNT
+} eon_event_type;
+
+typedef union
+{
+ eon_frame_data frame_rx;
+} eon_event_data;
+
+typedef struct
+{
+ eon_event_type type;
+ eon_link_state *link;
+ eon_event_data data;
+} eon_event;
+
+typedef bcmos_errno (*eon_event_handler)(const eon_event* in_event);
+
+static const char* _evname[EON_EVENT_TYPE_COUNT] =
+{
+ [EON_EVENT_TYPE_FRAME_RX] = "eon_event_type_frame_rx",
+ [EON_EVENT_TYPE_OAM_NEG_START] = "eon_event_type_oam_neg_start",
+ [EON_EVENT_TYPE_OAM_NEG_STOP] = "eon_event_type_oam_neg_stop",
+ [EON_EVENT_TYPE_OAM_TIMER_EXPIRE] = "eon_event_type_oam_timer_expire"
+};
+
+static bcmcli_enum_val eon_oam_set_id_table[EON_OAM_SET_ID_COUNT+1] =
+{
+ { .name = "dpoe", .val = EON_OAM_SET_ID_DPOE },
+ { .name = "brcm", .val = EON_OAM_SET_ID_BRCM },
+ { .name = "ctc", .val = EON_OAM_SET_ID_CTC },
+ BCMCLI_ENUM_LAST
+};
+
+eon_global_state eon_state[BCMTR_MAX_OLTS] = {};
+
+#define HEARTBEAT_SEND_PERIOD 1
+#define HEARTBEAT_SEND_OFFSET 15
+#define HEARTBEAT_RECEIVE_TIMEOUT 5
+#define OAM_TIMEOUT_US (HEARTBEAT_RECEIVE_TIMEOUT * 1000 * 1000)
+
+static bcmos_bool epon_oam_neg_running = BCMOS_FALSE;
+
+static inline int _link_key_cmp(const eon_link_state* a, const eon_link_state* b)
+{
+ return memcmp(&a->link_key.link, &b->link_key.link, sizeof(a->link_key.link));
+}
+
+RB_GENERATE_INLINE(link_state_map, eon_link_state, rb_entry, _link_key_cmp);
+
+
+static bcmos_errno _eon_init_task(bcmolt_devid device)
+{
+ bcmos_task_parm task_params =
+ {
+ .name = eon_state[device].task_name,
+ .priority = TASK_PRIORITY_USER_APPL_EON,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+ snprintf(eon_state[device].task_name, sizeof(eon_state[device].task_name), "user_appl_eon%u", device);
+ bcmos_errno rc = bcmos_task_create(&eon_state[device].task, &task_params);
+
+ return rc;
+}
+
+
+static bcmos_errno _eon_init_module(bcmolt_devid device)
+{
+ bcmos_module_parm module_params =
+ {
+ .qparm = { .name = eon_state[device].msg_queue_name, .size = EON_TASK_MSG_Q_SIZE },
+ .init = NULL
+ };
+ snprintf(eon_state[device].msg_queue_name, sizeof(eon_state[device].msg_queue_name), "eon_msg_q%u", device);
+ return bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_EON + device, &eon_state[device].task, &module_params);
+}
+
+static bcmos_errno _eon_send_os_msg(bcmolt_devid device, bcmos_msg_id type, eon_task_msg_data *data)
+{
+ bcmos_errno rc;
+ eon_task_msg *msg = bcmos_calloc(sizeof(*msg));
+ if (msg == NULL)
+ {
+ EON_LOG(ERROR, device, "OS Message calloc failed\n");
+ return BCM_ERR_NOMEM;
+ }
+
+ msg->os_msg.instance = device;
+ msg->os_msg.type = type;
+ msg->data = *data;
+ rc = bcmos_msg_dispatch(&msg->os_msg, BCMOS_MSG_SEND_AUTO_FREE);
+ if (BCM_ERR_OK != rc)
+ {
+ EON_LOG(ERROR, device, "OS Message send failed (%s)\n", bcmos_strerror(rc));
+ }
+
+ return rc;
+}
+
+static void _eon_destroy_link_state(eon_link_state* link_state)
+{
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "Destroying link\n");
+ bcmos_timer_destroy(&link_state->oam_timer);
+ RB_REMOVE(link_state_map, &eon_state[link_state->link_key.device_id].link_state_map, link_state);
+ if (NULL != link_state->tx.payload)
+ {
+ bcmos_free(link_state->tx.payload);
+ }
+ if (NULL != link_state->rx.payload)
+ {
+ bcmos_free(link_state->rx.payload);
+ }
+ if (NULL != link_state->org_spec_state)
+ {
+ bcmos_free(link_state->org_spec_state);
+ }
+ bcmos_free(link_state);
+}
+
+static eon_link_state* _eon_find_link_state(const eon_link_key* link_key)
+{
+ eon_link_state query_key = { .link_key.link = link_key->link };
+
+ return RB_FIND(link_state_map, &eon_state[link_key->device_id].link_state_map, &query_key);
+}
+
+static void _eon_default_callback_fn(void *context, const eon_link_key* link_key, bcmos_errno result)
+{
+ eon_link_state *link_state = _eon_find_link_state(link_key);
+
+ EON_LINK_LOG(
+ INFO, link_key, "OAM negotiation completed (%s), flags %04x\n", bcmos_strerror(result), link_state->oam_state);
+}
+
+static bcmos_errno _eon_inject_frame(eon_link_key* link_key, uint16_t payload_length, uint8_t *payload_addr)
+{
+ bcmos_errno rc;
+ bcmolt_epon_link_inject_frame inject_frame;
+ bcmolt_epon_link_key epon_link_key = link_key->link;
+ bcmolt_ethernet_frame_unmasked frame =
+ {
+ .frame_octets =
+ {
+ .len = payload_length,
+ .val = payload_addr
+ }
+ };
+
+ BCMOLT_PROXY_INIT(&inject_frame, epon_link, inject_frame, epon_link_key);
+ BCMOLT_PROXY_PROP_SET(&inject_frame, epon_link, inject_frame, frame, frame);
+ rc = bcmolt_proxy_send(link_key->device_id, &inject_frame.hdr);
+ if (BCM_ERR_OK != rc)
+ {
+ EON_LINK_LOG(ERROR, link_key, "inject operation failed (%s)\n", bcmos_strerror(rc));
+ }
+
+ return rc;
+}
+
+static bcmos_errno _eon_clear_prov(eon_link_key *link_key)
+{
+ bcmos_errno err;
+ bcmolt_epon_link_key key = link_key->link;
+ bcmolt_epon_link_oam_keepalive_timer_stop stop_oper;
+ bcmolt_oam_heartbeat_config hb_cfg = {};
+ bcmolt_epon_link_cfg link_cfg;
+
+ BCMOLT_OPER_INIT(&stop_oper, epon_link, oam_keepalive_timer_stop, key);
+ err = bcmolt_oper_submit(link_key->device_id, &stop_oper.hdr);
+ if (BCM_ERR_OK != err)
+ {
+ return err;
+ }
+
+ hb_cfg.transmit_frame.len = 0;
+ hb_cfg.transmit_frame.val = NULL;
+ hb_cfg.ignored_receive_frame_template.frame_octets.len = 0;
+ hb_cfg.ignored_receive_frame_template.frame_octets.val = NULL;
+ hb_cfg.ignored_receive_frame_template.mask_octets.len = 0;
+ hb_cfg.ignored_receive_frame_template.mask_octets.val = NULL;
+ hb_cfg.receive_timeout = 5;
+ hb_cfg.maximum_receive_size = 0;
+ BCMOLT_CFG_INIT(&link_cfg, epon_link, key);
+ BCMOLT_CFG_PROP_SET(&link_cfg, epon_link, oam_heartbeat_config, hb_cfg);
+ err = bcmolt_cfg_set(link_key->device_id, &link_cfg.hdr);
+
+ return err;
+}
+
+static bcmos_errno _eon_set_oam_heartbeat_config(const eon_link_state *link_state)
+{
+ bcmos_errno rc;
+ bcmolt_oam_heartbeat_config oam_heartbeat_config = {};
+
+ oam_heartbeat_config.receive_timeout = HEARTBEAT_RECEIVE_TIMEOUT;
+ oam_heartbeat_config.maximum_receive_size = link_state->max_pdu_size;
+
+ /* The BCM68620 TX heartbeat provisioning does not include the first 15 bytes - SA, DA, EtherType or Subtype */
+ oam_heartbeat_config.transmit_frame.val = &link_state->tx.payload[HEARTBEAT_SEND_OFFSET];
+ oam_heartbeat_config.transmit_frame.len = link_state->tx.length - HEARTBEAT_SEND_OFFSET;
+
+ oam_heartbeat_config.ignored_receive_frame_template.frame_octets.val = link_state->rx.payload;
+ oam_heartbeat_config.ignored_receive_frame_template.frame_octets.len = link_state->rx.length;
+
+ /* forward OAM frames with any differences to the host */
+ oam_heartbeat_config.ignored_receive_frame_template.mask_octets.val = bcmos_calloc(link_state->rx.length);
+ if (NULL != oam_heartbeat_config.ignored_receive_frame_template.mask_octets.val)
+ {
+ memset(oam_heartbeat_config.ignored_receive_frame_template.mask_octets.val, 0xFF, link_state->rx.length);
+ oam_heartbeat_config.ignored_receive_frame_template.mask_octets.len = link_state->rx.length;
+ }
+ else
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ bcmolt_epon_link_cfg link_cfg;
+ BCMOLT_CFG_INIT(&link_cfg, epon_link, link_state->link_key.link);
+ BCMOLT_CFG_PROP_SET(&link_cfg, epon_link, oam_heartbeat_config, oam_heartbeat_config);
+ rc = bcmolt_cfg_set(link_state->link_key.device_id, &link_cfg.hdr); /* call API */
+
+ bcmos_free(oam_heartbeat_config.ignored_receive_frame_template.mask_octets.val);
+
+ return rc;
+}
+
+static bcmos_errno _eon_start_oam_heartbeat(eon_link_state *link_state)
+{
+ bcmos_errno rc;
+
+ rc = _eon_set_oam_heartbeat_config(link_state);
+ if (BCM_ERR_OK == rc)
+ {
+ bcmolt_epon_link_key emb_link_key = link_state->link_key.link;
+ bcmolt_epon_link_oam_keepalive_timer_start start_heartbeat_op;
+ BCMOLT_OPER_INIT(&start_heartbeat_op, epon_link, oam_keepalive_timer_start, emb_link_key);
+ BCMOLT_OPER_PROP_SET(
+ &start_heartbeat_op,
+ epon_link,
+ oam_keepalive_timer_start,
+ send_period,
+ HEARTBEAT_SEND_PERIOD);
+ rc = bcmolt_oper_submit(link_state->link_key.device_id, &start_heartbeat_op.hdr);
+ if (BCM_ERR_OK != rc)
+ {
+ EON_LINK_LOG(ERROR, &link_state->link_key, "keepalive start operation submit failed (%s)\n",
+ bcmos_strerror(rc));
+ }
+ }
+ else
+ {
+ EON_LINK_LOG(ERROR, &link_state->link_key, "failed to set heartbeat configuration(%s)\n", bcmos_strerror(rc));
+ }
+
+ return rc;
+}
+
+static bcmos_errno _eon_send_info_frame(eon_link_state *link_state)
+{
+ eon_frame_data frame_tx_req;
+ bcmos_errno rc;
+
+ rc = build_tx_info_frame(link_state, &frame_tx_req);
+ if (BCM_ERR_OK == rc)
+ {
+ rc = _eon_inject_frame(&link_state->link_key, frame_tx_req.length, frame_tx_req.payload);
+ if (BCM_ERR_OK == rc)
+ {
+ bcmos_timer_start(&link_state->oam_timer, OAM_TIMEOUT_US); /* start timer for rx frame */
+ }
+ else
+ {
+ EON_LINK_LOG(WARNING, &link_state->link_key, "failed to inject frame (%s)\n", bcmos_strerror(rc));
+ }
+ bcmos_free(frame_tx_req.payload);
+ }
+
+ return rc;
+}
+
+static bcmos_errno _eon_complete_negotiation(eon_link_state *link_state, uint8_t *rx_payload, uint16_t rx_length)
+{
+ bcmos_errno rc;
+ eon_frame_data frame_tx_req;
+
+ rc = build_tx_info_frame(link_state, &frame_tx_req);
+ if (BCM_ERR_OK != rc)
+ {
+ return rc;
+ }
+ link_state->tx.payload = frame_tx_req.payload;
+ link_state->tx.length = frame_tx_req.length;
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "Setting TX payload\n");
+
+ link_state->rx.payload = bcmos_calloc(rx_length);
+ if (NULL == link_state->rx.payload)
+ {
+ return BCM_ERR_NOMEM;
+ }
+ memcpy(link_state->rx.payload, rx_payload, rx_length);
+ link_state->rx.length = rx_length;
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "Setting RX mask\n");
+
+ if (link_state->is_heartbeat_autostart)
+ {
+ rc = _eon_start_oam_heartbeat(link_state);
+ EON_LINK_LOG(INFO, &link_state->link_key, "start OAM heartbeat: %s\n", bcmos_strerror(rc));
+ }
+ link_state->callback(link_state->context, &link_state->link_key, rc);
+ _eon_destroy_link_state(link_state);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno _eon_event_handler_frame_rx(const eon_event* event)
+{
+ bcmos_errno rc;
+
+ EON_LINK_LOG(DEBUG, &event->link->link_key, "received %u-byte frame beginning at %p\n",
+ event->data.frame_rx.length, event->data.frame_rx.payload);
+
+ /* validates frame, updates flags */
+ rc = handle_rx_info_frame(event->link, event->data.frame_rx.length, event->data.frame_rx.payload);
+ switch (rc)
+ {
+ case BCM_ERR_OK: /* Completed Negotiation */
+ rc = _eon_complete_negotiation(event->link, event->data.frame_rx.payload, event->data.frame_rx.length);
+ break;
+ case BCM_ERR_IN_PROGRESS:
+ rc = _eon_send_info_frame(event->link);
+ break;
+ case BCM_ERR_PARSE:
+ EON_LINK_LOG(INFO, &event->link->link_key, "received non info frame; ignoring\n");
+ rc = BCM_ERR_OK; /* ...and keep running */
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+static bcmos_errno _eon_event_handler_start(const eon_event* event)
+{
+ bcmos_errno rc;
+
+ rc = _eon_clear_prov(&event->link->link_key);
+ if (BCM_ERR_OK == rc)
+ {
+ event->link->oam_state = BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING;
+ rc = _eon_send_info_frame(event->link);
+ }
+ return rc;
+}
+
+static bcmos_errno _eon_event_handler_stop(const eon_event* event)
+{
+ EON_LINK_LOG(INFO, &event->link->link_key, "OAM stop command received\n");
+ _eon_destroy_link_state(event->link);
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno _eon_event_handler_oam_timer_expire(const eon_event* event)
+{
+ EON_LINK_LOG(WARNING, &event->link->link_key, "OAM timer expired\n");
+ return BCM_ERR_TIMEOUT;
+}
+
+static const eon_event_handler event_sm[EON_EVENT_TYPE_COUNT] =
+{
+ [EON_EVENT_TYPE_FRAME_RX] = _eon_event_handler_frame_rx,
+ [EON_EVENT_TYPE_OAM_NEG_START] = _eon_event_handler_start,
+ [EON_EVENT_TYPE_OAM_NEG_STOP] = _eon_event_handler_stop,
+ [EON_EVENT_TYPE_OAM_TIMER_EXPIRE] = _eon_event_handler_oam_timer_expire
+};
+
+static bcmos_errno _eon_event_handler(const eon_event* event)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ if (event->type < EON_EVENT_TYPE_COUNT)
+ {
+ EON_LINK_LOG(DEBUG, &event->link->link_key, "event %s\n", _evname[event->type]);
+ rc = event_sm[event->type](event);
+ }
+ else
+ {
+ EON_LINK_LOG(ERROR, &event->link->link_key, "unknown event %d\n", event->type);
+ }
+
+ return rc;
+}
+
+static void _eon_event_dispatch(const eon_event* event_in)
+{
+ bcmos_errno rc;
+
+ rc = _eon_event_handler(event_in);
+ if (rc != BCM_ERR_OK)
+ {
+ event_in->link->callback(event_in->link->context, &event_in->link->link_key, rc);
+ _eon_destroy_link_state(event_in->link);
+ }
+}
+
+static bcmos_timer_rc _eon_oam_timer_expire(bcmos_timer* timer, long pUser)
+{
+ eon_event event = { .type = EON_EVENT_TYPE_OAM_TIMER_EXPIRE, .link = (eon_link_state *)pUser };
+
+ _eon_event_dispatch(&event);
+
+ return BCMOS_TIMER_OK;
+}
+
+static bcmos_errno _eon_create_link_state(
+ eon_link_state **state,
+ eon_link_key* link_key,
+ eon_oam_set_id oam_set,
+ bcmolt_eon_result_cb cb,
+ void *context,
+ bcmos_bool is_heartbeat_autostart)
+{
+ bcmos_errno rc;
+ eon_link_state *link_state;
+
+ link_state = _eon_find_link_state(link_key);
+ if (link_state != NULL)
+ {
+ EON_LINK_LOG(ERROR, link_key, "OAM negotiation is already in progress\n");
+ return BCM_ERR_ALREADY;
+ }
+
+ link_state = bcmos_calloc(sizeof(*link_state));
+ if (link_state == NULL)
+ {
+ EON_LINK_LOG(ERROR, link_key, "Failed to allocate state record\n");
+ return BCM_ERR_NOMEM;
+ }
+
+ bcmolt_epon_ni_key ni_key = { .epon_ni = link_key->link.epon_ni };
+ bcmolt_epon_ni_cfg ni_cfg;
+
+ BCMOLT_CFG_INIT(&ni_cfg, epon_ni, ni_key);
+ BCMOLT_CFG_PROP_GET(&ni_cfg, epon_ni, mac_address);
+ rc = bcmolt_cfg_get(link_key->device_id, &ni_cfg.hdr);
+ if (BCM_ERR_OK == rc)
+ {
+ link_state->epon_ni_mac_addr = ni_cfg.data.mac_address;
+ }
+ else
+ {
+ EON_LINK_LOG(ERROR, link_key, "Failed to retrieve EPON NI MAC address: %s\n", bcmos_strerror(rc));
+ bcmos_free(link_state);
+ return rc;
+ }
+
+ bcmos_timer_parm timer_spec =
+ {
+ .owner = BCMOS_MODULE_ID_USER_APPL_EON + link_key->device_id,
+ .handler = _eon_oam_timer_expire,
+ .data = (long)link_state
+ };
+ rc = bcmos_timer_create(&link_state->oam_timer, &timer_spec);
+ if (BCM_ERR_OK != rc)
+ {
+ EON_LINK_LOG(ERROR, link_key, "Failed to create timer: %s\n", bcmos_strerror(rc));
+ bcmos_free(link_state);
+ return rc;
+ }
+
+ link_state->link_key = *link_key;
+ link_state->callback = cb ? cb : _eon_default_callback_fn;
+ link_state->context = context;
+ link_state->is_heartbeat_autostart = is_heartbeat_autostart;
+ link_state->oam_set = oam_set;
+ link_state->max_pdu_size = 1536;
+
+ RB_INSERT(link_state_map, &eon_state[link_key->device_id].link_state_map, link_state);
+
+ *state = link_state;
+
+ return BCM_ERR_OK;
+}
+
+static void _eon_task_process_rx(bcmolt_devid device_id, const bcmolt_proxy_rx *rx)
+{
+ eon_event event = { .type = EON_EVENT_TYPE_FRAME_RX };
+
+ const bcmolt_epon_link_key* epon_link_key = (const bcmolt_epon_link_key*)(rx + 1);
+ eon_link_key link_key = { .device_id = device_id, .link = *epon_link_key };
+ eon_link_state *link_state = _eon_find_link_state(&link_key);
+
+ if (link_state != NULL)
+ {
+ EON_LINK_LOG(DEBUG, &link_key, "found existing link\n");
+ const bcmolt_u8_list_u32 frame = ((const bcmolt_epon_link_frame_captured*)rx)->data.frame;
+ event.link = link_state;
+ event.data.frame_rx.payload = frame.val;
+ event.data.frame_rx.length = frame.len;
+ _eon_event_dispatch(&event);
+ }
+ else
+ {
+ EON_LINK_LOG(DEBUG, &link_key, "rx on unknown link\n");
+ }
+}
+
+static void _eon_handle_proxy_rx(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ eon_task_msg *msg = (eon_task_msg*)os_msg;
+
+ _eon_task_process_rx(os_msg->instance, msg->data.proxy_rx.rx);
+
+ bcmolt_msg_free(&msg->data.proxy_rx.rx->hdr); /* free cloned indication */
+ bcmos_free(os_msg);
+}
+
+static void _eon_handle_start(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ eon_task_msg *msg = (eon_task_msg*)os_msg;
+ eon_link_key link_key = { .device_id = os_msg->instance, .link = msg->data.start.link_key };
+ eon_event event = { .type = EON_EVENT_TYPE_OAM_NEG_START };
+
+ if (BCM_ERR_OK == _eon_create_link_state(
+ &event.link,
+ &link_key,
+ msg->data.start.oam_set,
+ msg->data.start.cb,
+ msg->data.start.context,
+ msg->data.start.is_heartbeat_autostart))
+ {
+ _eon_event_dispatch(&event);
+ }
+
+ bcmos_free(os_msg);
+}
+
+static void _eon_handle_stop(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ eon_task_msg *msg = (eon_task_msg*)os_msg;
+ eon_link_key link_key = { .device_id = os_msg->instance, .link = msg->data.stop.link_key };
+ eon_event event = { .type = EON_EVENT_TYPE_OAM_NEG_STOP, .link = _eon_find_link_state(&link_key) };
+
+ if (NULL != event.link)
+ {
+ _eon_event_dispatch(&event);
+ }
+
+ bcmos_free(os_msg);
+}
+
+static bcmos_errno _eon_cmd_start(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmos_errno rc;
+ eon_task_msg_data msg_data;
+
+ msg_data.start.link_key.epon_ni = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ msg_data.start.link_key.mac_address = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+ msg_data.start.oam_set = (eon_oam_set_id)bcmcli_find_named_parm(session, "oam_set")->value.enum_val;
+ msg_data.start.cb = NULL;
+ msg_data.start.context = NULL;
+ msg_data.start.is_heartbeat_autostart = BCMOS_TRUE;
+
+ rc = _eon_send_os_msg(current_device, BCMOS_MSG_ID_EON_START, &msg_data);
+
+ return rc;
+}
+
+static bcmos_errno _eon_cmd_stop(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmos_errno rc;
+ eon_task_msg_data msg_data;
+
+ msg_data.stop.link_key.epon_ni = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ msg_data.stop.link_key.mac_address = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ rc = _eon_send_os_msg(current_device, BCMOS_MSG_ID_EON_STOP, &msg_data);
+
+ return rc;
+}
+
+
+static bcmos_errno _eon_cmd_show(
+ bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ eon_link_state* link_state;
+ bcmos_bool no_links = BCMOS_TRUE;
+
+ RB_FOREACH(link_state, link_state_map, &eon_state[current_device].link_state_map)
+ {
+ no_links = BCMOS_FALSE;
+ bcmcli_session_print(session,
+ "--> "LINK_KEY_FMT_STR" OAM set %u state %04x\n",
+ LINK_KEY_DATA(&link_state->link_key),
+ link_state->oam_set,
+ link_state->oam_state);
+ }
+ if (no_links)
+ {
+ bcmcli_session_print(session, "no links\n");
+ }
+
+ return BCM_ERR_OK;
+}
+
+
+void bcmolt_user_appl_eon_cli_init(bcmcli_entry *top_dir)
+{
+ static const char *dir_name = "eon";
+
+ if (bcmcli_dir_find(top_dir, dir_name))
+ {
+ return;
+ }
+
+ bcmcli_entry *dir = bcmcli_dir_add(top_dir,
+ dir_name,
+ "EPON OAM negotiation commands",
+ BCMCLI_ACCESS_ADMIN, NULL);
+ BUG_ON(dir == NULL);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir,
+ "show",
+ "Show current EON configuration and state",
+ _eon_cmd_show);
+
+ BCMCLI_MAKE_CMD(dir,
+ "start_oam_negotiation",
+ "Start OAM negotiation for an EPON link",
+ _eon_cmd_start,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("mac_address", "MAC address", BCMCLI_PARM_MAC, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_ENUM("oam_set", "OAM set", eon_oam_set_id_table, BCMCLI_PARM_FLAG_NONE));
+
+
+ BCMCLI_MAKE_CMD(dir,
+ "stop_oam_negotiation",
+ "Stop in progress OAM negotiation for an EPON link",
+ _eon_cmd_stop,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("mac_address", "MAC address", BCMCLI_PARM_MAC, BCMCLI_PARM_FLAG_NONE) );
+}
+
+
+void bcmolt_user_appl_eon_init(void)
+{
+ bcmos_errno rc;
+
+ if (epon_oam_neg_running)
+ {
+ return;
+ }
+
+ for (bcmolt_devid device = 0; device < BCMTR_MAX_OLTS; device++)
+ {
+ rc = _eon_init_task(device);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ rc = _eon_init_module(device);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ char log_name[MAX_DEV_LOG_ID_NAME];
+ snprintf(log_name, sizeof(log_name), "user_appl_eon%u", device);
+ eon_state[device].log_id = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
+
+ bcmos_msg_register(
+ BCMOS_MSG_ID_EON_START,
+ device,
+ BCMOS_MODULE_ID_USER_APPL_EON + device,
+ _eon_handle_start);
+ bcmos_msg_register(
+ BCMOS_MSG_ID_EON_STOP,
+ device,
+ BCMOS_MODULE_ID_USER_APPL_EON + device,
+ _eon_handle_stop);
+ bcmos_msg_register(
+ BCMOS_MSG_ID_EON_PROXY_RX,
+ device,
+ BCMOS_MODULE_ID_USER_APPL_EON + device,
+ _eon_handle_proxy_rx);
+
+ // initialize the state map
+ RB_INIT(&eon_state[device].link_state_map);
+ }
+
+ epon_oam_neg_running = BCMOS_TRUE;
+}
+
+// public indication handler interface -- called in transport layer context
+bcmos_errno bcmolt_user_appl_eon_process_rx(bcmolt_devid device_id, bcmolt_proxy_rx *rx)
+{
+ bcmos_errno rc;
+ eon_task_msg_data msg_data = {};
+
+ /* Not running --> silently ignore */
+ if (!epon_oam_neg_running)
+ {
+ return BCM_ERR_OK;
+ }
+
+ /* Not our message --> silenty ignore */
+ if ( (BCMOLT_OBJ_ID_EPON_LINK != rx->hdr.obj_type) ||
+ (BCMOLT_EPON_LINK_PROXY_RX_ID_FRAME_CAPTURED != rx->hdr.subgroup) )
+ {
+ return BCM_ERR_OK;
+ }
+
+ /* This is something this application does care about - so clone the message into
+ newly-allocated memory so we can handle it after the original message has been freed */
+ rc = bcmolt_msg_clone((bcmolt_msg**)&msg_data.proxy_rx.rx, &rx->hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ EON_LOG(ERROR, device_id, "Message clone failed: %s\n", bcmos_strerror(rc));
+ return rc;
+ }
+
+ rc = _eon_send_os_msg(device_id, BCMOS_MSG_ID_EON_PROXY_RX, &msg_data);
+ if (rc != BCM_ERR_OK)
+ {
+ bcmolt_msg_free(&msg_data.proxy_rx.rx->hdr);
+ }
+ return rc;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon.h b/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon.h
new file mode 100644
index 0000000..c93fcd1
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon.h
@@ -0,0 +1,98 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_EON_H_
+#define _BCMOLT_EON_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcmcli.h>
+
+/* uniquely identifies specific EPON links under management by this application */
+typedef struct
+{
+ bcmolt_devid device_id;
+ bcmolt_epon_link_key link;
+} eon_link_key;
+
+typedef enum
+{
+ EON_OAM_SET_ID_DPOE,
+ EON_OAM_SET_ID_BRCM,
+ EON_OAM_SET_ID_CTC,
+
+ EON_OAM_SET_ID_COUNT
+} eon_oam_set_id;
+
+/* signature of optional callback function used to report outcomes of OAM negotiations */
+typedef void (*bcmolt_eon_result_cb)(void *context, const eon_link_key *link_key, bcmos_errno result);
+
+typedef struct
+{
+ bcmolt_epon_link_key link_key;
+ eon_oam_set_id oam_set;
+ bcmolt_eon_result_cb cb;
+ void *context;
+ bcmos_bool is_heartbeat_autostart;
+} eon_task_start_data;
+
+typedef struct
+{
+ bcmolt_epon_link_key link_key;
+} eon_task_stop_data;
+
+typedef struct
+{
+ bcmolt_proxy_rx *rx;
+} eon_task_proxy_rx_data;
+
+typedef union
+{
+ eon_task_start_data start;
+ eon_task_stop_data stop;
+ eon_task_proxy_rx_data proxy_rx;
+} eon_task_msg_data;
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ eon_task_msg_data data;
+} eon_task_msg;
+
+// initialize the EPON OAM negotiation application (should be called as part of user application startup)
+extern void bcmolt_user_appl_eon_init(void);
+
+// process a proxy rx
+bcmos_errno bcmolt_user_appl_eon_process_rx(bcmolt_devid device_id, bcmolt_proxy_rx *proxy_rx);
+
+// initialize EON CLI
+void bcmolt_user_appl_eon_cli_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon_private.h b/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon_private.h
new file mode 100644
index 0000000..f40b453
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/bcmolt_eon_private.h
@@ -0,0 +1,92 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_EON_PRIVATE_H_
+#define _BCMOLT_EON_PRIVATE_H_
+
+/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ x This file is for internal use within the EPON OAM negotiation application. x
+ x x
+ x Outside code should not refer to anything defined or declared herein! x
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */
+
+#include "bcmolt_eon.h" /* the private interface extends the public interface */
+#include "bcmolt_epon_oam_types.h"
+#include "bcm_dev_log.h"
+#include "bcmolt_utils.h"
+
+typedef struct
+{
+ uint8_t* payload; /* payload bytes in [DA,FCS) */
+ uint16_t length; /* frame length from DA to just before FCS */
+} eon_frame_data;
+
+/* map containing state of all EPON links with OAM negotiation underway or completed */
+typedef struct eon_link_state eon_link_state;
+typedef struct link_state_map link_state_map;
+RB_HEAD(link_state_map, eon_link_state);
+
+struct eon_link_state
+{
+ eon_link_key link_key; /* globally unique opaque entity id */
+ bcmos_timer oam_timer; /* timer used when awaiting oam responses */
+ eon_oam_set_id oam_set; /* OAM set applicable to the link */
+ bcmolt_epon_oam_oam_flags oam_state; /* OAM link negotiation state */
+ uint16_t revision; /* Local revision */
+ uint16_t max_pdu_size; /* max PDU size */
+ bcmolt_eon_result_cb callback; /* callback when negotiation is complete */
+ void *context; /* context for the above callback function */
+ bcmolt_epon_oam_local_remote_info remote_info; /* previous TLV received from the remote */
+ bcmos_mac_address epon_ni_mac_addr; /* MAC address to use for SA of egress OAM frames */
+ eon_frame_data rx; /* Final received OAM */
+ eon_frame_data tx; /* Final sent OAM */
+ bcmos_bool is_heartbeat_autostart; /* start heartbeat upon successful negotiation completion */
+ void *org_spec_state; /* State specific to the selected OAM set */
+ RB_ENTRY(eon_link_state) rb_entry;
+};
+
+typedef struct
+{
+ bcmos_task task;
+ char task_name[MAX_TASK_NAME_SIZE];
+ char msg_queue_name[MAX_MSG_QUEUE_NAME_SIZE];
+ dev_log_id log_id;
+ link_state_map link_state_map;
+} eon_global_state;
+
+#define LINK_KEY_FMT_STR "PON %u, "BCMOS_MACADDR_FMT_STR
+#define LINK_KEY_DATA(lkey) (lkey)->link.epon_ni, BCMOS_MACADDR_PARAMS(&(lkey)->link.mac_address)
+
+#define EON_LOG(level, device, format, args...) BCM_LOG(level, eon_state[device].log_id, format, ##args)
+#define EON_LINK_LOG(level, link_key, fmt, args...) \
+ BCM_LOG(level, eon_state[(link_key)->device_id].log_id, LINK_KEY_FMT_STR": "fmt, LINK_KEY_DATA(link_key), ##args)
+
+extern eon_global_state eon_state[BCMTR_MAX_OLTS];
+
+#endif // _BCMOLT_EON_PRIVATE_H_
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/brcm/brcm.c b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/brcm/brcm.c
new file mode 100644
index 0000000..c6170f6
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/brcm/brcm.c
@@ -0,0 +1,108 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file contains the pieces of the OAM negotiation state machine that are specific to BRCM OAM. */
+
+#include "bcmolt_eon_private.h"
+#include "../oam_common.h"
+#include "brcm.h"
+
+/* Org specific TLV to send to the ONU */
+static const bcmolt_epon_oam_organization_specific_info brcm_tx_tlv =
+{
+ .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_TEK,
+ .u =
+ {
+ .tek =
+ {
+ .tlvs =
+ {
+ .type = BCMOLT_EPON_OAM_TEK_INFO_TLV_TYPE_EXTENSION_SUPPORT,
+ .u =
+ {
+ .extension_support =
+ {
+ .version = 0,
+ .report_mode = BCMOLT_EPON_OAM_TEK_REPORT_MODES_TEKNOVUS,
+ .preferred_report_mode = BCMOLT_EPON_OAM_TEK_REPORT_MODES_TEKNOVUS
+ }
+ }
+ }
+ }
+ }
+};
+
+void brcm_tx_add_tlv(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam)
+{
+ if (LOCAL_STABLE_REMOTE_STABLE == link_state->oam_state)
+ {
+ oam->u.info.tlvs[oam->u.info.tlvs_count].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value = brcm_tx_tlv;
+ oam->u.info.tlvs_count++;
+ }
+}
+
+void brcm_rx_tlv(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc)
+{
+ if (link_state->oam_state == LOCAL_STABLE_REMOTE_STABLE)
+ {
+ if ((NULL == org_spec) ||
+ (BCMOLT_EPON_OAM_WELL_KNOWN_OUI_TEK != org_spec->oui) ||
+ (BCMOLT_EPON_OAM_TEK_INFO_TLV_TYPE_EXTENSION_SUPPORT != org_spec->u.tek.tlvs.type))
+ {
+ EON_LINK_LOG(INFO, &link_state->link_key, "Didn't get BRCM TLV\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ if ((0 != org_spec->u.tek.tlvs.u.extension_support.version) ||
+ (0 ==
+ (BCMOLT_EPON_OAM_TEK_REPORT_MODES_TEKNOVUS & org_spec->u.tek.tlvs.u.extension_support.report_mode)))
+ {
+ EON_LINK_LOG(
+ INFO, &link_state->link_key, "BRCM TLV contained bad info: version %u rpt modes %02x\n",
+ org_spec->u.tek.tlvs.u.extension_support.version,
+ org_spec->u.tek.tlvs.u.extension_support.report_mode);
+ *rc = BCM_ERR_NOT_SUPPORTED;
+ }
+ }
+ }
+ else
+ {
+ if (NULL != org_spec)
+ {
+ EON_LINK_LOG(INFO, &link_state->link_key, "Got unexpected organization specific TLV\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ }
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/brcm/brcm.h b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/brcm/brcm.h
new file mode 100644
index 0000000..34c8878
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/brcm/brcm.h
@@ -0,0 +1,41 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_EON_BRCM_H_
+#define _BCMOLT_EON_BRCM_H_
+
+void brcm_tx_add_tlv(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam);
+
+void brcm_rx_tlv(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc);
+
+#endif // _BCMOLT_EON_BRCM_H_
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/ctc/ctc.c b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/ctc/ctc.c
new file mode 100644
index 0000000..768c8b9
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/ctc/ctc.c
@@ -0,0 +1,212 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file contains the pieces of the OAM negotiation state machine that are specific to CTC OAM. */
+
+#include "bcmolt_eon_private.h"
+#include "../oam_common.h"
+#include "ctc.h"
+
+#define CTC_EXTENSION_SUPPORT 0x1
+#define CTC_PREFERRED_VERSION 0x30
+
+typedef enum
+{
+ CTC_NEG_ADVERTISING,
+ CTC_NEG_CONFIRMING,
+ CTC_NEG_DONE
+} ctc_neg_state;
+
+typedef struct
+{
+ ctc_neg_state state;
+ uint8_t selected_version;
+} ctc_state;
+
+static bcmolt_epon_oam_ctc_oui_version_pair ctc_advertised_versions[] =
+{
+ { .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC, .version = 0x30 },
+ { .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC, .version = 0x21 },
+ { .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC, .version = 0x20 },
+ { .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC, .version = 0x13 },
+ { .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC, .version = 0x01 }
+};
+
+/* Org specific TLV to send to the ONU */
+static const bcmolt_epon_oam_organization_specific_info ctc_tx_advertise_tlv =
+{
+ .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC,
+ .u =
+ {
+ .ctc =
+ {
+ .extension_support = CTC_EXTENSION_SUPPORT,
+ .version = CTC_PREFERRED_VERSION,
+ .supp_version_count = NUM_ELEM(ctc_advertised_versions),
+ .supp_version = ctc_advertised_versions
+ }
+ }
+};
+
+void ctc_tx_add_tlv(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam)
+{
+ ctc_state *ctc = link_state->org_spec_state;
+
+ if (ctc != NULL)
+ {
+ switch (ctc->state)
+ {
+ case CTC_NEG_ADVERTISING:
+ oam->u.info.tlvs[oam->u.info.tlvs_count].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value = ctc_tx_advertise_tlv;
+ oam->u.info.tlvs_count++;
+ break;
+ case CTC_NEG_CONFIRMING:
+ oam->u.info.tlvs[oam->u.info.tlvs_count].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value.oui =
+ BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value.u.ctc.extension_support =
+ CTC_EXTENSION_SUPPORT;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value.u.ctc.version =
+ ctc->selected_version;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value.u.ctc.supp_version_count = 0;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value.u.ctc.supp_version = NULL;
+ oam->u.info.tlvs_count++;
+ break;
+ case CTC_NEG_DONE:
+ break; /* don't add a TLV */
+ default:
+ EON_LINK_LOG(ERROR, &link_state->link_key, "Unknown CTC state %d\n", ctc->state);
+ break;
+ }
+ }
+}
+
+void ctc_rx_tlv(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc)
+{
+ *rc = BCM_ERR_IN_PROGRESS;
+
+ if (link_state->org_spec_state != NULL)
+ {
+ ctc_state *ctc = link_state->org_spec_state;
+
+ switch (ctc->state)
+ {
+ case CTC_NEG_ADVERTISING:
+ if ((NULL == org_spec) || (BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC != org_spec->oui))
+ {
+ EON_LINK_LOG(INFO, &link_state->link_key, "Didn't get CTC advertise TLV\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ /* find highest common version */
+ ctc->selected_version = 0;
+ for (uint8_t i = 0; i < org_spec->u.ctc.supp_version_count; i++)
+ {
+ for (uint8_t j = 0; j < NUM_ELEM(ctc_advertised_versions); j++)
+ {
+ if ((org_spec->u.ctc.supp_version[i].oui == ctc_advertised_versions[j].oui) &&
+ (org_spec->u.ctc.supp_version[i].version == ctc_advertised_versions[j].version))
+ {
+ if (ctc_advertised_versions[j].version > ctc->selected_version)
+ {
+ ctc->selected_version = ctc_advertised_versions[j].version;
+ }
+ }
+ }
+ }
+ if (ctc->selected_version == 0)
+ {
+ *rc = BCM_ERR_NOT_SUPPORTED; /* no common version found */
+ }
+ else
+ {
+ ctc->state = CTC_NEG_CONFIRMING;
+ }
+ }
+ break;
+ case CTC_NEG_CONFIRMING:
+ if ((NULL == org_spec) || (BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC != org_spec->oui))
+ {
+ EON_LINK_LOG(INFO, &link_state->link_key, "Didn't get CTC confirm TLV\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ if (org_spec->u.ctc.supp_version_count == 0)
+ {
+ if (org_spec->u.ctc.version != ctc->selected_version)
+ {
+ *rc = BCM_ERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ ctc->state = CTC_NEG_DONE;
+ }
+ }
+ }
+ break;
+ case CTC_NEG_DONE:
+ if ((NULL != org_spec) && (org_spec->oui == BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC))
+ {
+ EON_LINK_LOG(
+ INFO, &link_state->link_key, "Got unexpected CTC TLV after negotiation\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ *rc = BCM_ERR_OK;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if ((NULL != org_spec) && (org_spec->oui == BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC))
+ {
+ EON_LINK_LOG(
+ INFO, &link_state->link_key, "Got unexpected CTC TLV before negotiation\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+
+ if (LOCAL_STABLE_REMOTE_STABLE == link_state->oam_state)
+ {
+ ctc_state *ctc = bcmos_calloc(sizeof(ctc_state));
+ ctc->state = CTC_NEG_ADVERTISING;
+ link_state->org_spec_state = ctc;
+ }
+ }
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/ctc/ctc.h b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/ctc/ctc.h
new file mode 100644
index 0000000..a2107b4
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/ctc/ctc.h
@@ -0,0 +1,42 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_EON_CTC_H_
+#define _BCMOLT_EON_CTC_H_
+
+void ctc_tx_add_tlv(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam);
+
+void ctc_rx_tlv(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc);
+
+#endif // _BCMOLT_EON_CTC_H_
+
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/dpoe/dpoe.c b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/dpoe/dpoe.c
new file mode 100644
index 0000000..6b0acf1
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/dpoe/dpoe.c
@@ -0,0 +1,122 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file contains the pieces of the OAM negotiation state machine that are specific to DPoE OAM. */
+
+#include "bcmolt_eon_private.h"
+#include "../oam_common.h"
+#include "dpoe.h"
+
+/* Org specific TLV to send to the ONU */
+static const bcmolt_epon_oam_organization_specific_info dpoe_tx_tlv =
+{
+ .oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE,
+ .u =
+ {
+ .dpoe =
+ {
+ .dpoe_info_tlv =
+ {
+ .type = BCMOLT_EPON_OAM_DPOE_INFO_TLV_TYPE_DPOE_OAM_SUPPORT,
+ .u =
+ {
+ .dpoe_oam_support =
+ {
+ .dpoe_oam_version = 0x11
+ }
+ }
+ }
+ }
+ }
+};
+
+void dpoe_tx_add_tlv(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam)
+{
+ if ((LOCAL_EVAL_REMOTE_EVAL == link_state->oam_state) ||
+ (LOCAL_STABLE_REMOTE_EVAL == link_state->oam_state))
+ {
+ oam->u.info.tlvs[oam->u.info.tlvs_count].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC;
+ oam->u.info.tlvs[oam->u.info.tlvs_count].u.organization_specific.value = dpoe_tx_tlv;
+ oam->u.info.tlvs_count++;
+ }
+}
+
+void dpoe_rx_tlv(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc)
+{
+ if (link_state->oam_state == LOCAL_STABLE_REMOTE_STABLE)
+ {
+ if (NULL != org_spec)
+ {
+ EON_LINK_LOG(INFO, &link_state->link_key, "Got unexpected organization specific TLV\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ }
+ else
+ {
+ if ((NULL == org_spec) ||
+ (BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE != org_spec->oui) ||
+ (BCMOLT_EPON_OAM_DPOE_INFO_TLV_TYPE_DPOE_OAM_SUPPORT != org_spec->u.dpoe.dpoe_info_tlv.type))
+ {
+ EON_LINK_LOG(INFO, &link_state->link_key, "Didn't get DPoE TLV\n");
+ *rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ switch (org_spec->u.dpoe.dpoe_info_tlv.u.dpoe_oam_support.dpoe_oam_version)
+ {
+ case 0x22:
+ case 0x21:
+ case 0x20:
+ case 0x11: /* DPoE v1.0 I05 and higher */
+ break;
+ case 0x01: /* backwards compatibility, same as 0x10 */
+ case 0x02: /* pre-DPoE OAM, no Certificate Authority support */
+ case 0x03: /* pre-DPoE OAM, with Certificate Authority support */
+ case 0x10: /* DPoE v1.0 I04 and lower */
+ EON_LINK_LOG(
+ WARNING, &link_state->link_key,
+ "DPoE TLV contained old version: %u (some features may not work)\n",
+ org_spec->u.dpoe.dpoe_info_tlv.u.dpoe_oam_support.dpoe_oam_version);
+ break;
+ case 0x00: /* workaround for EASW-17839 */
+ break;
+ default:
+ EON_LINK_LOG(
+ ERROR, &link_state->link_key, "DPoE TLV contained bad version: %u\n",
+ org_spec->u.dpoe.dpoe_info_tlv.u.dpoe_oam_support.dpoe_oam_version);
+ *rc = BCM_ERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ }
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/dpoe/dpoe.h b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/dpoe/dpoe.h
new file mode 100644
index 0000000..2b6d6f2
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/dpoe/dpoe.h
@@ -0,0 +1,41 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_EON_DPOE_H_
+#define _BCMOLT_EON_DPOE_H_
+
+void dpoe_tx_add_tlv(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam);
+
+void dpoe_rx_tlv(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc);
+
+#endif // _BCMOLT_EON_DPOE_H_
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/oam_common.c b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/oam_common.c
new file mode 100644
index 0000000..cd92c92
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/oam_common.c
@@ -0,0 +1,361 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file contains the pieces of the OAM negotiation state machine that are common to all OAM. */
+
+#include "oam_common.h"
+#include "bcmolt_utils.h"
+#include "bcmolt_math.h"
+#include "bcmolt_eon_private.h"
+#include "bcmolt_user_appl_epon_oam.h"
+#include "dpoe/dpoe.h"
+#include "brcm/brcm.h"
+#include "ctc/ctc.h"
+
+static const oam_info_add_tlv add_org_spec_tlv[EON_OAM_SET_ID_COUNT] =
+{
+ [EON_OAM_SET_ID_DPOE] = dpoe_tx_add_tlv,
+ [EON_OAM_SET_ID_BRCM] = brcm_tx_add_tlv,
+ [EON_OAM_SET_ID_CTC] = ctc_tx_add_tlv
+};
+
+static const oam_rx_org_spec_tlv rx_org_spec_tlv[EON_OAM_SET_ID_COUNT] =
+{
+ [EON_OAM_SET_ID_DPOE] = dpoe_rx_tlv,
+ [EON_OAM_SET_ID_BRCM] = brcm_rx_tlv,
+ [EON_OAM_SET_ID_CTC] = ctc_rx_tlv
+};
+
+static uint8_t get_oam_version(void)
+{
+ return 0x01; /* only version currently supported */
+}
+
+static void fill_local_tlv(const eon_link_state *link_state, bcmolt_epon_oam_local_remote_info *tlv)
+{
+ tlv->oam_version = get_oam_version();
+ tlv->revision = link_state->revision;
+ tlv->state =
+ BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_STATE_PARSER_ACTION1 |
+ BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_STATE_MULTIPLEXER_ACTION;
+ tlv->oam_config =
+ BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_CONFIG_OAM_MODE |
+ BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_CONFIG_UNIDIRECTIONAL_SUPPORT |
+ BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_CONFIG_LINK_EVENTS;
+ tlv->max_pdu_size = link_state->max_pdu_size;
+ tlv->oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_TEK;
+ tlv->vendor_specific_information[0] = 0x06;
+ tlv->vendor_specific_information[1] = 0x86;
+ tlv->vendor_specific_information[2] = 0x20;
+ tlv->vendor_specific_information[3] = 0xA0;
+}
+
+static void dump_std_info_tlv(const eon_link_key *link_key, const char *type, bcmolt_epon_oam_local_remote_info *info)
+{
+ EON_LINK_LOG(
+ DEBUG,
+ link_key,
+ "%s: ver %02x rev %04x state %02x cfg %02x pdu %5u oui %06x vendor %02x%02x%02x%02x\n",
+ type,
+ info->oam_version,
+ info->revision,
+ info->state,
+ info->oam_config,
+ info->max_pdu_size,
+ info->oui,
+ info->vendor_specific_information[0],
+ info->vendor_specific_information[1],
+ info->vendor_specific_information[2],
+ info->vendor_specific_information[3]);
+}
+
+static void dump_org_spec_tlv(const eon_link_key *link_key, bcmolt_epon_oam_organization_specific_info *org_spec)
+{
+ switch (org_spec->oui)
+ {
+ case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_TEK:
+ if (BCMOLT_EPON_OAM_TEK_INFO_TLV_TYPE_EXTENSION_SUPPORT == org_spec->u.tek.tlvs.type)
+ {
+ EON_LINK_LOG(
+ DEBUG, link_key, "BRCM Ext Support: ver %02x rpt %02x pref %02x\n",
+ org_spec->u.tek.tlvs.u.extension_support.version,
+ org_spec->u.tek.tlvs.u.extension_support.report_mode,
+ org_spec->u.tek.tlvs.u.extension_support.preferred_report_mode);
+ }
+ break;
+ case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE:
+ if (BCMOLT_EPON_OAM_DPOE_INFO_TLV_TYPE_DPOE_OAM_SUPPORT == org_spec->u.dpoe.dpoe_info_tlv.type)
+ {
+ EON_LINK_LOG(
+ DEBUG, link_key, "DPoE Support: ver %02x\n",
+ org_spec->u.dpoe.dpoe_info_tlv.u.dpoe_oam_support.dpoe_oam_version);
+ }
+ break;
+ case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC:
+ EON_LINK_LOG(
+ DEBUG, link_key, "CTC Extension Support %u, Preferred Version 0x%02x, Supports (%u):\n",
+ org_spec->u.ctc.extension_support,
+ org_spec->u.ctc.version,
+ org_spec->u.ctc.supp_version_count);
+ for (uint32_t i = 0; i < org_spec->u.ctc.supp_version_count; i++)
+ {
+ EON_LINK_LOG(
+ DEBUG, link_key, "\tOUI %06x, Version 0x%02x\n",
+ org_spec->u.ctc.supp_version[i].oui, org_spec->u.ctc.supp_version[i].version);
+ }
+ break;
+ default:
+ EON_LINK_LOG(WARNING, link_key, "No support for %06x\n", org_spec->oui);
+ break;
+ }
+}
+
+static void dump_info_frame(const bcmolt_epon_oam_ethernet_frame *oam, const eon_link_state* link_state)
+{
+ static const char* entname[] = {"ONU","OLT"};
+ char da_buf[MAC_STR_LEN];
+ char sa_buf[MAC_STR_LEN];
+ bcmolt_epon_oam_oam_flags flags = oam->protocols[0].u.slow_protocol.value.u.oam.flags;
+ uint8_t is_olt = (0 == memcmp(link_state->epon_ni_mac_addr.u8, oam->sa.u8, BCMOS_ETH_ALEN));
+ uint8_t olt_stab = 0 !=
+ (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE : BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE));
+ uint8_t onu_stab = 0 !=
+ (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE : BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE));
+ uint8_t olt_eval = 0 !=
+ (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING : BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING));
+ uint8_t onu_eval = 0 !=
+ (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING : BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING));
+
+ EON_LINK_LOG(
+ DEBUG, &link_state->link_key, "from %s: OLT %s ONU %s\n", entname[is_olt],
+ olt_stab ? "stab" : (olt_eval ? "eval" : "NONE"),
+ onu_stab ? "stab" : (onu_eval ? "eval" : "NONE"));
+
+ EON_LINK_LOG(DEBUG, &link_state->link_key, " da sa eth su flgs cd\n");
+ EON_LINK_LOG(
+ DEBUG, &link_state->link_key, "%s %s %04x %02x %04x %02x\n",
+ bcmos_mac_2_str(&oam->da, da_buf), bcmos_mac_2_str(&oam->sa, sa_buf),
+ oam->protocols[0].ethertype, oam->protocols[0].u.slow_protocol.value.subtype,
+ oam->protocols[0].u.slow_protocol.value.u.oam.flags,
+ oam->protocols[0].u.slow_protocol.value.u.oam.content.code);
+ EON_LINK_LOG(
+ DEBUG, &link_state->link_key, "TLVS: (%u)\n",
+ oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs_count);
+ for (uint8_t i = 0; i < oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs_count; i++)
+ {
+ switch (oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].type)
+ {
+ case BCMOLT_EPON_OAM_INFO_TLV_TYPE_LOCAL:
+ dump_std_info_tlv(
+ &link_state->link_key,
+ "LOCAL",
+ &oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].u.local.info);
+ break;
+ case BCMOLT_EPON_OAM_INFO_TLV_TYPE_REMOTE:
+ dump_std_info_tlv(
+ &link_state->link_key,
+ "REMOTE",
+ &oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].u.remote.info);
+ break;
+ case BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC:
+ dump_org_spec_tlv(
+ &link_state->link_key,
+ &oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].u.organization_specific.value);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bcmos_errno build_tx_info_frame(eon_link_state *link_state, eon_frame_data *frame_data)
+{
+ bcmos_bool have_remote_info = link_state->remote_info.oam_version == get_oam_version();
+ bcmolt_epon_oam_ethernet_frame oam_frame = {};
+ bcmolt_epon_oam_ethernet_protocol protocol = {};
+ bcmolt_epon_oam_info_tlv_base tlvs[4] = {};
+
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "BUILDING INFO FRAME, current state %04x\n", link_state->oam_state);
+
+ oam_frame.da = slow_protocol_multicast_mac;
+ oam_frame.sa = link_state->epon_ni_mac_addr;
+ oam_frame.protocols_count = 1;
+ oam_frame.protocols = &protocol;
+ protocol.ethertype = BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL;
+ protocol.u.slow_protocol.value.subtype = BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM;
+ protocol.u.slow_protocol.value.u.oam.flags = link_state->oam_state;
+ protocol.u.slow_protocol.value.u.oam.content.code = BCMOLT_EPON_OAM_OAM_OPCODE_INFO;
+ protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count = 1;
+ protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs = tlvs;
+ tlvs[0].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_LOCAL;
+ fill_local_tlv(link_state, &tlvs[0].u.local.info);
+ if (have_remote_info)
+ {
+ tlvs[1].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_REMOTE;
+ tlvs[1].u.remote.info = link_state->remote_info;
+ protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count++;
+ }
+ add_org_spec_tlv[link_state->oam_set](link_state, &protocol.u.slow_protocol.value.u.oam.content);
+ tlvs[protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_END;
+ protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count++;
+
+ dump_info_frame(&oam_frame, link_state);
+
+ return epon_oam_pack_frame(&oam_frame, &frame_data->payload, &frame_data->length);
+}
+
+/* OK = done
+ in_progress = send next frame
+ parse = ignore
+ other = stop
+ */
+bcmos_errno handle_rx_info_frame(eon_link_state *link_state, uint16_t rx_length, uint8_t *rx_payload)
+{
+ bcmos_errno rc = BCM_ERR_IN_PROGRESS;
+ bcmolt_epon_oam_oam_flags start_state;
+ bcmolt_epon_oam_ethernet_frame *frame;
+ bcmolt_epon_oam_slow_protocol *sp;
+ bcmolt_epon_oam_organization_specific_info *org_spec = NULL;
+
+ start_state = link_state->oam_state;
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "RECEIVED INFO FRAME, current state %04x\n", start_state);
+
+ frame = epon_oam_unpack(link_state->link_key.device_id, rx_length, rx_payload);
+
+ if ((frame->protocols_count == 0) ||
+ (frame->protocols[0].ethertype != BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL) ||
+ (frame->protocols[0].u.slow_protocol.value.subtype != BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM) ||
+ (frame->protocols[0].u.slow_protocol.value.u.oam.content.code != BCMOLT_EPON_OAM_OAM_OPCODE_INFO))
+ {
+ /* not an info frame, ignore */
+ bcmos_free(frame);
+ return BCM_ERR_PARSE;
+ }
+
+ dump_info_frame(frame, link_state);
+ sp = &frame->protocols[0].u.slow_protocol.value;
+
+ /* capture link's TLV for subsequent retransmission */
+ if ((sp->u.oam.content.u.info.tlvs_count > 0) &&
+ (sp->u.oam.content.u.info.tlvs[0].type == BCMOLT_EPON_OAM_INFO_TLV_TYPE_LOCAL))
+ {
+ link_state->remote_info = sp->u.oam.content.u.info.tlvs[0].u.local.info;
+ }
+ else
+ {
+ bcmos_free(frame);
+ return BCM_ERR_ONU_ERR_RESP; /* no local info */
+ }
+
+ if (0 != memcmp(frame->da.u8, slow_protocol_multicast_mac.u8, sizeof(slow_protocol_multicast_mac)))
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ EON_LINK_LOG(WARNING, &link_state->link_key,
+ "DA mismatch "BCMOS_MACADDR_FMT_STR" expected vs. "BCMOS_MACADDR_FMT_STR" actual\n",
+ BCMOS_MACADDR_PARAMS(&slow_protocol_multicast_mac), BCMOS_MACADDR_PARAMS(&frame->da));
+ }
+
+ if (0 != memcmp(frame->sa.u8, &link_state->link_key.link.mac_address, sizeof(frame->sa)))
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ EON_LINK_LOG(WARNING, &link_state->link_key,
+ "SA mismatch "BCMOS_MACADDR_FMT_STR" expected vs. "BCMOS_MACADDR_FMT_STR" actual\n",
+ BCMOS_MACADDR_PARAMS(&link_state->link_key.link.mac_address), BCMOS_MACADDR_PARAMS(&frame->sa));
+ }
+
+ if (sp->u.oam.content.u.info.tlvs[0].u.local.info.oam_version != get_oam_version())
+ {
+ rc = BCM_ERR_ONU_ERR_RESP;
+ EON_LINK_LOG(WARNING, &link_state->link_key, "oam version mismatch 0x%02x expected vs. 0x%02x actual\n",
+ get_oam_version(), sp->u.oam.content.u.info.tlvs[0].u.local.info.oam_version);
+ }
+
+ /* Is the far end echoing the TLV we sent? */
+ if ((sp->u.oam.content.u.info.tlvs_count > 1) &&
+ (sp->u.oam.content.u.info.tlvs[1].type == BCMOLT_EPON_OAM_INFO_TLV_TYPE_REMOTE))
+ {
+ if (sp->u.oam.content.u.info.tlvs[0].u.local.info.max_pdu_size !=
+ sp->u.oam.content.u.info.tlvs[1].u.remote.info.max_pdu_size)
+ {
+ link_state->max_pdu_size = MIN(
+ sp->u.oam.content.u.info.tlvs[0].u.local.info.max_pdu_size,
+ sp->u.oam.content.u.info.tlvs[1].u.remote.info.max_pdu_size);
+ link_state->revision++;
+ }
+ else
+ {
+ bcmolt_epon_oam_local_remote_info loc_tlv = {};
+ fill_local_tlv(link_state, &loc_tlv);
+ if (0 == memcmp(&loc_tlv, &sp->u.oam.content.u.info.tlvs[1].u.remote.info, sizeof(loc_tlv)))
+ {
+ link_state->oam_state = (link_state->oam_state | BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE) &
+ (~BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING);
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "setting LOCAL STABLE flag\n");
+ }
+ else
+ {
+ EON_LINK_LOG(WARNING, &link_state->link_key, "Remote TLV from ONU does NOT match what we sent\n");
+ }
+ }
+ }
+
+ if (sp->u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING)
+ {
+ link_state->oam_state = (link_state->oam_state | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING) &
+ (~BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE);
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "setting REMOTE EVAL flag\n");
+ }
+
+ if (sp->u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE)
+ {
+ link_state->oam_state = (link_state->oam_state | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE) &
+ (~BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING);
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "setting REMOTE STABLE flag\n");
+ }
+
+ if (LOCAL_STABLE_REMOTE_STABLE == link_state->oam_state)
+ {
+ rc = BCM_ERR_OK; /* basic negotiation complete */
+ }
+
+ if ((sp->u.oam.content.u.info.tlvs_count > 2) &&
+ (sp->u.oam.content.u.info.tlvs[2].type == BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC))
+ {
+ org_spec = &sp->u.oam.content.u.info.tlvs[2].u.organization_specific.value;
+ }
+ rx_org_spec_tlv[link_state->oam_set](link_state, org_spec, &rc);
+
+ EON_LINK_LOG(DEBUG, &link_state->link_key, "state %04x -> %04x (%s)\n",
+ start_state, link_state->oam_state, bcmos_strerror(rc));
+
+ bcmos_free(frame);
+
+ return rc;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/oam_common.h b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/oam_common.h
new file mode 100644
index 0000000..50f08b2
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/eon/oam_sets/oam_common.h
@@ -0,0 +1,56 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file contains the pieces of the OAM negotiation state machine that are common to all OAM. */
+
+#ifndef _OAM_COMMON_H_
+#define _OAM_COMMON_H_
+
+#include "bcmolt_eon_private.h"
+
+#define LOCAL_EVAL_REMOTE_EVAL\
+ (BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING)
+#define LOCAL_STABLE_REMOTE_EVAL\
+ (BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING)
+#define LOCAL_STABLE_REMOTE_STABLE\
+ (BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE)
+
+typedef void (*oam_info_add_tlv)(eon_link_state *link_state, bcmolt_epon_oam_oam_pdu_content *oam);
+
+typedef void (*oam_rx_org_spec_tlv)(
+ eon_link_state *link_state,
+ bcmolt_epon_oam_organization_specific_info *org_spec,
+ bcmos_errno *rc);
+
+bcmos_errno build_tx_info_frame(eon_link_state *link_state, eon_frame_data *frame_data);
+
+bcmos_errno handle_rx_info_frame(eon_link_state *link_state, uint16_t rx_length, uint8_t *rx_payload);
+
+#endif // _OAM_COMMON_H
+
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_hde/Makefile b/bcm68620_release/release/host_reference/user_appl/epon_hde/Makefile
new file mode 100644
index 0000000..36f10f1
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_hde/Makefile
@@ -0,0 +1,11 @@
+# EPON Host Driven Encryption
+
+ifeq ("$(ENABLE_CLI)", "y")
+
+ MOD_NAME = bcm_user_appl_epon_hde
+ MOD_TYPE = lib
+ MOD_DEPS = host_api
+
+ srcs = bcmolt_epon_hde.c encrypt_oam.c
+
+endif
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_hde/bcmolt_epon_hde.c b/bcm68620_release/release/host_reference/user_appl/epon_hde/bcmolt_epon_hde.c
new file mode 100644
index 0000000..60cb073
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_hde/bcmolt_epon_hde.c
@@ -0,0 +1,693 @@
+/*
+ <: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 <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcmcli_session.h>
+#include <bcmolt_msg_pack.h>
+#include <bcm_dev_log.h>
+#include <encrypt_oam.h>
+#include <oam_defs.h>
+#include "bcmolt_epon_hde.h"
+
+#define HDE_TASK_MSG_Q_SIZE 64
+#define ENC_KEY_LENGTH_KEY 16
+#define HDE_MAX_NUM_LINKS 512
+#define KEY_EXCHANGE_PERIOD 15
+
+typedef enum
+{
+ HDE_EVENT_ENABLE_ENCRYPTION,
+ HDE_EVENT_DISABLE_ENCRYPTION,
+ HDE_EVENT_FRAME_RX,
+ HDE_EVENT_OAM_TIMEOUT,
+ HDE_EVENT__NUM_OF
+} hde_event;
+
+typedef enum
+{
+ HDE_STATE_NOT_ENCRYPTED,
+ HDE_STATE_ENCRYPTED,
+ HDE_STATE_WAITING_FOR_OAM_ACK,
+ HDE_STATE__NUM_OF
+} hde_state;
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ bcmolt_msg *rx;
+ hde_key key;
+ hde_event event;
+} hde_task_msg;
+
+typedef struct
+{
+ hde_key hde_link;
+ hde_state state;
+ bcmos_timer oam_timeout_timer;
+ bcmolt_epon_link_rate link_rate;
+ bcmolt_epon_llid llid;
+ bcmos_mac_address corresponding_pon_port_mac;
+} hde_link_data;
+
+static struct
+{
+ bcmos_task task;
+ dev_log_id log_id[BCMTR_MAX_OLTS];
+ hde_link_data *links;
+ uint16_t num_links;
+} hde_data;
+
+static bcmos_bool is_running = BCMOS_FALSE;
+
+typedef void (*link_state_handler)(hde_link_data *hde_link, const bcmolt_msg *ind);
+
+static const char *get_state_string(hde_state state)
+{
+ switch (state)
+ {
+ case HDE_STATE_NOT_ENCRYPTED:
+ return "not encrypted";
+ case HDE_STATE_ENCRYPTED:
+ return "encrypted";
+ case HDE_STATE_WAITING_FOR_OAM_ACK:
+ return "waiting for oam ack";
+ default:
+ return "UNKNOWN STATE!!!";
+ }
+}
+
+static bcmos_errno set_new_key(const hde_key *hde_link, bcmolt_encryption_information_container *new_key, bcmolt_epon_key_choice key_choice, bcmolt_epon_encryption_mode mode)
+{
+ bcmolt_epon_link_cfg link_cfg;
+ bcmolt_epon_encryption_config encryption_config = { };
+
+ // Up/down format must match even when doing DS only
+ encryption_config.upstream_encryption_information.format = new_key->format;
+
+ encryption_config.downstream_encryption_information = *new_key;
+ encryption_config.downstream_key_choice = key_choice;
+ encryption_config.downstream_mode = mode;
+ bcmolt_epon_link_key link_key =
+ {
+ .epon_ni = hde_link->epon_ni,
+ .mac_address = hde_link->mac_addr
+ };
+ BCMOLT_CFG_INIT(&link_cfg, epon_link, link_key);
+ BCMOLT_CFG_PROP_SET(&link_cfg, epon_link, epon_encryption, encryption_config);
+ return bcmolt_cfg_set(hde_link->device_id, &link_cfg.hdr);
+}
+
+static bcmos_errno set_pon_encryption_if_needed(const hde_key *hde_link)
+{
+ // First check the current state of the PON's encryption mode.
+ bcmos_errno rc;
+ bcmolt_epon_ni_cfg pon_cfg;
+ bcmolt_epon_ni_key pon_key = { .epon_ni = hde_link->epon_ni };
+ BCMOLT_CFG_INIT(&pon_cfg, epon_ni, pon_key);
+ BCMOLT_CFG_PROP_GET(&pon_cfg, epon_ni, encryption_cfg);
+ rc = bcmolt_cfg_get(hde_link->device_id, &pon_cfg.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->device_id], "Failure - Cannot query PON encryption mode!.\n");
+ return rc;
+ }
+
+ if (pon_cfg.data.encryption_cfg.downstream_encryption_mode != BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES)
+ {
+ // Encryption mode is not correctly set on the PON, so set it now.
+ pon_cfg.data.encryption_cfg.downstream_encryption_mode = BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES;
+ pon_cfg.data.encryption_cfg.upstream_encryption_mode = BCMOLT_EPON_ENCRYPTION_MODE_NO_ENCRYPTION;
+ rc = bcmolt_cfg_set(hde_link->device_id, &pon_cfg.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->device_id], "Failure - Cannot set PON encryption mode!.\n");
+ return rc;
+ }
+ }
+
+ return BCM_ERR_OK;
+}
+
+static bcmolt_epon_link_rate get_link_rate(const hde_key *hde_link)
+{
+ bcmos_errno rc;
+ bcmolt_epon_link_cfg link_cfg;
+ bcmolt_epon_link_key link_key =
+ {
+ .epon_ni = hde_link->epon_ni,
+ .mac_address = hde_link->mac_addr
+ };
+ BCMOLT_CFG_INIT(&link_cfg, epon_link, link_key);
+ BCMOLT_CFG_PROP_GET(&link_cfg, epon_link, link_rate);
+ rc = bcmolt_cfg_get(hde_link->device_id, &link_cfg.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(
+ ERROR,
+ hde_data.log_id[hde_link->device_id],
+ "Failure - cannot query link rate for link %02x:%02x:%02x:%02x:%02x:%02x!.\n",
+ hde_link->mac_addr.u8[0],
+ hde_link->mac_addr.u8[1],
+ hde_link->mac_addr.u8[2],
+ hde_link->mac_addr.u8[3],
+ hde_link->mac_addr.u8[4],
+ hde_link->mac_addr.u8[5]);
+ }
+ return link_cfg.data.link_rate;
+}
+
+static bcmos_mac_address get_pon_mac(const hde_key *hde_link)
+{
+ bcmos_errno rc;
+ bcmolt_epon_ni_cfg pon_cfg;
+ bcmolt_epon_ni_key pon_key = { .epon_ni = hde_link->epon_ni };
+ BCMOLT_CFG_INIT(&pon_cfg, epon_ni, pon_key);
+ BCMOLT_CFG_PROP_GET(&pon_cfg, epon_ni, mac_address);
+ rc = bcmolt_cfg_get(hde_link->device_id, &pon_cfg.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->device_id], "Failure - Cannot query PON mac address!.\n");
+ }
+ return pon_cfg.data.mac_address;
+}
+
+static bcmolt_epon_llid get_llid(const hde_key *hde_link)
+{
+ bcmos_errno rc;
+ bcmolt_epon_link_cfg link_cfg;
+ bcmolt_epon_link_key link_key =
+ {
+ .epon_ni = hde_link->epon_ni,
+ .mac_address = hde_link->mac_addr
+ };
+ BCMOLT_CFG_INIT(&link_cfg, epon_link, link_key);
+ BCMOLT_CFG_PROP_GET(&link_cfg, epon_link, llid);
+ rc = bcmolt_cfg_get(hde_link->device_id, &link_cfg.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(
+ ERROR,
+ hde_data.log_id[hde_link->device_id],
+ "Failure - cannot query LLID for link %02x:%02x:%02x:%02x:%02x:%02x!.\n",
+ hde_link->mac_addr.u8[0],
+ hde_link->mac_addr.u8[1],
+ hde_link->mac_addr.u8[2],
+ hde_link->mac_addr.u8[3],
+ hde_link->mac_addr.u8[4],
+ hde_link->mac_addr.u8[5]);
+ }
+ return link_cfg.data.llid;
+}
+
+/* By spec the SCI is the LLID of the link appended to the corresponding epon port mac address */
+static void create_sci(const bcmos_mac_address *epon_port_mac, bcmolt_epon_llid llid, uint8_t *sci)
+{
+ sci[7] = (uint8_t) llid;
+ sci[6] = (uint8_t) (llid >> 8);
+ sci[5] = epon_port_mac->u8[5];
+ sci[4] = epon_port_mac->u8[4];
+ sci[3] = epon_port_mac->u8[3];
+ sci[2] = epon_port_mac->u8[2];
+ sci[1] = epon_port_mac->u8[1];
+ sci[0] = epon_port_mac->u8[0];
+}
+
+static void handle_not_encrypted_enable_encryption(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ dpoe_encrypt_mode mode = DPOE_ENCRYPT_MODE_NONE;
+ // Retreive useful link info and store it to save messages later.
+ hde_link->link_rate = get_link_rate(&hde_link->hde_link);
+ hde_link->corresponding_pon_port_mac = get_pon_mac(&hde_link->hde_link);
+ hde_link->llid = get_llid(&hde_link->hde_link);
+
+ if (hde_link->link_rate == BCMOLT_EPON_LINK_RATE_TEN_TEN || hde_link->link_rate == BCMOLT_EPON_LINK_RATE_TEN_ONE)
+ {
+ mode = DPOE_ENCRYPT_MODE_10DOWN;
+ }
+ else if (hde_link->link_rate == BCMOLT_EPON_LINK_RATE_ONE_ONE)
+ {
+ mode = DPOE_ENCRYPT_MODE_1DOWN;
+ }
+ else
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id], "Failure - UNKNOWN LINK RATE!!!.\n");
+ }
+
+ dpoe_encrypt_oam_set_request_send(
+ &hde_link->hde_link,
+ KEY_EXCHANGE_PERIOD,
+ mode,
+ hde_link->corresponding_pon_port_mac);
+
+ bcmos_timer_start(&hde_link->oam_timeout_timer, HDE_ENABLE_OAM_WAIT_US + OAM_TIMEOUT_GRACE_PERIOD_US);
+
+ hde_link->state = HDE_STATE_WAITING_FOR_OAM_ACK;
+}
+
+static void handle_encrypted_disable_encryption(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ bcmolt_encryption_information_container empty_key = { };
+ bcmos_errno rc;
+
+ // Turn off encryption on the embedded side.
+ rc = set_new_key(&hde_link->hde_link, &empty_key, BCMOLT_EPON_KEY_CHOICE_KEY_0, BCMOLT_EPON_ENCRYPTION_MODE_NO_ENCRYPTION);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id],
+ "Failure - Cannot communicate with the embedded!.\n");
+ }
+
+ // Send the disable OAM to the link. Don't bother waiting for a response.
+ dpoe_encrypt_oam_set_request_send(&hde_link->hde_link, 0, DPOE_ENCRYPT_MODE_NONE, get_pon_mac(&hde_link->hde_link));
+
+ hde_link->state = HDE_STATE_NOT_ENCRYPTED;
+}
+
+static void handle_encrypted_frame_rx(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ const bcmolt_epon_link_frame_captured *frame_captured = (const bcmolt_epon_link_frame_captured*) ind;
+ uint8_t new_key[16];
+ bcmolt_epon_key_choice key_choice;
+ bcmos_errno rc;
+ bcmolt_encryption_information_container key_for_embedded;
+ rc = dpoe_encrypt_oam_parse_new_key(&frame_captured->data.frame, new_key, &key_choice);
+ if (rc != BCM_ERR_OK)
+ {
+ // Not a DPoE new key, ignore it
+ return;
+ }
+
+ if (hde_link->link_rate == BCMOLT_EPON_LINK_RATE_TEN_TEN || hde_link->link_rate == BCMOLT_EPON_LINK_RATE_TEN_ONE)
+ {
+ uint8_t sci[8];
+ create_sci(&hde_link->corresponding_pon_port_mac, hde_link->llid, sci);
+ key_for_embedded.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR;
+ memcpy(key_for_embedded.u.ctr.key, new_key, sizeof(new_key));
+ memcpy(key_for_embedded.u.ctr.sci, sci, sizeof(sci));
+ }
+ else if (hde_link->link_rate == BCMOLT_EPON_LINK_RATE_ONE_ONE)
+ {
+ key_for_embedded.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CFB;
+ memcpy(key_for_embedded.u.cfb.key, new_key, sizeof(new_key));
+ }
+ else
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id], "Failure - UNKNOWN LINK RATE!!!.\n");
+ }
+
+ rc = set_new_key(&hde_link->hde_link, &key_for_embedded, key_choice, BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id],
+ "Failure - Cannot communicate with the embedded!.\n");
+ }
+}
+
+static void handle_waiting_for_oam_ack_oam_timeout(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id], "Failure - No response to initial OAM.\n");
+ hde_link->state = HDE_STATE_NOT_ENCRYPTED;
+}
+
+static void handle_waiting_for_oam_ack_frame_rx(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ const bcmolt_epon_link_frame_captured *frame_captured = (const bcmolt_epon_link_frame_captured*) ind;
+ uint8_t encrypt_mode_response;
+ uint8_t key_expiry_response;
+ bcmos_errno rc;
+
+ rc = dpoe_encrypt_oam_parse_set_response(&frame_captured->data.frame, &encrypt_mode_response, &key_expiry_response);
+ if (rc != BCM_ERR_OK)
+ {
+ // Not a encryption set reponse, ignore it
+ return;
+ }
+
+ /* 0 represents 'no error'. */
+ if ((encrypt_mode_response != 0) || (key_expiry_response != 0))
+ {
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id],
+ "Failure - The link refused one of the requested TLVs.\n");
+ hde_link->state = HDE_STATE_NOT_ENCRYPTED;
+ return;
+ }
+
+ // We have an appropriate response, so move on normally.
+ bcmos_timer_stop(&hde_link->oam_timeout_timer);
+ hde_link->state = HDE_STATE_ENCRYPTED;
+}
+
+static void handle_waiting_for_oam_ack_disable_encryption(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ hde_link->state = HDE_STATE_NOT_ENCRYPTED;
+}
+
+static void handle_ignore(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ // Literally do nothing
+}
+
+static void handle_error(hde_link_data *hde_link, const bcmolt_msg *ind)
+{
+ BCM_LOG(ERROR, hde_data.log_id[hde_link->hde_link.device_id], "Unexpected event in state %s\n",
+ get_state_string(hde_link->state));
+}
+
+static link_state_handler link_state_machine[HDE_STATE__NUM_OF][HDE_EVENT__NUM_OF] =
+{
+ [HDE_STATE_NOT_ENCRYPTED] =
+ {
+ [HDE_EVENT_ENABLE_ENCRYPTION] = handle_not_encrypted_enable_encryption,
+ [HDE_EVENT_DISABLE_ENCRYPTION] = handle_error,
+ [HDE_EVENT_FRAME_RX] = handle_ignore,
+ [HDE_EVENT_OAM_TIMEOUT] = handle_ignore,
+ },
+ [HDE_STATE_ENCRYPTED] =
+ {
+ [HDE_EVENT_ENABLE_ENCRYPTION] = handle_error,
+ [HDE_EVENT_DISABLE_ENCRYPTION] = handle_encrypted_disable_encryption,
+ [HDE_EVENT_FRAME_RX] = handle_encrypted_frame_rx,
+ [HDE_EVENT_OAM_TIMEOUT] = handle_ignore,
+ },
+ [HDE_STATE_WAITING_FOR_OAM_ACK] =
+ {
+ [HDE_EVENT_ENABLE_ENCRYPTION] = handle_error,
+ [HDE_EVENT_DISABLE_ENCRYPTION] = handle_waiting_for_oam_ack_disable_encryption,
+ [HDE_EVENT_FRAME_RX] = handle_waiting_for_oam_ack_frame_rx,
+ [HDE_EVENT_OAM_TIMEOUT] = handle_waiting_for_oam_ack_oam_timeout,
+ }
+};
+
+static int32_t find_table_index(const hde_key *hde_link)
+{
+ uint16_t i;
+ for (i = 0; i < hde_data.num_links; ++i)
+ {
+ if (memcmp(hde_link, &hde_data.links[i], sizeof(*hde_link)) == 0)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void remove_link_from_table(int32_t link_index)
+{
+ int32_t i;
+ for (i = link_index; i < hde_data.num_links - 1; ++i)
+ {
+ /* Backwards copy all data expect for the timer handle */
+ hde_data.links[i].hde_link = hde_data.links[i + 1].hde_link;
+ hde_data.links[i].link_rate = hde_data.links[i + 1].link_rate;
+ hde_data.links[i].llid = hde_data.links[i + 1].llid;
+ hde_data.links[i].state = hde_data.links[i + 1].state;
+ }
+ /* also clear the state of the now empty link */
+ hde_data.links[hde_data.num_links - 1].state = HDE_STATE_NOT_ENCRYPTED;
+ hde_data.num_links--;
+}
+
+static void run_state_machine(int32_t link_index, const hde_task_msg *task_msg)
+{
+ if (link_index < 0) // We don't know of this link yet.
+ {
+ if (hde_data.num_links == HDE_MAX_NUM_LINKS)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[task_msg->key.device_id], "Failure - Cannot add any more links!.\n");
+ return;
+ }
+
+ link_index = hde_data.num_links;
+ hde_data.links[link_index].hde_link = task_msg->key;
+ link_state_machine[hde_data.links[link_index].state][task_msg->event](&hde_data.links[link_index], task_msg->rx);
+
+ if (hde_data.links[link_index].state != HDE_STATE_NOT_ENCRYPTED)
+ {
+ // This link did not end in the state 'not encrypted', so we need to start tracking him.
+ ++hde_data.num_links;
+ }
+ }
+ else // We already know of this link.
+ {
+ link_state_machine[hde_data.links[link_index].state][task_msg->event](&hde_data.links[link_index], task_msg->rx);
+
+ if (hde_data.links[link_index].state == HDE_STATE_NOT_ENCRYPTED)
+ {
+ // The link has is no longer encrypted so lets forget about him.
+ remove_link_from_table(link_index);
+ }
+ }
+}
+
+static void hde_handle_event(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ hde_task_msg *task_msg = (hde_task_msg *) os_msg;
+
+ int32_t link_index;
+
+ link_index = find_table_index(&task_msg->key);
+ run_state_machine(link_index, task_msg);
+
+ if (task_msg->rx != NULL)
+ {
+ bcmolt_msg_free(task_msg->rx);
+ }
+ bcmos_free(task_msg);
+}
+
+static bcmos_timer_rc oam_response_timeout(bcmos_timer *timer, long data)
+{
+ hde_task_msg *task_msg = bcmos_calloc(sizeof(*task_msg));
+
+ BUG_UNLESS(task_msg);
+
+ task_msg->key = hde_data.links[data].hde_link;
+ task_msg->event = HDE_EVENT_OAM_TIMEOUT;
+ task_msg->os_msg.handler = hde_handle_event;
+
+ bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_EPON_HDE, &task_msg->os_msg, BCMOS_MSG_SEND_AUTO_FREE);
+ return BCMOS_TIMER_OK;
+}
+
+static bcmos_errno hde_set_encryption(hde_key* key, bcmos_bool enabled)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ hde_task_msg *task_msg = bcmos_calloc(sizeof(*task_msg));
+
+ BUG_UNLESS(task_msg);
+
+ task_msg->key = *key;
+ task_msg->event = enabled ? HDE_EVENT_ENABLE_ENCRYPTION : HDE_EVENT_DISABLE_ENCRYPTION;
+ task_msg->os_msg.handler = hde_handle_event;
+
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_EPON_HDE, &task_msg->os_msg, BCMOS_MSG_SEND_AUTO_FREE);
+ BUG_ON(rc != BCM_ERR_OK);
+ return rc;
+}
+
+static bcmos_errno hde_cli_enable(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ hde_key key =
+ {
+ .device_id = current_device,
+ .epon_ni = (bcmolt_epon_ni)(bcmcli_find_named_parm(session, "epon_ni")->value.number),
+ .mac_addr = (bcmos_mac_address)(bcmcli_find_named_parm(session, "mac_address")->value.mac)
+ };
+
+ rc = set_pon_encryption_if_needed(&key);
+ if (rc != BCM_ERR_OK)
+ {
+ return rc;
+ }
+ return hde_set_encryption(&key, BCMOS_TRUE);
+}
+
+static bcmos_errno hde_cli_disable(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+ hde_key key =
+ {
+ .device_id = current_device,
+ .epon_ni = (bcmolt_epon_ni)(bcmcli_find_named_parm(session, "epon_ni")->value.number),
+ .mac_addr = (bcmos_mac_address)(bcmcli_find_named_parm(session, "mac_address")->value.mac)
+ };
+
+ return hde_set_encryption(&key, BCMOS_FALSE);
+}
+
+// public indication handler interface -- called in transport layer context
+bcmos_errno bcmolt_epon_hde_process_rx(bcmolt_devid device_id, uint8_t instance, bcmolt_proxy_rx *rx)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ bcmolt_epon_link_frame_captured* actual_rx;
+ hde_task_msg *task_msg;
+ hde_key key;
+
+ if (!is_running)
+ {
+ return BCM_ERR_OK;
+ }
+
+ // We only look at message targetting epon links.
+ if (rx->hdr.obj_type != BCMOLT_OBJ_ID_EPON_LINK)
+ {
+ // Not an error, we just don't care about this object.
+ return BCM_ERR_OK;
+ }
+
+ // This is the only kind of proxy RX currently supported.
+ if (rx->hdr.subgroup != BCMOLT_EPON_LINK_PROXY_RX_ID_FRAME_CAPTURED)
+ {
+ return BCM_ERR_OK;
+ }
+
+ // This is safe due to the above subgroup check.
+ actual_rx = (bcmolt_epon_link_frame_captured*) rx;
+ key.device_id = device_id;
+ key.epon_ni = instance;
+ key.mac_addr = actual_rx->key.mac_address;
+
+ task_msg = bcmos_calloc(sizeof(*task_msg));
+ if (task_msg == NULL)
+ {
+ rc = BCM_ERR_NOMEM;
+ }
+ else
+ {
+ bcmolt_msg *rx_clone = NULL;
+ rc = bcmolt_msg_clone(&rx_clone, &rx->hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ bcmos_free(task_msg);
+ return rc;
+ }
+ actual_rx = (bcmolt_epon_link_frame_captured*) rx_clone;
+
+ task_msg->rx = rx_clone;
+ task_msg->key = key;
+ task_msg->event = HDE_EVENT_FRAME_RX;
+ task_msg->os_msg.handler = hde_handle_event;
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_EPON_HDE, &task_msg->os_msg, BCMOS_MSG_SEND_AUTO_FREE);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, hde_data.log_id[device_id], "Message send failed!\n");
+ bcmolt_msg_free(rx_clone);
+ }
+ }
+
+ return rc;
+}
+
+void bcmolt_epon_hde_appl_cli_init(bcmcli_entry *top_dir)
+{
+ static const char *dir_name = "hde";
+
+ if (bcmcli_dir_find(top_dir, dir_name))
+ {
+ return;
+ }
+
+ bcmcli_entry *dir = bcmcli_dir_add(top_dir, dir_name, "EPON OAM negotiation commands", BCMCLI_ACCESS_ADMIN, NULL);
+ BUG_ON(dir == NULL);
+
+ BCMCLI_MAKE_CMD(
+ dir,
+ "enable",
+ "Enable encryption for an EPON link",
+ hde_cli_enable,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "MAC address", BCMCLI_PARM_MAC, 0));
+
+ BCMCLI_MAKE_CMD(
+ dir,
+ "disable",
+ "Disable encryption for an EPON link",
+ hde_cli_disable,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "MAC address", BCMCLI_PARM_MAC, 0));
+}
+
+void bcmolt_epon_hde_appl_init(void)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+ uint32_t i;
+
+ bcmos_task_parm task_params =
+ {
+ .name = "user_appl_hde",
+ .priority = TASK_PRIORITY_USER_APPL_EPON_HDE,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+
+ bcmos_timer_parm timer_parm =
+ {
+ .name = "oam_timeout_timer",
+ .owner = BCMOS_MODULE_ID_NONE,
+ .periodic = BCMOS_FALSE,
+ .handler = oam_response_timeout
+ };
+
+ if (is_running)
+ {
+ return;
+ }
+
+ is_running = BCMOS_TRUE;
+
+ for (i=0; i<BCMTR_MAX_OLTS; i++)
+ {
+ char log_name[MAX_DEV_LOG_ID_NAME];
+ snprintf(log_name, sizeof(log_name)-1, "user_appl_hde_%d", i);
+ hde_data.log_id[i] = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_ERROR, DEV_LOG_ID_TYPE_BOTH);
+ }
+
+ hde_data.links = bcmos_calloc(HDE_MAX_NUM_LINKS * sizeof(hde_data.links[0]));
+ BUG_ON(hde_data.links == NULL);
+
+ rc = bcmos_task_create(&hde_data.task, &task_params);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ bcmos_module_parm module_params =
+ {
+ .qparm = { .name = "user_appl_hde", .size = HDE_TASK_MSG_Q_SIZE }
+ };
+ rc = bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_EPON_HDE, &hde_data.task, &module_params);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ for (i = 0; i < HDE_MAX_NUM_LINKS; ++i)
+ {
+ timer_parm.data = i;
+ rc = bcmos_timer_create(&hde_data.links[i].oam_timeout_timer, &timer_parm);
+ BUG_ON(rc != BCM_ERR_OK);
+ }
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_hde/bcmolt_epon_hde.h b/bcm68620_release/release/host_reference/user_appl/epon_hde/bcmolt_epon_hde.h
new file mode 100644
index 0000000..ce7f95b
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_hde/bcmolt_epon_hde.h
@@ -0,0 +1,55 @@
+/*
+ <: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.
+
+ :>
+ */
+
+#ifndef BCMOLT_EPON_HDE_H_
+#define BCMOLT_EPON_HDE_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcmolt_utils.h>
+#include <bcmcli.h>
+
+typedef struct
+{
+ bcmolt_devid device_id;
+ bcmolt_epon_ni epon_ni;
+ bcmos_mac_address mac_addr;
+} hde_key;
+
+// initialize the EPON OAM negotiation application (should be called as part of user application startup)
+void bcmolt_epon_hde_appl_init(void);
+
+// process an indication.
+bcmos_errno bcmolt_epon_hde_process_rx(bcmolt_devid device_id, uint8_t instance, bcmolt_proxy_rx *rx);
+
+// initialize HDE CLI
+void bcmolt_epon_hde_appl_cli_init(bcmcli_entry *top_dir);
+
+#endif /* BCMOLT_EPON_HDE_H_ */
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_hde/encrypt_oam.c b/bcm68620_release/release/host_reference/user_appl/epon_hde/encrypt_oam.c
new file mode 100644
index 0000000..13ef632
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_hde/encrypt_oam.c
@@ -0,0 +1,291 @@
+/*
+ <: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 "bcmolt_model_types.h"
+#include "bcmolt_api.h"
+#include "oam_defs.h"
+#include "bcmolt_math.h"
+#include <encrypt_oam.h>
+
+static const bcmos_mac_address slow_prot_mcast_mac =
+{
+ .u8 = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 }
+};
+
+static uint8_t oam_buffer[OAM_MAX_PACKET_SIZE];
+
+static bcmos_bool oam_var_add_tlv_header(bcmolt_buf *buf, oam_var_branch branch, uint16_t leaf, uint8_t len)
+{
+ bcmos_bool ok = BCMOS_TRUE;
+ uint8_t *snap = bcmolt_buf_snap_get(buf);
+
+ ok = ok && (len <= OAM_MAX_TLV_LENGTH);
+ ok = ok && bcmolt_buf_write_u8(buf, (uint8_t) branch);
+ ok = ok && bcmolt_buf_write_u16_be(buf, leaf);
+ ok = ok && bcmolt_buf_write_u8(buf, oam_var_tlv_length_encode(len));
+ ok = ok && (bcmolt_buf_get_remaining_size(buf) >= len);
+ if (!ok)
+ {
+ bcmolt_buf_snap_restore(buf, snap);
+ }
+ return ok;
+}
+
+static bcmos_bool dpoe_encrypt_tlv_add(bcmolt_buf *oambuf, uint16_t period, dpoe_encrypt_mode mode)
+{
+ bcmos_bool ok = BCMOS_TRUE;
+
+ // encryption mode TLV
+ ok = ok && oam_var_add_tlv_header(oambuf, OAM_VAR_BRANCH_DPOE_ATTRIBUTE, (uint16_t) OAM_DPOE_ATTR_ENCRYPT_MODE, 1);
+ ok = ok && bcmolt_buf_write_u8(oambuf, (uint8_t) mode);
+
+ /* Key Expiry Time TLV */
+ ok = ok && oam_var_add_tlv_header(oambuf, OAM_VAR_BRANCH_DPOE_ATTRIBUTE, (uint16_t) OAM_DPOE_ATTR_KEY_EXPIRY_TIME, 2);
+ ok = ok && bcmolt_buf_write_u16_be(oambuf, period);
+
+ return ok;
+}
+
+static bcmos_bool oam_pdu_build_common(bcmos_mac_address pon_mac, bcmolt_buf *outbuf)
+{
+ bcmos_bool ok = BCMOS_TRUE;
+
+ ok = ok && bcmolt_buf_write_mac_address(outbuf, slow_prot_mcast_mac); // DA
+ ok = ok && bcmolt_buf_write_mac_address(outbuf, pon_mac); // SA
+ ok = ok && bcmolt_buf_write_u16_be(outbuf, ETHERTYPE_SLOWPROTOCOLS); // Type
+ ok = ok && bcmolt_buf_write_u8(outbuf, (uint8_t) SLOW_PROTOCOL_SUBTYPE_OAM); // Subtype
+
+ return ok;
+}
+
+/* Sends OAM frame 'frame' to a link via the inject frame operation */
+static bcmos_bool oam_pdu_transmit(const hde_key* hde_link, uint8_t *frame, uint16_t len)
+{
+ bcmolt_ethernet_frame_unmasked oam_frame;
+ bcmolt_epon_link_inject_frame inject_frame;
+
+ oam_frame.frame_octets.len = len;
+ oam_frame.frame_octets.val = frame;
+ bcmolt_epon_link_key link_key =
+ {
+ .epon_ni = hde_link->epon_ni,
+ .mac_address = hde_link->mac_addr
+ };
+
+ BCMOLT_PROXY_INIT(&inject_frame, epon_link, inject_frame, link_key);
+ BCMOLT_PROXY_PROP_SET(&inject_frame, epon_link, inject_frame, frame, oam_frame);
+ if (bcmolt_proxy_send(hde_link->device_id, &inject_frame.hdr) != BCM_ERR_OK)
+ {
+ return BCMOS_FALSE;
+ }
+
+ return BCMOS_TRUE;
+}
+
+static bcmos_bool oam_var_get_next(bcmolt_buf *buf, oam_var_generic *oamvar)
+{
+ bcmos_bool ok = BCMOS_TRUE;
+ uint8_t branch = OAM_VAR_BRANCH_TERMINATION;
+
+ memset(oamvar, 0, sizeof(*oamvar));
+ ok = ok && (bcmolt_buf_get_remaining_size(buf) != 0);
+ ok = ok && bcmolt_buf_read_u8(buf, &branch); // Read the branch.
+ if (ok)
+ {
+ switch (branch)
+ {
+ case OAM_VAR_BRANCH_ATTRIBUTE:
+ case OAM_VAR_BRANCH_ACTION:
+ case OAM_VAR_BRANCH_DPOE_ACT:
+ case OAM_VAR_BRANCH_DPOE_ATTRIBUTE:
+ case OAM_VAR_BRANCH_DPOE_OBJECT:
+ oamvar->branch = (oam_var_branch) branch;
+ ok = ok && bcmolt_buf_read_u16_be(buf, &oamvar->leaf); // Read leaf
+ ok = ok && bcmolt_buf_read_u8(buf, &oamvar->width); // Read Width
+ oamvar->value = bcmolt_buf_snap_get(buf); // Keep the position of value
+ oamvar->width = oam_var_tlv_length_encode(oamvar->width); // Re-encode width via OAM spec
+ ok = ok && bcmolt_buf_skip(buf, oamvar->width); // Skip a 'width' amount
+ break;
+
+ case OAM_VAR_BRANCH_TERMINATION:
+ default:
+ ok = BCMOS_FALSE;
+ break;
+ }
+ }
+
+ ok = ok && bcmolt_buf_skip(buf, oamvar->width);
+ return ok;
+}
+
+/* Loops through 'oam_resp' looking for the provided branch/leaf. If found it will return it. */
+static bcmos_bool oam_var_get_branch_leaf(uint8_t *oam_resp, uint32_t oam_resp_len, oam_var_branch branch, uint16_t leaf, oam_var_generic *oam_var)
+{
+ bcmolt_buf buf;
+ oam_var_generic var;
+
+ bcmolt_buf_init(&buf, oam_resp_len, oam_resp, (bcmos_endian) BCMOLT_BUF_ENDIAN_FIXED);
+
+ while (oam_var_get_next(&buf, &var)) // Keep going through all OAM vars.
+ {
+ if ((var.branch == branch) && (var.leaf == leaf)) // Check to see it is the one we are looking for.
+ {
+ *oam_var = var;
+ return BCMOS_TRUE;
+ }
+ }
+ return BCMOS_FALSE;
+}
+
+static bcmos_bool oam_parse_to_dpoe_opcode(bcmolt_buf *buf, oam_dpoe_opcode opcode)
+{
+ bcmos_bool ok = BCMOS_TRUE;
+ uint24_t dpoe_oui = { .low_hi = { .hi = 0x00, .mid = 0x10, .low = 0x00 } };
+ uint16_t u16;
+ uint8_t u8;
+ uint24_t u24;
+ ok = ok && bcmolt_buf_skip(buf, 12); // Skip both mac addresses
+ ok = ok && bcmolt_buf_read_u16_be(buf, &u16); // Read ethertype
+ ok = ok && u16 == ETHERTYPE_SLOWPROTOCOLS; // Make sure it is slow protocol
+ ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the subtype
+ ok = ok && u8 == SLOW_PROTOCOL_SUBTYPE_OAM; // Make sure it is OAM
+ ok = ok && bcmolt_buf_skip(buf, 2); // Skip flags
+ ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the code
+ ok = ok && u8 == OAM_PDU_CODE_ORG_EXT; // Make sure it is organization specific
+ ok = ok && bcmolt_buf_read_u24(buf, &u24); // Read the OUI
+ ok = ok && memcmp(&dpoe_oui, &u24, sizeof(uint24_t)) == 0; // Make sure it is the DPoE OUI
+ ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the opcode
+ ok = ok && opcode == u8; // Make sure it matches the provided opcode
+ return ok;
+}
+
+bcmos_errno dpoe_encrypt_oam_set_request_send(const hde_key *hde_link, uint16_t period, dpoe_encrypt_mode encrypt_mode, bcmos_mac_address pon_mac)
+{
+ // send the encrypt mode and key expiry time TLVs to ONU.
+ bcmolt_buf buf;
+ ieee_oui dpoe_oui = { { 0x00, 0x10, 0x00 } };
+ bcmos_bool ok = BCMOS_TRUE;
+
+ memset(oam_buffer, 0, sizeof(oam_buffer));
+
+ bcmolt_buf_init(&buf, OAM_MAX_PACKET_SIZE, oam_buffer, BCMOLT_BUF_ENDIAN_FIXED);
+ ok = ok && oam_pdu_build_common(pon_mac, &buf);
+ ok = ok && bcmolt_buf_write_u16_be(&buf, (uint16_t) OAM_PDU_FLAG_SEND_ANY); /* write flags */
+ ok = ok && bcmolt_buf_write_u8(&buf, (uint8_t) OAM_PDU_CODE_ORG_EXT); /* write code */
+ ok = ok && bcmolt_buf_write(&buf, dpoe_oui.byte, 3); /* write oui */
+ ok = ok && bcmolt_buf_write_u8(&buf, OAM_DPOE_OPCODE_SET_REQUEST); /* write opcode */
+ ok = ok && dpoe_encrypt_tlv_add(&buf, period, encrypt_mode); /* write encryption TLVs */
+ ok = ok && bcmolt_buf_write_u16_be(&buf, 0); /* write END */
+ ok = ok && oam_pdu_transmit(hde_link, oam_buffer, MAX(bcmolt_buf_get_used(&buf), 60)); /* Send it */
+
+ if (!ok)
+ {
+ return BCM_ERR_NOMEM;
+ }
+ else
+ {
+ return BCM_ERR_OK;
+ }
+ return ok;
+}
+
+bcmos_errno dpoe_encrypt_oam_parse_set_response(const bcmolt_u8_list_u32 *frame, uint8_t *encrypt_mode_response, uint8_t *key_expiry_response)
+{
+ oam_var_generic encrypt_mode;
+ oam_var_generic key_expiry_time;
+ bcmolt_buf buf;
+ bcmolt_buf_init(&buf, frame->len, frame->val, BCMOLT_BUF_ENDIAN_FIXED);
+
+ // Move the OAM buffer forward to the opcode
+ if (!oam_parse_to_dpoe_opcode(&buf, OAM_DPOE_OPCODE_SET_RESPONSE))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ // Check for encrypt mode TLV
+ if (!oam_var_get_branch_leaf(
+ bcmolt_buf_snap_get(&buf),
+ bcmolt_buf_get_remaining_size(&buf),
+ OAM_VAR_BRANCH_DPOE_ATTRIBUTE,
+ OAM_DPOE_ATTR_ENCRYPT_MODE,
+ &encrypt_mode))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ // Check for key expiry time TLV
+ if (!oam_var_get_branch_leaf(
+ bcmolt_buf_snap_get(&buf),
+ bcmolt_buf_get_remaining_size(&buf),
+ OAM_VAR_BRANCH_DPOE_ATTRIBUTE,
+ OAM_DPOE_ATTR_KEY_EXPIRY_TIME,
+ &key_expiry_time))
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ *encrypt_mode_response = encrypt_mode.width;
+ *key_expiry_response = key_expiry_time.width;
+ return BCM_ERR_OK;
+}
+
+bcmos_errno dpoe_encrypt_oam_parse_new_key(const bcmolt_u8_list_u32 *frame, uint8_t *new_key, bcmolt_epon_key_choice *key_choice)
+{
+ bcmos_bool ok = BCMOS_TRUE;
+ uint8_t key_number;
+ uint8_t key_length;
+ bcmolt_buf buf;
+ bcmolt_buf_init(&buf, frame->len, frame->val, BCMOLT_BUF_ENDIAN_FIXED);
+ ok = oam_parse_to_dpoe_opcode(&buf, OAM_DPOE_OPCODE_KEY_EXCHANGE);
+ ok = ok && bcmolt_buf_read_u8(&buf, &key_number); // Read key number
+ ok = ok && bcmolt_buf_read_u8(&buf, &key_length); // Read key length
+ ok = ok && key_length == 16; // Make sure the key is 16 byte length
+ ok = ok && bcmolt_buf_read(&buf, new_key, 16); // Read the key
+
+ if (!ok)
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ if (key_number == 0)
+ {
+ *key_choice = BCMOLT_EPON_KEY_CHOICE_KEY_0;
+ }
+ else if (key_number == 1)
+ {
+ *key_choice = BCMOLT_EPON_KEY_CHOICE_KEY_1;
+ }
+ else
+ {
+ return BCM_ERR_PARSE;
+ }
+
+ return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_hde/encrypt_oam.h b/bcm68620_release/release/host_reference/user_appl/epon_hde/encrypt_oam.h
new file mode 100644
index 0000000..18cd2eb
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_hde/encrypt_oam.h
@@ -0,0 +1,59 @@
+/*
+ <: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.
+
+ :>
+ */
+
+#ifndef ENCRYPT_OAM_H_
+#define ENCRYPT_OAM_H_
+
+#include "oam_defs.h"
+#include "bcmolt_epon_hde.h"
+
+/* Send a Encryption Request OAM to ONU. */
+bcmos_errno dpoe_encrypt_oam_set_request_send(
+ const hde_key *hde_link,
+ uint16_t period,
+ dpoe_encrypt_mode encrypt_mode,
+ bcmos_mac_address pon_mac);
+
+/* Attempt to parse a DPoE set response and fill in the values of the two TLVs we initially sent down.
+ * This will return BCM_ERR_PARSE if it fails to fill in either of the TLVs.
+ */
+bcmos_errno dpoe_encrypt_oam_parse_set_response(
+ const bcmolt_u8_list_u32 *frame,
+ uint8_t *encrypt_mode_response,
+ uint8_t *key_expiry_response);
+
+/* Attempts to parse an OAM frame and fill in the values of new_key and key_choice from that frame.
+ * This will return BCM_ERR_PARSE if it fails to fill in either new_key or key_choice.
+ */
+bcmos_errno dpoe_encrypt_oam_parse_new_key(
+ const bcmolt_u8_list_u32 *frame,
+ uint8_t *new_key,
+ bcmolt_epon_key_choice *key_choice);
+
+#endif /* ENCRYPT_OAM_H_ */
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_hde/oam_defs.h b/bcm68620_release/release/host_reference/user_appl/epon_hde/oam_defs.h
new file mode 100644
index 0000000..276d5eb
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_hde/oam_defs.h
@@ -0,0 +1,153 @@
+/*
+ <: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.
+
+ :>
+ */
+
+#ifndef OAM_DEFS_H_
+#define OAM_DEFS_H_
+
+#include "bcmolt_model_types.h"
+
+#define ETHERTYPE_SLOWPROTOCOLS 0x8809
+#define OAM_TIMEOUT_GRACE_PERIOD_US 100000
+#define HDE_ENABLE_OAM_WAIT_US 3e6 // 3 seconds
#define OAM_MAX_PACKET_SIZE 1518
+#define OAM_MAX_TLV_LENGTH 128
+
+/* Common OAMPDU Structure */
+typedef enum
+{
+ SLOW_PROTOCOL_SUBTYPE_LACP = 0x01, /* Link Agregation Control Protocol */
+ SLOW_PROTOCOL_SUBTYPE_LAMP = 0x02, /* Link Agregation Marker Protocol */
+ SLOW_PROTOCOL_SUBTYPE_OAM = 0x03, /* OAM */
+ SLOW_PROTOCOL_SUBTYPE_ORG_SPEC = 0x0A /* Organization Specific Subtype */
+} slow_protocol_subtype;
+
+/* Standard OAM Flags field. IEEE802.3-2008_section5, ch.57.4.2.1 */
+typedef enum
+{
+ OAM_PDU_FLAG_LINK_FAULT = (1 << 0), /* 0x0001 */
+ OAM_PDU_FLAG_DYING_GASP = (1 << 1), /* 0x0002 */
+ OAM_PDU_FLAG_CRITICAL_EVENT = (1 << 2), /* 0x0004 */
+ OAM_PDU_FLAG_LOCAL_EVAL = (1 << 3), /* 0x0008 */
+ OAM_PDU_FLAG_LOCAL_STABLE = (1 << 4), /* 0x0010 */
+ OAM_PDU_FLAG_REMOTE_EVAL = (1 << 5), /* 0x0020 */
+ OAM_PDU_FLAG_REMOTE_STABLE = (1 << 6), /* 0x0040 */
+ OAM_PDU_FLAG_SEND_ANY = (OAM_PDU_FLAG_LOCAL_STABLE | OAM_PDU_FLAG_REMOTE_STABLE)
+} oam_pdu_flag;
+
+/* Standard OAM Code field. IEEE802.3-2008_section5, ch.57.4.2.2 */
+typedef enum
+{
+ OAM_PDU_CODE_INFO = 0x0,
+ OAM_PDU_CODE_EVENTNOTIFICATION = 0x01,
+ OAM_PDU_CODE_VARREQUEST = 0x02,
+ OAM_PDU_CODE_VARRESPONSE = 0x03,
+ OAM_PDU_CODE_LOOPBACK = 0x04,
+ OAM_PDU_CODE_ORG_EXT = 0xfe
+} oam_pdu_code;
+
+/* IEEE Organization Unique Identifier */
+typedef struct
+{
+ uint8_t byte[3];
+} ieee_oui;
+
+/* Branch types */
+typedef enum
+{
+ OAM_VAR_BRANCH_TERMINATION = 0x00,
+ OAM_VAR_BRANCH_ATTRIBUTE = 0x07,
+ OAM_VAR_BRANCH_ACTION = 0x09,
+ OAM_VAR_BRANCH_DPOE_OBJECT = 0xD6,
+ OAM_VAR_BRANCH_DPOE_ATTRIBUTE = 0xD7,
+ OAM_VAR_BRANCH_DPOE_PROG_CTR = 0xD8,
+ OAM_VAR_BRANCH_DPOE_ACT = 0xD9
+} oam_var_branch;
+
+typedef enum
+{
+ OAM_DPOE_OPCODE_RESERVED = 0,
+ OAM_DPOE_OPCODE_GET_REQUEST = 1,
+ OAM_DPOE_OPCODE_GET_RESPONSE = 2,
+ OAM_DPOE_OPCODE_SET_REQUEST = 3,
+ OAM_DPOE_OPCODE_SET_RESPONSE = 4,
+ OAM_DPOE_OPCODE_IPMC_CTRL = 5,
+ OAM_DPOE_OPCODE_MCAST_REG = 6,
+ OAM_DPOE_OPCODE_MCAST_REG_RESP = 7,
+ OAM_DPOE_OPCODE_KEY_EXCHANGE = 8,
+ OAM_DPOE_OPCODE_FILE_TRANSFER = 9,
+ OAM_DPOE_OPCODE_IPMC_CTRL_RESP = 0x0a /* DPoE 2.0 */
+} oam_dpoe_opcode;
+
+typedef enum
+{
+ OAM_DPOE_ATTR_KEY_EXPIRY_TIME = 0x0401,
+ OAM_DPOE_ATTR_ENCRYPT_MODE = 0x0402
+} oam_dpoe_attr;
+
+/* DPoE Key Exchange OAM TLV encryption mode */
+typedef enum
+{
+ DPOE_ENCRYPT_MODE_NONE,
+ DPOE_ENCRYPT_MODE_1DOWN,
+ DPOE_ENCRYPT_MODE_10DOWN,
+ DPOE_ENCRYPT_MODE_10BI
+} dpoe_encrypt_mode;
+
+/* generic OAM Variable format */
+typedef struct
+{
+ oam_var_branch branch;
+ uint16_t leaf;
+ uint8_t width;
+ uint8_t *value;
+} oam_var_generic;
+
+/** In the OAM protocol length 0 tlv is encoded as 0x80 (no error).
+ * Length (1-127) are encoded with no change and 128 is encoded as 0.
+ *
+ * \param width TLV width field value
+ * \return encoded value for the width field
+ * \ref IEEE802.3-2008_section5, Ch. 57.6.2.1
+ */
+static inline uint8_t oam_var_tlv_length_encode(uint8_t width)
+{
+ if (width == 0)
+ {
+ return OAM_MAX_TLV_LENGTH; /* 0x00 equals 128 octets. */
+ }
+ else if (width == OAM_MAX_TLV_LENGTH)
+ {
+ return 0; /* 0x80 represents 'no error'. No 'Value' field. */
+ }
+ else
+ {
+ return width; /* represents the length of 'Value'. */
+ }
+}
+
+#endif /* OAM_DEFS_H_ */
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_oam/Makefile b/bcm68620_release/release/host_reference/user_appl/epon_oam/Makefile
new file mode 100644
index 0000000..ff56b8f
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_oam/Makefile
@@ -0,0 +1,10 @@
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl_epon_oam
+ MOD_TYPE = lib
+ MOD_DEPS = utils dev_log common_epon_oam common_api
+ srcs = bcmolt_user_appl_epon_oam.c
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_oam/bcmolt_user_appl_epon_oam.c b/bcm68620_release/release/host_reference/user_appl/epon_oam/bcmolt_user_appl_epon_oam.c
new file mode 100644
index 0000000..558594c
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_oam/bcmolt_user_appl_epon_oam.c
@@ -0,0 +1,1436 @@
+/*
+ <: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 "bcmolt_math.h"
+#include "bcmolt_utils.h"
+#include "bcm_dev_log.h"
+#include "bcmolt_msg.h"
+#include "bcmolt_msg_pack.h"
+#include "bcmolt_api.h"
+#include "bcmolt_model_types.h"
+#include "bcmolt_user_appl_epon_oam.h"
+#include "bcmos_hash_table.h"
+
+#define MAX_CONCURRENT_LINKS 2048
+
+typedef struct
+{
+ bcmolt_devid device_id;
+ bcmolt_epon_link_key key;
+ FILE *file;
+ void *buf;
+ uint16_t block_size;
+ uint16_t last_block;
+ uint16_t last_block_size;
+ uint8_t retries;
+ bcmos_bool done;
+ bcmos_timer timer;
+ uint32_t timeout_us;
+ uint32_t last_time;
+} epon_oam_dpoe_fw_upgrade_state;
+
+typedef struct
+{
+ bcmolt_devid device_id;
+ bcmolt_proxy_rx *rx;
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb;
+} epon_oam_proxy_rx_data;
+
+typedef struct
+{
+ epon_oam_dpoe_fw_upgrade_state *state;
+} epon_oam_timeout_data;
+
+typedef union
+{
+ epon_oam_proxy_rx_data proxy_rx;
+ epon_oam_timeout_data timeout;
+} epon_oam_msg_data;
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ epon_oam_msg_data data;
+} epon_oam_msg;
+
+static bcmos_bool is_running = BCMOS_FALSE;
+static dev_log_id epon_oam_log[BCMTR_MAX_OLTS];
+static char mac_buf[MAC_STR_LEN];
+static bcmos_task epon_oam_task;
+static hash_table *dpoe_fw_upgrade_state;
+
+static void epon_oam_handle_os_msg(bcmos_module_id module_id, bcmos_msg *os_msg);
+
+#define EPON_OAM_LOG(level, device, link, fmt, args...) \
+ BCM_LOG_CALLER_FMT(level, epon_oam_log[device], "%u-%s: "fmt, (link)->epon_ni, bcmos_mac_2_str(&(link)->mac_address, mac_buf), ##args)
+
+static bcmos_errno epon_oam_get_mac_da(bcmolt_devid device_id, bcmolt_epon_ni epon, bcmos_mac_address *mac)
+{
+ bcmos_errno err;
+ bcmolt_epon_ni_cfg pon_cfg;
+ bcmolt_epon_ni_key pon_key = { .epon_ni = epon };
+
+ /* retrieve the EPON NI MAC address */
+ BCMOLT_CFG_INIT(&pon_cfg, epon_ni, pon_key);
+ BCMOLT_CFG_PROP_GET(&pon_cfg, epon_ni, mac_address);
+ err = bcmolt_cfg_get(device_id, &pon_cfg.hdr);
+ if (BCM_ERR_OK != err)
+ {
+ BCM_LOG(ERROR, epon_oam_log[device_id], "Failed to retrieve EPON NI MAC address!\n");
+ }
+ else
+ {
+ *mac = pon_cfg.data.mac_address;
+ }
+ return err;
+}
+
+bcmos_errno epon_oam_pack_frame(bcmolt_epon_oam_ethernet_frame *oam_frame, uint8_t **buffer, uint16_t *length)
+{
+ bcmolt_buf buf;
+
+ *length = MAX(bcmolt_epon_oam_ethernet_frame_get_packed_length(oam_frame), 60);
+ *buffer = bcmos_calloc(*length);
+ if (NULL == *buffer)
+ {
+ return BCM_ERR_NOMEM;
+ }
+ bcmolt_buf_init(&buf, *length, *buffer, BCMOLT_BUF_ENDIAN_FIXED);
+ if (!bcmolt_epon_oam_ethernet_frame_pack(oam_frame, &buf))
+ {
+ bcmos_free(*buffer);
+ return BCM_ERR_INTERNAL;
+ }
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno epon_oam_pack_and_send(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ const bcmos_mac_address *mac,
+ bcmolt_epon_oam_ethernet_frame *oam_frame)
+{
+ bcmos_errno err;
+ bcmolt_epon_link_inject_frame inject_frame;
+ bcmolt_epon_link_key link_key = { .epon_ni = epon, .mac_address = *mac };
+ bcmolt_ethernet_frame_unmasked frame;
+
+ err = epon_oam_pack_frame(oam_frame, &frame.frame_octets.val, &frame.frame_octets.len);
+ if (BCM_ERR_OK != err)
+ {
+ EPON_OAM_LOG(ERROR, device_id, &link_key, "Failed to pack OAM frame: %s!\n", bcmos_strerror(err));
+ return err;
+ }
+
+ BCMOLT_PROXY_INIT(&inject_frame, epon_link, inject_frame, link_key);
+ BCMOLT_PROXY_PROP_SET(&inject_frame, epon_link, inject_frame, frame, frame);
+ err = bcmolt_proxy_send(device_id, &inject_frame.hdr);
+
+ bcmos_free(frame.frame_octets.val);
+
+ return err;
+}
+
+bcmolt_epon_oam_ethernet_frame *epon_oam_unpack(bcmolt_devid device_id, uint32_t len, uint8_t *bytes)
+{
+ bcmolt_buf buf;
+ uint32_t unpack_bytes = sizeof(bcmolt_epon_oam_ethernet_frame);
+ void *unpack_mem;
+ bcmolt_epon_oam_ethernet_frame *frame;
+ void *extra_mem;
+
+ /* We don't know how much space we'll need to unpack the OAM frame. Starting with the known fixed size, we scan the
+ frame to determine the exact space needed */
+ bcmolt_buf_init(&buf, len, bytes, BCMOLT_BUF_ENDIAN_FIXED);
+ if (!bcmolt_epon_oam_ethernet_frame_scan(&buf, &unpack_bytes))
+ {
+ BCM_LOG(ERROR, epon_oam_log[device_id], "Failed to scan OAM frame\n");
+ return NULL;
+ }
+ unpack_mem = bcmos_calloc(unpack_bytes);
+ if (NULL == unpack_mem)
+ {
+ BCM_LOG(ERROR, epon_oam_log[device_id], "Failed to allocate %u bytes required to unpack\n", unpack_bytes);
+ return NULL;
+ }
+ else
+ {
+ BCM_LOG(DEBUG, epon_oam_log[device_id], "Successfully allocated %u bytes required to unpack\n", unpack_bytes);
+ }
+ /* Now that we have an appropriately sized buffer to unpack into, point the fixed structure to the start of the
+ buffer and direct the unpacker to store any additional data immediately after that */
+ frame = (bcmolt_epon_oam_ethernet_frame*)unpack_mem;
+ extra_mem = (void*)(frame + 1);
+
+ bcmolt_buf_init(&buf, len, bytes, BCMOLT_BUF_ENDIAN_FIXED);
+ if (!bcmolt_epon_oam_ethernet_frame_unpack(frame, &buf, &extra_mem))
+ {
+ BCM_LOG(ERROR, epon_oam_log[device_id], "Failed to unpack OAM frame\n");
+ bcmos_free(unpack_mem);
+ return NULL;
+ }
+
+ return frame;
+}
+
+static bcmos_errno epon_oam_make_dpoe_frame(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmolt_epon_oam_ethernet_frame *oam_frame,
+ bcmolt_epon_oam_ethernet_protocol *protocol)
+{
+ bcmos_errno err;
+
+ oam_frame->da = slow_protocol_multicast_mac;
+ err = epon_oam_get_mac_da(device_id, epon, &oam_frame->sa);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ oam_frame->protocols_count = 1;
+ oam_frame->protocols = protocol;
+ protocol->ethertype = BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL;
+ protocol->u.slow_protocol.value.subtype = BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM;
+ protocol->u.slow_protocol.value.u.oam.flags =
+ BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE;
+ protocol->u.slow_protocol.value.u.oam.content.code = BCMOLT_EPON_OAM_OAM_OPCODE_ORGANIZATION_SPECIFIC;
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.oui =
+ BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE;
+
+ return err;
+}
+
+static bcmos_errno epon_oam_send_os_msg(bcmos_msg_id type, epon_oam_msg_data *msg_data)
+{
+ bcmos_errno rc;
+ epon_oam_msg *msg = bcmos_calloc(sizeof(*msg));
+
+ if (msg == NULL)
+ {
+ BCM_LOG(ERROR, epon_oam_log[msg_data->proxy_rx.device_id], "OS msg calloc failed\n");
+ return BCM_ERR_NOMEM;
+ }
+
+ msg->os_msg.type = type;
+ msg->os_msg.handler = epon_oam_handle_os_msg;
+ msg->data = *msg_data;
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_EPON_OAM, &msg->os_msg, 0);
+ if (BCM_ERR_OK != rc)
+ {
+ BCM_LOG(ERROR, epon_oam_log[msg_data->proxy_rx.device_id], "OS msg send failed\n");
+ }
+ return rc;
+}
+
+bcmos_errno epon_oam_dpoe_get_critical_info(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_descriptor vars[3] = {};
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_GET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.get_request.vars_count =
+ 3;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.get_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[0].u.extended_attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_ONU_ID;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[1].u.extended_attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_MAX_LOGICAL_LINKS;
+ vars[2].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[2].u.end.unknown_count = 0;
+ vars[2].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_set_oam_rate(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t min,
+ uint8_t max)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[2] = {};
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 2;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[0].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_OAM_RATE;
+ vars[0].u.extended_attribute.attribute.u.oam_rate.minimum_oam_rate = min;
+ vars[0].u.extended_attribute.attribute.u.oam_rate.maximum_oam_rate = max;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[1].u.end.unknown_count = 0;
+ vars[1].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_set_report_thresholds(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t report_values_per_queue_set,
+ bcmolt_epon_oam_queue_sets queue_sets,
+ uint8_t num_queue_sets)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[2] = {};
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 2;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[0].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_REPORT_THRESHOLDS;
+ vars[0].u.extended_attribute.attribute.u.report_thresholds.number_of_queue_sets = num_queue_sets;
+ vars[0].u.extended_attribute.attribute.u.report_thresholds.report_values_per_queue_set =
+ report_values_per_queue_set;
+ vars[0].u.extended_attribute.attribute.u.report_thresholds.queue_set0 = queue_sets[0];
+ vars[0].u.extended_attribute.attribute.u.report_thresholds.queue_set1 = queue_sets[1];
+ vars[0].u.extended_attribute.attribute.u.report_thresholds.queue_set2 = queue_sets[2];
+ vars[0].u.extended_attribute.attribute.u.report_thresholds.queue_set3 = queue_sets[3];
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[1].u.end.unknown_count = 0;
+ vars[1].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_set_encryption(
+ bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ bcmolt_epon_oam_dpoe_encryption_mode enc_mode,
+ uint16_t period)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[3] = {};
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 3;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[0].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_ENCRYPT_MODE;
+ vars[0].u.extended_attribute.attribute.u.encrypt_mode.encryption_method = enc_mode;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[1].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_KEY_EXPIRY_TIME;
+ vars[1].u.extended_attribute.attribute.u.key_expiry_time.time = period;
+ vars[2].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[2].u.end.unknown_count = 0;
+ vars[2].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_bool epon_oam_is_dpoe(bcmolt_epon_oam_ethernet_frame *oam)
+{
+ return ((oam->protocols_count > 0) &&
+ (BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL == oam->protocols[0].ethertype) &&
+ (BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM == oam->protocols[0].u.slow_protocol.value.subtype) &&
+ (BCMOLT_EPON_OAM_OAM_OPCODE_ORGANIZATION_SPECIFIC ==
+ oam->protocols[0].u.slow_protocol.value.u.oam.content.code) &&
+ (BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE ==
+ oam->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.oui));
+}
+
+bcmos_bool epon_oam_is_dpoe_encrypt_response(bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe)
+{
+ return (BCMOLT_EPON_OAM_DPOE_OPCODE_SET_RESPONSE == dpoe->op) &&
+ (dpoe->u.set_response.vars_count > 2) &&
+ (BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE == dpoe->u.set_response.vars[0].branch) &&
+ (BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_ENCRYPT_MODE ==
+ dpoe->u.set_response.vars[0].u.extended_attribute.attribute.leaf) &&
+ (BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE == dpoe->u.set_response.vars[1].branch) &&
+ (BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_KEY_EXPIRY_TIME ==
+ dpoe->u.set_response.vars[1].u.extended_attribute.attribute.leaf);
+}
+
+static bcmos_errno epon_oam_dpoe_clear_ingress_rules_make_frame(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ bcmolt_epon_oam_ethernet_frame *oam_frame,
+ bcmolt_epon_oam_ethernet_protocol *protocol,
+ bcmolt_epon_oam_dpoe_var_container_base *vars)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ err = epon_oam_make_dpoe_frame(device_id, epon, oam_frame, protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 3;
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_OBJECT;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ACTION;
+ vars[1].u.extended_action.action.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ACTION_CLEAR_INGRESS_RULES;
+ vars[2].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[2].u.end.unknown_count = 0;
+ vars[2].u.end.unknown = NULL;
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno epon_oam_dpoe_clear_ingress_rules_network_pon(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[3] = {};
+ uint8_t port = 0;
+
+ epon_oam_dpoe_clear_ingress_rules_make_frame(device_id, epon, mac, &oam_frame, &protocol, vars);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ vars[0].u.object.object_context.object_type = BCMOLT_EPON_OAM_DPOE_OBJECT_TYPE_NETWORK_PON;
+ vars[0].u.object.object_context.u.network_pon.pon_count = 1;
+ vars[0].u.object.object_context.u.network_pon.pon = &port;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_clear_ingress_rules_user_port(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[3] = {};
+ uint8_t port = 0;
+
+ epon_oam_dpoe_clear_ingress_rules_make_frame(device_id, epon, mac, &oam_frame, &protocol, vars);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ vars[0].u.object.object_context.object_type = BCMOLT_EPON_OAM_DPOE_OBJECT_TYPE_USER_PORT;
+ vars[0].u.object.object_context.u.user_port.port_count = 1;
+ vars[0].u.object.object_context.u.user_port.port = &port;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_clear_ingress_rules(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ err = epon_oam_dpoe_clear_ingress_rules_network_pon(device_id, epon, mac);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ return epon_oam_dpoe_clear_ingress_rules_user_port(device_id, epon, mac);
+}
+
+bcmos_errno epon_oam_dpoe_set_basic_queue_config(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t up_queue_size,
+ uint8_t dn_queue_size)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[2] = {};
+ bcmolt_epon_oam_dpoe_queue_set up_queue_set;
+ bcmolt_epon_oam_dpoe_queue_set dn_queue_set;
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 2;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[0].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_QUEUE_CONFIG;
+ vars[0].u.extended_attribute.attribute.u.queue_config.number_of_links = 1;
+ vars[0].u.extended_attribute.attribute.u.queue_config.link_configuration = &up_queue_set;
+ up_queue_set.queue_count = 1;
+ up_queue_set.queue_sizes = &up_queue_size;
+ vars[0].u.extended_attribute.attribute.u.queue_config.number_of_ports = 1;
+ vars[0].u.extended_attribute.attribute.u.queue_config.port_configuration = &dn_queue_set;
+ dn_queue_set.queue_count = 1;
+ dn_queue_set.queue_sizes = &dn_queue_size;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[1].u.end.unknown_count = 0;
+ vars[1].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+static bcmos_errno epon_oam_dpoe_add_ingress_rules_make_frame(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ bcmolt_epon_oam_ethernet_frame *oam_frame,
+ bcmolt_epon_oam_ethernet_protocol *protocol,
+ bcmolt_epon_oam_dpoe_var_container_base *vars,
+ dpoe_rule_vlan_mode vlan_mode,
+ uint8_t vlan_tag[4])
+{
+ static const uint32_t var_count[DPOE_RULE_VLAN_MODE_COUNT] =
+ {
+ [DPOE_RULE_VLAN_MODE_NONE] = 8,
+ [DPOE_RULE_VLAN_MODE_ADD] = 10,
+ [DPOE_RULE_VLAN_MODE_REMOVE] = 9
+ };
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ err = epon_oam_make_dpoe_frame(device_id, epon, oam_frame, protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ var_count[vlan_mode];
+ protocol->u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_OBJECT;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[1].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[1].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_HEADER;
+ vars[1].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.header.precedence = 10;
+ vars[2].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[2].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_CLAUSE;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.field_code.code =
+ BCMOLT_EPON_OAM_DPOE_FIELD_CODE_L2DA;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.field_code.instance = 0;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.msb_mask = 0;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.lsb_mask = 0;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.operator =
+ BCMOLT_EPON_OAM_RULE_OPERATOR_ALWAYS_MATCH;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.match_value_length = 0;
+ vars[2].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.clause.match_value = NULL;
+ vars[3].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[3].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_RESULT;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.result =
+ BCMOLT_EPON_OAM_DPOE_RESULT_QUEUE;
+ vars[4].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[4].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[4].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_RESULT;
+ vars[4].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.result =
+ BCMOLT_EPON_OAM_DPOE_RESULT_FORWARD;
+
+ switch (vlan_mode)
+ {
+ case DPOE_RULE_VLAN_MODE_NONE:
+ break; /* nothing to do */
+ case DPOE_RULE_VLAN_MODE_ADD:
+ vars[5].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[5].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_RESULT;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.result =
+ BCMOLT_EPON_OAM_DPOE_RESULT_INSERT;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.insert.field.field.code =
+ BCMOLT_EPON_OAM_DPOE_FIELD_CODE_CVLAN;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.insert.field.field.instance = 0;
+ vars[6].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[6].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[6].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_RESULT;
+ vars[6].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.result =
+ BCMOLT_EPON_OAM_DPOE_RESULT_SET;
+ vars[6].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.set.field.field.code =
+ BCMOLT_EPON_OAM_DPOE_FIELD_CODE_CVLAN;
+ vars[6].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.set.field.field.instance = 0;
+ vars[6].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.set.value_count = 4;
+ vars[6].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.set.value = vlan_tag;
+ break;
+ case DPOE_RULE_VLAN_MODE_REMOVE:
+ vars[5].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[5].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_RESULT;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.result =
+ BCMOLT_EPON_OAM_DPOE_RESULT_DELETE;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.delete.field.field.code =
+ BCMOLT_EPON_OAM_DPOE_FIELD_CODE_CVLAN;
+ vars[5].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.delete.field.field.instance = 0;
+ break;
+ default:
+ return BCM_ERR_PARM;
+ }
+
+ vars[var_count[vlan_mode]-3].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE;
+ vars[var_count[vlan_mode]-3].u.extended_attribute.attribute.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_PORT_INGRESS_RULE;
+ vars[var_count[vlan_mode]-3].u.extended_attribute.attribute.u.port_ingress_rule.rule.subtype = BCMOLT_EPON_OAM_RULE_TYPE_TERMINATOR;
+ vars[var_count[vlan_mode]-2].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ACTION;
+ vars[var_count[vlan_mode]-2].u.extended_action.action.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ACTION_ADD_INGRESS_RULES;
+ vars[var_count[vlan_mode]-1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[var_count[vlan_mode]-1].u.end.unknown_count = 0;
+ vars[var_count[vlan_mode]-1].u.end.unknown = NULL;
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno epon_oam_dpoe_add_ingress_rules_network_pon(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ dpoe_rule_vlan_mode vlan_mode,
+ uint8_t vlan_tag[4])
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[10] = {};
+ uint8_t port = 0;
+
+ err = epon_oam_dpoe_add_ingress_rules_make_frame(device_id,
+ epon,
+ mac,
+ &oam_frame,
+ &protocol,
+ vars,
+ vlan_mode,
+ vlan_tag);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ vars[0].u.object.object_context.object_type = BCMOLT_EPON_OAM_DPOE_OBJECT_TYPE_NETWORK_PON;
+ vars[0].u.object.object_context.u.network_pon.pon_count = 1;
+ vars[0].u.object.object_context.u.network_pon.pon = &port;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.queue.queue.type =
+ BCMOLT_EPON_OAM_DPOE_OBJECT_TYPE_USER_PORT;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.queue.queue.instance = 0;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.queue.queue.queue = 0;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_add_ingress_rules_user_port(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ dpoe_rule_vlan_mode vlan_mode,
+ uint8_t vlan_tag[4])
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[10] = {};
+ uint8_t port = 0;
+
+ err = epon_oam_dpoe_add_ingress_rules_make_frame(device_id,
+ epon,
+ mac,
+ &oam_frame,
+ &protocol,
+ vars,
+ vlan_mode,
+ vlan_tag);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ vars[0].u.object.object_context.object_type = BCMOLT_EPON_OAM_DPOE_OBJECT_TYPE_USER_PORT;
+ vars[0].u.object.object_context.u.user_port.port_count = 1;
+ vars[0].u.object.object_context.u.user_port.port = &port;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.queue.queue.type =
+ BCMOLT_EPON_OAM_DPOE_OBJECT_TYPE_LINK;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.queue.queue.instance = 0;
+ vars[3].u.extended_attribute.attribute.u.port_ingress_rule.rule.u.result.result.u.queue.queue.queue = 0;
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_add_ingress_rules(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ err = epon_oam_dpoe_add_ingress_rules_network_pon(device_id, epon, mac, DPOE_RULE_VLAN_MODE_NONE, NULL);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ return epon_oam_dpoe_add_ingress_rules_user_port(device_id, epon, mac, DPOE_RULE_VLAN_MODE_NONE, NULL);
+}
+
+bcmos_errno epon_oam_dpoe_add_ingress_rules_with_vlan(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t vlan_tag[4])
+{
+ static uint8_t example_vlan[4] = { 0x81, 0x00, 0x12, 0x34 };
+ bcmos_errno err = BCM_ERR_OK;
+
+ err = epon_oam_dpoe_add_ingress_rules_network_pon(device_id, epon, mac, DPOE_RULE_VLAN_MODE_REMOVE, NULL);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+
+ if (NULL == vlan_tag)
+ {
+ vlan_tag = example_vlan;
+ }
+ return epon_oam_dpoe_add_ingress_rules_user_port(device_id, epon, mac, DPOE_RULE_VLAN_MODE_ADD, vlan_tag);
+}
+
+static bcmos_errno _epon_oam_dpoe_user_traffic_set_action(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ bcmolt_epon_oam_dpoe_leaf_action action)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[2] = {};
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 2;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ACTION;
+ vars[0].u.extended_action.action.leaf = action;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[1].u.end.unknown_count = 0;
+ vars[1].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+bcmos_errno epon_oam_dpoe_disable_user_traffic(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ return _epon_oam_dpoe_user_traffic_set_action(device_id,
+ epon,
+ mac,
+ BCMOLT_EPON_OAM_DPOE_LEAF_ACTION_DISABLE_USER_TRAFFIC);
+}
+
+bcmos_errno epon_oam_dpoe_enable_user_traffic(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac)
+{
+ return _epon_oam_dpoe_user_traffic_set_action(device_id,
+ epon,
+ mac,
+ BCMOLT_EPON_OAM_DPOE_LEAF_ACTION_ENABLE_USER_TRAFFIC);
+}
+
+static epon_oam_dpoe_fw_upgrade_state *epon_oam_dpoe_fw_upgrade_state_add(const bcmolt_epon_link_key *key)
+{
+ epon_oam_dpoe_fw_upgrade_state state = {};
+ return hash_table_put(dpoe_fw_upgrade_state, (const uint8_t*)key, &state);
+}
+
+static epon_oam_dpoe_fw_upgrade_state* epon_oam_dpoe_fw_upgrade_state_get(const bcmolt_epon_link_key *key)
+{
+ return hash_table_get(dpoe_fw_upgrade_state, (const uint8_t*)key);
+}
+
+static void epon_oam_dpoe_fw_upgrade_state_release(epon_oam_dpoe_fw_upgrade_state *state)
+{
+ if (NULL == state)
+ {
+ BCM_LOG(ERROR, epon_oam_log[0], "Tried to release NULL state\n");
+ return;
+ }
+
+ if (NULL != state->file)
+ {
+ fclose(state->file);
+ }
+ bcmos_free(state->buf);
+ bcmos_timer_destroy(&state->timer);
+ if (!hash_table_remove(dpoe_fw_upgrade_state, (const uint8_t*)&state->key))
+ {
+ BCM_LOG(ERROR, epon_oam_log[state->device_id], "Unable to remove from hash table\n");
+ }
+}
+
+static void epon_oam_dpoe_send_block(bcmolt_devid device_id,
+ const bcmolt_epon_link_key *key,
+ uint16_t block,
+ uint8_t *data,
+ uint16_t length)
+{
+ bcmos_errno err;
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+
+ err = epon_oam_make_dpoe_frame(device_id, key->epon_ni, &oam_frame, &protocol);
+ BCMOS_RETURN_ON_ERROR(err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_FILE_TRANSFER;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.opcode = BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_DATA;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_data.block = block;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_data.length = length;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_data.data = data;
+
+ if (BCM_ERR_OK != epon_oam_pack_and_send(device_id, key->epon_ni, &key->mac_address, &oam_frame))
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Failed to send fw upgrade block %u\n", block);
+ }
+ else
+ {
+ EPON_OAM_LOG(DEBUG, device_id, key, "Sent block %u\n", block);
+ }
+}
+
+static void epon_oam_dpoe_send_ack(bcmolt_devid device_id,
+ const bcmolt_epon_link_key *key,
+ bcmolt_epon_oam_dpoe_file_transfer_error error)
+{
+ bcmos_errno err;
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+
+ err = epon_oam_make_dpoe_frame(device_id, key->epon_ni, &oam_frame, &protocol);
+ BCMOS_RETURN_ON_ERROR(err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_FILE_TRANSFER;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.opcode = BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_ACK;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_ack.block = 0;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_ack.error = error;
+
+ if (BCM_ERR_OK != epon_oam_pack_and_send(device_id, key->epon_ni, &key->mac_address, &oam_frame))
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Failed to send fw upgrade ack\n");
+ }
+ else
+ {
+ EPON_OAM_LOG(DEBUG, device_id, key, "Sent ack: error %u\n", error);
+ }
+}
+
+static void epon_oam_dpoe_reset_timeout(epon_oam_dpoe_fw_upgrade_state *state)
+{
+ EPON_OAM_LOG(DEBUG, state->device_id, &state->key, "ONU took %u us to respond\n", bcmos_timestamp() - state->last_time);
+ state->last_time = bcmos_timestamp();
+ bcmos_timer_start(&state->timer, state->timeout_us);
+}
+
+static void epon_oam_dpoe_handle_ack(bcmolt_devid device_id,
+ const bcmolt_epon_link_key *key,
+ const bcmolt_epon_oam_dpoe_file_transfer_base *ft,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ epon_oam_dpoe_fw_upgrade_state *state;
+ int block_size;
+
+ state = epon_oam_dpoe_fw_upgrade_state_get(key);
+
+ if (NULL == state)
+ {
+ EPON_OAM_LOG(DEBUG, device_id, key, "Rx unexpected ack\n");
+ return;
+ }
+
+ if (state->done)
+ {
+ if (ft->u.file_ack.block != 0)
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Final ack contained non-zero block: %u\n", ft->u.file_ack.block);
+ }
+ switch (ft->u.file_ack.error)
+ {
+ case BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_ERROR_OK:
+ EPON_OAM_LOG(INFO, device_id, key, "Upgrade successful\n");
+ break;
+ default:
+ EPON_OAM_LOG(ERROR, device_id, key, "Upgrade failed with error %u\n", ft->u.file_ack.error);
+ break;
+ }
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ return;
+ }
+
+ state->last_block++;
+ if ((state->last_block - 1) == ft->u.file_ack.block)
+ { /* retry last block */
+ state->last_block--;
+ state->retries++;
+ if (state->retries > 3)
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Exceeded retry limit on block %u\n", ft->u.file_ack.block);
+ epon_oam_dpoe_send_ack(device_id, key, BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_ERROR_TIMEOUT);
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ }
+ else
+ {
+ epon_oam_dpoe_send_block(device_id, key, ft->u.file_ack.block, state->buf, state->last_block_size);
+ epon_oam_dpoe_reset_timeout(state);
+ }
+ }
+ else if (state->last_block == ft->u.file_ack.block)
+ { /* send next block */
+ state->retries = 0;
+ block_size = fread(state->buf, 1, state->block_size, state->file);
+ if (block_size > 0)
+ {
+ epon_oam_dpoe_send_block(device_id, key, ft->u.file_ack.block, state->buf, block_size);
+ }
+ else
+ {
+ state->done = BCMOS_TRUE;
+ epon_oam_dpoe_send_ack(device_id, key, BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_ERROR_OK);
+ }
+ state->last_block_size = block_size;
+ epon_oam_dpoe_reset_timeout(state);
+ }
+ else
+ { /* unexpected block */
+ EPON_OAM_LOG(ERROR, device_id, key, "Unexpected block requested: %u (expecting %u)\n",
+ ft->u.file_ack.block, state->last_block + 1);
+ epon_oam_dpoe_send_ack(device_id, key, BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_ERROR_BAD_BLOCK);
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ }
+}
+
+static bcmos_timer_rc epon_oam_dpoe_fw_upgrade_timeout(bcmos_timer *timer, long data)
+{
+ epon_oam_msg_data msg_data;
+
+ msg_data.timeout.state = (epon_oam_dpoe_fw_upgrade_state*)data;
+ epon_oam_send_os_msg(BCMOS_MSG_ID_EPON_OAM_TIMEOUT, &msg_data);
+
+ return BCMOS_TIMER_OK;
+}
+
+bcmos_errno epon_oam_dpoe_start_upgrade(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ const char *fw_file,
+ uint16_t block_size,
+ uint8_t timeout_sec)
+{
+ bcmos_errno err = BCM_ERR_OK;
+ bcmolt_epon_link_key link_key = { .epon_ni = epon, .mac_address = *mac };
+ bcmos_timer_parm tp =
+ {
+ .name = "fw_upgrade_timer",
+ .owner = BCMOS_MODULE_ID_USER_APPL_EPON_OAM,
+ .periodic = BCMOS_FALSE,
+ .handler = epon_oam_dpoe_fw_upgrade_timeout
+ };
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ epon_oam_dpoe_fw_upgrade_state *state;
+
+ if (NULL != epon_oam_dpoe_fw_upgrade_state_get(&link_key))
+ {
+ return BCM_ERR_ALREADY;
+ }
+ state = epon_oam_dpoe_fw_upgrade_state_add(&link_key);
+ if (NULL == state)
+ {
+ return BCM_ERR_NOMEM;
+ }
+
+ state->device_id = device_id;
+ state->key = link_key;
+ state->file = fopen(fw_file, "rb");
+ if (NULL == state->file)
+ {
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ return BCM_ERR_IO;
+ }
+ state->block_size = block_size;
+ state->buf = bcmos_calloc(block_size);
+ if (NULL == state->buf)
+ {
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ return BCM_ERR_NOMEM;
+ }
+ state->last_block = 0xffff;/*-1*/
+ state->retries = 0;
+ state->done = BCMOS_FALSE;
+ tp.data = (long)state;
+ err = bcmos_timer_create(&state->timer, &tp);
+ if (BCM_ERR_OK != err)
+ {
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ return err;
+ }
+ state->timeout_us = timeout_sec * 1000 * 1000;
+ state->last_time = bcmos_timestamp();
+ bcmos_timer_start(&state->timer, state->timeout_us);
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ if (BCM_ERR_OK != err)
+ {
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ return err;
+ }
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_FILE_TRANSFER;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.opcode = BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_WRITE;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_write.filename_count = 0;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.file_transfer.file_transfer.u.file_write.filename = NULL;
+
+ err = epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+ if (BCM_ERR_OK != err)
+ {
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+ }
+ return err;
+}
+
+bcmos_errno epon_oam_dpoe_reset_onu(bcmolt_devid device_id, bcmolt_epon_ni epon, bcmos_mac_address *mac)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* OAM stuff */
+ bcmolt_epon_oam_ethernet_frame oam_frame;
+ bcmolt_epon_oam_ethernet_protocol protocol;
+ bcmolt_epon_oam_dpoe_var_container_base vars[2] = {};
+
+ err = epon_oam_make_dpoe_frame(device_id, epon, &oam_frame, &protocol);
+ BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != err, err);
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.op =
+ BCMOLT_EPON_OAM_DPOE_OPCODE_SET_REQUEST;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars_count =
+ 2;
+ protocol.u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value.u.set_request.vars = vars;
+ vars[0].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ACTION;
+ vars[0].u.extended_action.action.leaf = BCMOLT_EPON_OAM_DPOE_LEAF_ACTION_RESET_ONU;
+ vars[1].branch = BCMOLT_EPON_OAM_DPOE_BRANCH_END;
+ vars[1].u.end.unknown_count = 0;
+ vars[1].u.end.unknown = NULL;
+
+ return epon_oam_pack_and_send(device_id, epon, mac, &oam_frame);
+}
+
+static void epon_oam_handle_event(bcmolt_devid device_id,
+ bcmolt_epon_oam_oam_pdu_content *content,
+ const bcmolt_epon_link_key *key,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ for (uint32_t i = 0; i < content->u.event_notification.tlvs_count; i++)
+ {
+ if (BCMOLT_EPON_OAM_LINK_EVENT_TYPE_ORGANIZATION_SPECIFIC == content->u.event_notification.tlvs[i].type)
+ {
+ switch (content->u.event_notification.tlvs[i].u.organization_specific.value.oui)
+ {
+ case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE:
+ if (NULL != cb)
+ {
+ cb(device_id, key->epon_ni, &key->mac_address, BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_EVENT, BCM_ERR_OK);
+ }
+ EPON_OAM_LOG(INFO, device_id, key, "Received DPoE event %u\n",
+ content->u.event_notification.tlvs[i].u.organization_specific.value.u.dpoe.value.event_code);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+static void epon_oam_handle_dpoe_get_response(bcmolt_devid device_id,
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe,
+ const bcmolt_epon_link_key *key,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ EPON_OAM_LOG(DEBUG, device_id, key, "Rx %u vars\n", dpoe->u.get_response.vars_count);
+ for (uint32_t i = 0; i < dpoe->u.get_response.vars_count; i++)
+ {
+ EPON_OAM_LOG(DEBUG, device_id, key, " %u: branch %u\n", i, dpoe->u.get_response.vars[i].branch);
+ if (BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE == dpoe->u.get_response.vars[i].branch)
+ {
+ if (dpoe->u.get_response.vars[i].u.extended_attribute.attribute.width >
+ BCMOLT_EPON_OAM_DPOE_ERROR_CODE_NO_ERROR)
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Error 0x%x getting DPoE extended attribute 0x%04x\n",
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.width,
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.leaf);
+ rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ switch (dpoe->u.get_response.vars[i].u.extended_attribute.attribute.leaf)
+ {
+ case BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_ONU_ID:
+ EPON_OAM_LOG(INFO, device_id, key, "Got DPoE ONUID %02x%02x%02x%02x%02x%02x\n",
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.onu_id.id.u8[0],
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.onu_id.id.u8[1],
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.onu_id.id.u8[2],
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.onu_id.id.u8[3],
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.onu_id.id.u8[4],
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.onu_id.id.u8[5]);
+ break;
+ case BCMOLT_EPON_OAM_DPOE_LEAF_ATTRIBUTE_MAX_LOGICAL_LINKS:
+ EPON_OAM_LOG(INFO, device_id, key, "Got DPoE Max Logical Links BiDir %u, DownOnly %u\n",
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.max_logical_links.bidirectional,
+ dpoe->u.get_response.vars[i].u.extended_attribute.attribute.u.max_logical_links.downstream_only);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (NULL != cb)
+ {
+ cb(device_id, key->epon_ni, &key->mac_address, BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_GET_RESPONSE, rc);
+ }
+}
+
+static void epon_oam_handle_dpoe_set_response(bcmolt_devid device_id,
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe,
+ const bcmolt_epon_link_key *key,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ bcmos_errno rc = BCM_ERR_OK;
+
+ EPON_OAM_LOG(DEBUG, device_id, key, "Rx %u vars\n", dpoe->u.set_response.vars_count);
+ for (uint32_t i = 0; i < dpoe->u.set_response.vars_count; i++)
+ {
+ EPON_OAM_LOG(DEBUG, device_id, key, " %u: branch %u\n", i, dpoe->u.set_response.vars[i].branch);
+ switch (dpoe->u.set_response.vars[i].branch)
+ {
+ case BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ATTRIBUTE:
+ if (dpoe->u.set_response.vars[i].u.extended_attribute.attribute.width >
+ BCMOLT_EPON_OAM_DPOE_ERROR_CODE_NO_ERROR)
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Error 0x%x setting DPoE extended attribute 0x%04x\n",
+ dpoe->u.set_response.vars[i].u.extended_attribute.attribute.width,
+ dpoe->u.set_response.vars[i].u.extended_attribute.attribute.leaf);
+ rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ EPON_OAM_LOG(INFO, device_id, key, "Successfully set DPoE extended attribute 0x%04x\n",
+ dpoe->u.set_response.vars[i].u.extended_attribute.attribute.leaf);
+ }
+ break;
+ case BCMOLT_EPON_OAM_DPOE_BRANCH_EXTENDED_ACTION:
+ if (dpoe->u.set_response.vars[i].u.extended_action.action.width >
+ BCMOLT_EPON_OAM_DPOE_ERROR_CODE_NO_ERROR)
+ {
+ EPON_OAM_LOG(ERROR, device_id, key, "Error 0x%x performing DPoE extended action 0x%04x\n",
+ dpoe->u.set_response.vars[i].u.extended_action.action.width,
+ dpoe->u.set_response.vars[i].u.extended_action.action.leaf);
+ rc = BCM_ERR_ONU_ERR_RESP;
+ }
+ else
+ {
+ EPON_OAM_LOG(INFO, device_id, key, "Successfully performed DPoE extended action 0x%04x\n",
+ dpoe->u.set_response.vars[i].u.extended_action.action.leaf);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (NULL != cb)
+ {
+ cb(device_id, key->epon_ni, &key->mac_address, BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_SET_RESPONSE, rc);
+ }
+}
+
+static void epon_oam_handle_dpoe_file_transfer(bcmolt_devid device_id,
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe,
+ const bcmolt_epon_link_key *key,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ EPON_OAM_LOG(DEBUG, device_id, key, "Got DPoE File Transfer: %u\n", dpoe->u.file_transfer.file_transfer.opcode);
+ switch (dpoe->u.file_transfer.file_transfer.opcode)
+ {
+ case BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_ACK:
+ EPON_OAM_LOG(DEBUG, device_id, key, "Rx ack block %u error %u\n",
+ dpoe->u.file_transfer.file_transfer.u.file_ack.block,
+ dpoe->u.file_transfer.file_transfer.u.file_ack.error);
+ switch (dpoe->u.file_transfer.file_transfer.u.file_ack.error)
+ {
+ case BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_ERROR_OK:
+ epon_oam_dpoe_handle_ack(device_id, key, &dpoe->u.file_transfer.file_transfer, cb);
+ break;
+ default:
+ EPON_OAM_LOG(ERROR, device_id, key, "File transfer failed on block %u with error %u\n",
+ dpoe->u.file_transfer.file_transfer.u.file_ack.block,
+ dpoe->u.file_transfer.file_transfer.u.file_ack.error);
+ break;
+ }
+ break;
+ case BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_DATA:
+ EPON_OAM_LOG(DEBUG, device_id, key, "Rx data\n");
+ break;
+ case BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_READ:
+ EPON_OAM_LOG(ERROR, device_id, key, "Rx read request\n");
+ break;
+ case BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_OPCODE_FILE_WRITE:
+ EPON_OAM_LOG(ERROR, device_id, key, "Rx write request\n");
+ break;
+ default:
+ EPON_OAM_LOG(ERROR, device_id, key, "Unknown DPoE File Transfer opcode: %u\n",
+ dpoe->u.file_transfer.file_transfer.opcode);
+ break;
+ }
+}
+
+static void epon_oam_handle_dpoe(bcmolt_devid device_id,
+ bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe,
+ const bcmolt_epon_link_key *key,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ EPON_OAM_LOG(DEBUG, device_id, key, "Rx DPoE opcode %u\n", dpoe->op);
+ switch (dpoe->op)
+ {
+ case BCMOLT_EPON_OAM_DPOE_OPCODE_GET_RESPONSE:
+ epon_oam_handle_dpoe_get_response(device_id, dpoe, key, cb);
+ break;
+ case BCMOLT_EPON_OAM_DPOE_OPCODE_SET_RESPONSE:
+ epon_oam_handle_dpoe_set_response(device_id, dpoe, key, cb);
+ break;
+ case BCMOLT_EPON_OAM_DPOE_OPCODE_FILE_TRANSFER:
+ epon_oam_handle_dpoe_file_transfer(device_id, dpoe, key, cb);
+ break;
+ default:
+ break;
+ }
+}
+
+static void bcmolt_user_appl_epon_oam_handle_proxy_rx(epon_oam_proxy_rx_data *data)
+{
+ const bcmolt_epon_link_frame_captured *cap = (bcmolt_epon_link_frame_captured*)data->rx;
+ bcmolt_epon_oam_ethernet_frame *oam_frame;
+
+ oam_frame = epon_oam_unpack(data->device_id, cap->data.frame.len, cap->data.frame.val);
+ if (NULL == oam_frame)
+ {
+ return;
+ }
+
+ if (oam_frame->protocols_count == 0)
+ {
+ EPON_OAM_LOG(ERROR, data->device_id, &cap->key, "No protocols in OAM frame\n");
+ bcmos_free(oam_frame);
+ return;
+ }
+
+ EPON_OAM_LOG(DEBUG, data->device_id, &cap->key, "Rx ethertype %04x subtype %02x\n",
+ oam_frame->protocols[0].ethertype, oam_frame->protocols[0].u.slow_protocol.value.subtype);
+ if ((BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL == oam_frame->protocols[0].ethertype) &&
+ (BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM == oam_frame->protocols[0].u.slow_protocol.value.subtype))
+ {
+ if (oam_frame->protocols[0].u.slow_protocol.value.u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_LINK_FAULT)
+ {
+ if (NULL != data->cb)
+ {
+ data->cb(data->device_id, cap->key.epon_ni, &cap->key.mac_address, BCMOLT_USER_APPL_EPON_OAM_RX_ID_LINK_FAULT, BCM_ERR_OK);
+ }
+ EPON_OAM_LOG(ERROR, data->device_id, &cap->key, "Received OAM link fault\n");
+ }
+ if (oam_frame->protocols[0].u.slow_protocol.value.u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_DYING_GASP)
+ {
+ if (NULL != data->cb)
+ {
+ data->cb(data->device_id, cap->key.epon_ni, &cap->key.mac_address, BCMOLT_USER_APPL_EPON_OAM_RX_ID_DYING_GASP, BCM_ERR_OK);
+ }
+ EPON_OAM_LOG(ERROR, data->device_id, &cap->key, "Received OAM dying gasp\n");
+ }
+ if (oam_frame->protocols[0].u.slow_protocol.value.u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_CRITICAL_EVENT)
+ {
+ if (NULL != data->cb)
+ {
+ data->cb(data->device_id, cap->key.epon_ni, &cap->key.mac_address, BCMOLT_USER_APPL_EPON_OAM_RX_ID_CRITICAL_EVENT, BCM_ERR_OK);
+ }
+ EPON_OAM_LOG(ERROR, data->device_id, &cap->key, "Received OAM critical event\n");
+ }
+ EPON_OAM_LOG(DEBUG, data->device_id, &cap->key, "Rx opcode %u\n",
+ oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.code);
+ switch (oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.code)
+ {
+ case BCMOLT_EPON_OAM_OAM_OPCODE_INFO:
+ break; /* ignore info - this should be handled by eon */
+ case BCMOLT_EPON_OAM_OAM_OPCODE_EVENT_NOTIFICATION:
+ epon_oam_handle_event(data->device_id, &oam_frame->protocols[0].u.slow_protocol.value.u.oam.content, &cap->key, data->cb);
+ break;
+ case BCMOLT_EPON_OAM_OAM_OPCODE_ORGANIZATION_SPECIFIC:
+ EPON_OAM_LOG(DEBUG, data->device_id, &cap->key, "Rx OUI %u\n",
+ oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.oui);
+ switch (oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.oui)
+ {
+ case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE:
+ epon_oam_handle_dpoe(data->device_id, &oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value, &cap->key, data->cb);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ bcmos_free(oam_frame);
+ bcmolt_msg_free(&data->rx->hdr);
+}
+
+static void epon_oam_dpoe_handle_fw_upgrade_timeout(epon_oam_dpoe_fw_upgrade_state *state)
+{
+ EPON_OAM_LOG(ERROR, state->device_id, &state->key, "Firmware upgrade timed out\n");
+ epon_oam_dpoe_send_ack(state->device_id, &state->key, BCMOLT_EPON_OAM_DPOE_FILE_TRANSFER_ERROR_TIMEOUT);
+ epon_oam_dpoe_fw_upgrade_state_release(state);
+}
+
+static void epon_oam_handle_os_msg(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ epon_oam_msg *msg = (epon_oam_msg*)os_msg;
+
+ switch (msg->os_msg.type)
+ {
+ case BCMOS_MSG_ID_EPON_OAM_PROXY_RX:
+ bcmolt_user_appl_epon_oam_handle_proxy_rx(&msg->data.proxy_rx);
+ break;
+ case BCMOS_MSG_ID_EPON_OAM_TIMEOUT:
+ epon_oam_dpoe_handle_fw_upgrade_timeout(msg->data.timeout.state);
+ break;
+ default:
+ BCM_LOG(ERROR, epon_oam_log[0], "Unknown OS message %u\n", msg->os_msg.type);
+ break;
+ }
+ bcmos_free(os_msg);
+}
+
+void bcmolt_user_appl_epon_oam_handle_rx(bcmolt_devid device_id,
+ bcmolt_proxy_rx *rx,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb)
+{
+ bcmos_errno rc;
+ const bcmolt_epon_link_frame_captured *cap = (bcmolt_epon_link_frame_captured*)rx;
+ epon_oam_msg_data msg_data = {};
+
+ if (!is_running)
+ {
+ return;
+ }
+
+ if ((BCMOLT_OBJ_ID_EPON_LINK != rx->hdr.obj_type) ||
+ (BCMOLT_EPON_LINK_PROXY_RX_ID_FRAME_CAPTURED != rx->hdr.subgroup) ||
+ (cap->data.frame.val[12] != 0x88) ||
+ (cap->data.frame.val[13] != 0x09) ||
+ (cap->data.frame.val[14] != 0x03))
+ {
+ return; /* not OAM frame captured on a link - ignore */
+ }
+
+ msg_data.proxy_rx.device_id = device_id;
+ msg_data.proxy_rx.cb = cb;
+ rc = bcmolt_msg_clone((bcmolt_msg**)&msg_data.proxy_rx.rx, &rx->hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, epon_oam_log[device_id], "Proxy Rx clone failed: %s\n", bcmos_strerror(rc));
+ return;
+ }
+
+ if (BCM_ERR_OK != epon_oam_send_os_msg(BCMOS_MSG_ID_EPON_OAM_PROXY_RX, &msg_data))
+ {
+ bcmolt_msg_free(&msg_data.proxy_rx.rx->hdr);
+ }
+}
+
+void bcmolt_user_appl_epon_oam_init(void)
+{
+ bcmos_module_parm module_params =
+ {
+ .qparm =
+ {
+ .name = "user_appl_epon_oam_module",
+ .size = MAX_CONCURRENT_LINKS
+ }
+ };
+ bcmos_task_parm task_params =
+ {
+ .name = "user_appl_epon_oam_task",
+ .priority = BCMOS_TASK_PRIORITY_12,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+
+ if (is_running)
+ {
+ return;
+ }
+
+#ifdef ENABLE_LOG
+ int i;
+ char log_name[MAX_DEV_LOG_ID_NAME] = {};
+ for (i=0; i<BCMTR_MAX_OLTS; i++)
+ {
+ snprintf(log_name, sizeof(log_name)-1, "epon_oam%d", i);
+ epon_oam_log[i] = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
+ }
+#endif
+ dpoe_fw_upgrade_state = hash_table_create(MAX_CONCURRENT_LINKS,
+ sizeof(epon_oam_dpoe_fw_upgrade_state),
+ sizeof(bcmolt_epon_link_key),
+ "dpoe_fw_upgrade_state");
+ if (BCM_ERR_OK != bcmos_task_create(&epon_oam_task, &task_params))
+ {
+ BCM_LOG(FATAL, epon_oam_log[0], "Failed to create EPON OAM task!\n");
+ }
+ if (BCM_ERR_OK != bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_EPON_OAM, &epon_oam_task, &module_params))
+ {
+ BCM_LOG(FATAL, epon_oam_log[0], "Failed to create EPON OAM module!\n");
+ }
+ is_running = BCMOS_TRUE;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_oam/bcmolt_user_appl_epon_oam.h b/bcm68620_release/release/host_reference/user_appl/epon_oam/bcmolt_user_appl_epon_oam.h
new file mode 100644
index 0000000..c5dc9ed
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_oam/bcmolt_user_appl_epon_oam.h
@@ -0,0 +1,167 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_EPON_OAM_H_
+#define _BCMOLT_USER_APPL_EPON_OAM_H_
+
+#include "bcmcli.h"
+#include "bcmolt_msg.h"
+#include "bcmolt_epon_oam_types.h"
+
+#define MAX_QUEUE_SETS 4
+#define MAX_REPORT_THRESHOLDS 8
+
+static const bcmos_mac_address slow_protocol_multicast_mac = {{0x01,0x80,0xc2,0x00,0x00,0x02}};
+
+/* Typedef required for conveying a 2-dimensional array to epon_oam_dpoe_set_report_thresholds(). */
+typedef uint16_t bcmolt_epon_oam_queue_sets[MAX_QUEUE_SETS][MAX_REPORT_THRESHOLDS];
+
+typedef enum
+{
+ BCMOLT_USER_APPL_EPON_OAM_RX_ID_LINK_FAULT,
+ BCMOLT_USER_APPL_EPON_OAM_RX_ID_DYING_GASP,
+ BCMOLT_USER_APPL_EPON_OAM_RX_ID_CRITICAL_EVENT,
+ BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_EVENT,
+ BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_GET_RESPONSE,
+ BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_SET_RESPONSE,
+} bcmolt_user_appl_epon_oam_rx_id;
+
+typedef enum
+{
+ DPOE_RULE_VLAN_MODE_NONE,
+ DPOE_RULE_VLAN_MODE_ADD,
+ DPOE_RULE_VLAN_MODE_REMOVE,
+
+ DPOE_RULE_VLAN_MODE_COUNT
+} dpoe_rule_vlan_mode;
+
+/* During parsing of OAM indication, this optional callback may be called. */
+typedef void (*bcmolt_user_appl_epon_oam_handle_rx_cb)(bcmolt_devid device_id,
+ bcmolt_epon_ni epon_ni,
+ const bcmos_mac_address *mac,
+ bcmolt_user_appl_epon_oam_rx_id id,
+ bcmos_errno rc);
+
+/* Callback 'cb' is optional. */
+void bcmolt_user_appl_epon_oam_handle_rx(bcmolt_devid device_id,
+ bcmolt_proxy_rx *rx,
+ bcmolt_user_appl_epon_oam_handle_rx_cb cb);
+
+bcmos_errno epon_oam_pack_frame(bcmolt_epon_oam_ethernet_frame *oam_frame, uint8_t **buffer, uint16_t *length);
+
+bcmos_errno epon_oam_pack_and_send(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ const bcmos_mac_address *mac,
+ bcmolt_epon_oam_ethernet_frame *oam_frame);
+
+bcmolt_epon_oam_ethernet_frame *epon_oam_unpack(bcmolt_devid device_id, uint32_t len, uint8_t *bytes);
+
+bcmos_errno epon_oam_dpoe_get_critical_info(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+
+bcmos_errno epon_oam_dpoe_set_oam_rate(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t min,
+ uint8_t max);
+
+bcmos_errno epon_oam_dpoe_set_report_thresholds(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t report_values_per_queue_set,
+ bcmolt_epon_oam_queue_sets queue_sets,
+ uint8_t num_queue_sets);
+
+bcmos_errno epon_oam_dpoe_set_encryption(
+ bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ bcmolt_epon_oam_dpoe_encryption_mode enc_mode,
+ uint16_t period);
+
+bcmos_bool epon_oam_is_dpoe(bcmolt_epon_oam_ethernet_frame *oam);
+
+bcmos_bool epon_oam_is_dpoe_encrypt_response(bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe);
+
+bcmos_errno epon_oam_dpoe_clear_ingress_rules_network_pon(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+bcmos_errno epon_oam_dpoe_clear_ingress_rules_user_port(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+bcmos_errno epon_oam_dpoe_clear_ingress_rules(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+
+bcmos_errno epon_oam_dpoe_set_basic_queue_config(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t up_queue_size,
+ uint8_t dn_queue_size);
+
+bcmos_errno epon_oam_dpoe_add_ingress_rules_network_pon(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ dpoe_rule_vlan_mode vlan_mode,
+ uint8_t vlan_tag[4]);
+bcmos_errno epon_oam_dpoe_add_ingress_rules_user_port(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ dpoe_rule_vlan_mode vlan_mode,
+ uint8_t vlan_tag[4]);
+bcmos_errno epon_oam_dpoe_add_ingress_rules(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+
+bcmos_errno epon_oam_dpoe_add_ingress_rules_with_vlan(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ uint8_t vlan_tag[4]);
+
+bcmos_errno epon_oam_dpoe_enable_user_traffic(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+
+bcmos_errno epon_oam_dpoe_disable_user_traffic(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac);
+
+bcmos_errno epon_oam_dpoe_start_upgrade(bcmolt_devid device_id,
+ bcmolt_epon_ni epon,
+ bcmos_mac_address *mac,
+ const char *fw_file,
+ uint16_t block_size,
+ uint8_t timeout_sec);
+
+bcmos_errno epon_oam_dpoe_reset_onu(bcmolt_devid device_id, bcmolt_epon_ni epon, bcmos_mac_address *mac);
+
+void bcmolt_user_appl_epon_oam_init(void);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/Makefile b/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/Makefile
new file mode 100644
index 0000000..8bc0811
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/Makefile
@@ -0,0 +1,10 @@
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl_epon_oam_cli
+ MOD_TYPE = lib
+ MOD_DEPS = utils dev_log common_epon_oam bcm_user_appl_epon_oam
+ srcs = bcmolt_user_appl_epon_oam_cli.c
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/bcmolt_user_appl_epon_oam_cli.c b/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/bcmolt_user_appl_epon_oam_cli.c
new file mode 100644
index 0000000..ba8eaa6
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/bcmolt_user_appl_epon_oam_cli.c
@@ -0,0 +1,360 @@
+/*
+ <: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 "bcmolt_math.h"
+#include "bcm_dev_log.h"
+#include "bcmolt_api.h"
+#include "bcmolt_model_types.h"
+#include "bcmolt_user_appl_epon_oam.h"
+#include "bcmolt_user_appl_epon_oam_cli.h"
+#include "bcmolt_epon_oam_types.h"
+
+static bcmos_errno epon_oam_dpoe_get_critical_info_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_get_critical_info(current_device, epon, &mac);
+}
+
+static bcmos_errno epon_oam_dpoe_set_oam_rate_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+ uint8_t min = (uint8_t)bcmcli_find_named_parm(session, "min")->value.unumber;
+ uint8_t max = (uint8_t)bcmcli_find_named_parm(session, "max")->value.unumber;
+
+ return epon_oam_dpoe_set_oam_rate(current_device, epon, &mac, min, max);
+}
+
+static bcmos_errno epon_oam_dpoe_set_report_thresholds_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+ bcmcli_cmd_parm *queue_set_parms[MAX_QUEUE_SETS];
+
+ /* OAM stuff */
+ bcmolt_epon_oam_queue_sets queue_sets;
+ uint8_t i, j, num_queue_sets = 0;
+
+ /* process input */
+ queue_set_parms[0] = bcmcli_find_named_parm(session, "queue_0");
+ queue_set_parms[1] = bcmcli_find_named_parm(session, "queue_1");
+ queue_set_parms[2] = bcmcli_find_named_parm(session, "queue_2");
+ queue_set_parms[3] = bcmcli_find_named_parm(session, "queue_3");
+ for (i = 0; i < MAX_QUEUE_SETS; i++)
+ {
+ if (NULL != queue_set_parms[i])
+ {
+ num_queue_sets++;
+ if (queue_set_parms[i]->array_size != queue_set_parms[0]->array_size)
+ {
+ bcmcli_session_print(session, "All queue sets must have the same number of report thresholds!\n");
+ return BCM_ERR_PARM;
+ }
+ for (j = 0; j < queue_set_parms[i]->array_size; j++)
+ {
+ if (queue_set_parms[i]->values[j].unumber > 65535)
+ {
+ bcmcli_session_print(session, "Queue Set %u, Threshold %u: %lu is not a valid report threshold!\n",
+ i, j, queue_set_parms[i]->values[j].unumber);
+ return BCM_ERR_PARM;
+ }
+ queue_sets[i][j] = (uint16_t)queue_set_parms[i]->values[j].unumber;
+ }
+ }
+ else
+ {
+ for (j = i + 1; j < MAX_QUEUE_SETS; j++)
+ {
+ if (NULL != queue_set_parms[j])
+ {
+ bcmcli_session_print(session, "Included queue sets must be sequential from 0!\n");
+ return BCM_ERR_PARM;
+ }
+ }
+ }
+ }
+
+ return epon_oam_dpoe_set_report_thresholds(current_device,
+ epon,
+ &mac,
+ queue_set_parms[0]->array_size,
+ queue_sets,
+ num_queue_sets);
+}
+
+static bcmos_errno epon_oam_dpoe_clear_ingress_rules_network_pon_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_clear_ingress_rules_network_pon(current_device, epon, &mac);
+}
+
+static bcmos_errno epon_oam_dpoe_clear_ingress_rules_user_port_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_clear_ingress_rules_user_port(current_device, epon, &mac);
+}
+
+static bcmos_errno epon_oam_dpoe_clear_ingress_rules_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_clear_ingress_rules(current_device, epon, &mac);
+}
+
+static bcmos_errno epon_oam_dpoe_set_basic_queue_config_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+ uint8_t up_queue_size = (uint8_t)bcmcli_find_named_parm(session, "up_size")->value.unumber;
+ uint8_t dn_queue_size = (uint8_t)bcmcli_find_named_parm(session, "dn_size")->value.unumber;
+
+ return epon_oam_dpoe_set_basic_queue_config(current_device, epon, &mac, up_queue_size, dn_queue_size);
+}
+
+static bcmos_errno epon_oam_dpoe_add_ingress_rules_network_pon_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_add_ingress_rules_network_pon(current_device, epon, &mac, DPOE_RULE_VLAN_MODE_NONE, NULL);
+}
+
+static bcmos_errno epon_oam_dpoe_add_ingress_rules_user_port_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_add_ingress_rules_user_port(current_device, epon, &mac, DPOE_RULE_VLAN_MODE_NONE, NULL);
+}
+
+static bcmos_errno epon_oam_dpoe_add_ingress_rules_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_add_ingress_rules(current_device, epon, &mac);
+}
+
+static bcmos_errno epon_oam_dpoe_add_ingress_rules_with_vlan_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+ uint32_t vlan = bcmcli_find_named_parm(session, "vlan")->value.unumber;
+ uint8_t vlan_tag[4];
+
+ vlan = BCMOS_ENDIAN_CPU_TO_BIG_U32(vlan);
+ vlan_tag[0] = (vlan >> 24) & 0xff;
+ vlan_tag[1] = (vlan >> 16) & 0xff;
+ vlan_tag[2] = (vlan >> 8) & 0xff;
+ vlan_tag[3] = (vlan >> 0) & 0xff;
+
+ return epon_oam_dpoe_add_ingress_rules_with_vlan(current_device, epon, &mac, vlan_tag);
+}
+
+static bcmos_errno epon_oam_dpoe_enable_user_traffic_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_enable_user_traffic(current_device, epon, &mac);
+}
+
+static bcmos_errno epon_oam_dpoe_start_upgrade_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+ const char *fw_file = bcmcli_find_named_parm(session, "fw_file")->value.string;
+ /* This should be based on the negotiated PDU size! */
+ uint16_t block_size = (uint16_t)bcmcli_find_named_parm(session, "block_size")->value.unumber;
+ uint8_t timeout = (uint8_t)bcmcli_find_named_parm(session, "timeout")->value.unumber;
+
+ return epon_oam_dpoe_start_upgrade(current_device, epon, &mac, fw_file, block_size, timeout);
+}
+
+static bcmos_errno epon_oam_dpoe_reset_onu_cli(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ bcmolt_epon_ni epon = (bcmolt_epon_ni)bcmcli_find_named_parm(session, "epon_ni")->value.unumber;
+ bcmos_mac_address mac = bcmcli_find_named_parm(session, "mac_address")->value.mac;
+
+ return epon_oam_dpoe_reset_onu(current_device, epon, &mac);
+}
+
+void bcmolt_user_appl_epon_oam_cli_init(bcmcli_entry *top_dir)
+{
+#ifdef ENABLE_CLI
+ static const char *dir_name = "epon_oam";
+
+ if (bcmcli_dir_find(top_dir, dir_name))
+ {
+ return;
+ }
+
+ bcmcli_entry *oam_dir = bcmcli_dir_add(top_dir, dir_name, "EPON OAM", BCMCLI_ACCESS_ADMIN, NULL);
+ BUG_ON(NULL == oam_dir);
+
+ static bcmcli_parm_value queue_0[MAX_REPORT_THRESHOLDS];
+ static bcmcli_parm_value queue_1[MAX_REPORT_THRESHOLDS];
+ static bcmcli_parm_value queue_2[MAX_REPORT_THRESHOLDS];
+ static bcmcli_parm_value queue_3[MAX_REPORT_THRESHOLDS];
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_get_critical_info", "DPoE get ONU ID and max links",
+ epon_oam_dpoe_get_critical_info_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_set_oam_rate", "DPoE set OAM rate",
+ epon_oam_dpoe_set_oam_rate_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0),
+ BCMCLI_MAKE_PARM("min", "Minimum OAM rate", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("max", "Maximum OAM rate", BCMCLI_PARM_UNUMBER, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_set_report_thresholds", "DPoE set report threhsolds",
+ epon_oam_dpoe_set_report_thresholds_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0),
+ BCMCLI_MAKE_PARM_ARRAY("queue_0",
+ "Report threhsold for queue set 0",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_NONE,
+ MAX_REPORT_THRESHOLDS,
+ queue_0),
+ BCMCLI_MAKE_PARM_ARRAY("queue_1",
+ "Report threhsold for queue set 0",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ MAX_REPORT_THRESHOLDS,
+ queue_1),
+ BCMCLI_MAKE_PARM_ARRAY("queue_2",
+ "Report threhsold for queue set 0",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ MAX_REPORT_THRESHOLDS,
+ queue_2),
+ BCMCLI_MAKE_PARM_ARRAY("queue_3",
+ "Report threhsold for queue set 0",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ MAX_REPORT_THRESHOLDS,
+ queue_3));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_clear_ingress_rules_network_pon", "DPoE clear ingress rules for network PON",
+ epon_oam_dpoe_clear_ingress_rules_network_pon_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_clear_ingress_rules_user_port", "DPoE clear ingress rules for user port",
+ epon_oam_dpoe_clear_ingress_rules_user_port_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_clear_ingress_rules", "DPoE clear ingress rules",
+ epon_oam_dpoe_clear_ingress_rules_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_set_basic_queue_config", "DPoE set basic queue config for 1 link/1 port",
+ epon_oam_dpoe_set_basic_queue_config_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0),
+ BCMCLI_MAKE_PARM("up_size", "upstream queue size", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("dn_size", "downstream queue size", BCMCLI_PARM_UNUMBER, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_add_ingress_rules_network_pon", "DPoE add ingress rules for network PON",
+ epon_oam_dpoe_add_ingress_rules_network_pon_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_add_ingress_rules_user_port", "DPoE add ingress rules for user port",
+ epon_oam_dpoe_add_ingress_rules_user_port_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_add_ingress_rules", "DPoE add ingress rules",
+ epon_oam_dpoe_add_ingress_rules_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_add_ingress_rules_with_vlan", "DPoE add ingress rules with VLAN",
+ epon_oam_dpoe_add_ingress_rules_with_vlan_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0),
+ BCMCLI_MAKE_PARM("vlan", "VLAN", BCMCLI_PARM_UNUMBER, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_enable_user_traffic", "DPoE enable user traffic",
+ epon_oam_dpoe_enable_user_traffic_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_fw_upgrade", "DPoE firwmare upgrade",
+ epon_oam_dpoe_start_upgrade_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0),
+ BCMCLI_MAKE_PARM("fw_file", "Firmware file", BCMCLI_PARM_STRING, 0),
+ BCMCLI_MAKE_PARM("block_size", "Block size", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("timeout", "Timeout in seconds", BCMCLI_PARM_UNUMBER, 0));
+
+ BCMCLI_MAKE_CMD(oam_dir, "dpoe_reset_onu", "DPoE reset ONU",
+ epon_oam_dpoe_reset_onu_cli,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_UNUMBER, 0),
+ BCMCLI_MAKE_PARM("mac_address", "link MAC", BCMCLI_PARM_MAC, 0));
+#endif
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/bcmolt_user_appl_epon_oam_cli.h b/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/bcmolt_user_appl_epon_oam_cli.h
new file mode 100644
index 0000000..99b4ae8
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/epon_oam_cli/bcmolt_user_appl_epon_oam_cli.h
@@ -0,0 +1,38 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_EPON_OAM_CLI_H_
+#define _BCMOLT_USER_APPL_EPON_OAM_CLI_H_
+
+#include "bcmcli.h"
+#include "bcmolt_msg.h"
+
+void bcmolt_user_appl_epon_oam_cli_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/Makefile b/bcm68620_release/release/host_reference/user_appl/image_transfer/Makefile
new file mode 100644
index 0000000..e2392f5
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/Makefile
@@ -0,0 +1,12 @@
+# File transfer application
+
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_image_transfer
+ MOD_TYPE = lib
+ MOD_DEPS = host_api
+ srcs = bcmolt_image_transfer.c bcmolt_image_transfer_user.c bcmolt_image_transfer_cli.c
+
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+endif
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer.c b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer.c
new file mode 100644
index 0000000..7085eaa
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer.c
@@ -0,0 +1,507 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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 <bcmtr_interface.h>
+#include <bcmcli.h>
+#include <libgen.h>
+#include "bcmolt_api.h"
+#include "bcm_dev_log.h"
+#include "bcmolt_image_transfer.h"
+#include "bcmolt_image_transfer_user.h"
+
+static bcmuser_mftp_block_size_get mftp_block_size_get;
+static bcmuser_mftp_crc_get mftp_crc_get_cb;
+static bcmuser_mftp_image_read mftp_read_data_cb;
+static bcmuser_mftp_notification_rx mftp_notify_user_cb;
+static mftp_context mftp_context_db[BCMTR_MAX_OLTS];
+
+static bcmos_errno mftp_ind_rx_cb_register(bcmolt_devid device);
+static bcmos_errno mftp_ind_rx_cb_unregister(bcmolt_devid device);
+
+/* ========================================================================== */
+static bcmolt_image_transfer_status mftp_err_to_status(bcmos_errno err)
+{
+ bcmolt_image_transfer_status status;
+ switch (err)
+ {
+ case BCM_ERR_OK:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS;
+ break;
+ case BCM_ERR_NOMEM:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_MEMORY_ALLOCATION_FAILURE;
+ break;
+ case BCM_ERR_INCOMPLETE_TERMINATION:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_PREMATURE_TERMINATION;
+ break;
+ case BCM_ERR_CHECKSUM:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_CRC_ERROR;
+ break;
+ case BCM_ERR_OVERFLOW:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_INTERNAL_ERROR;
+ break;
+ case BCM_ERR_OUT_OF_SYNC:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_BLOCK_OUT_OF_SYNC;
+ break;
+ case BCM_ERR_STATE:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_INVALID_STATE;
+ break;
+ case BCM_ERR_IMAGE_TYPE:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_UNSUPPORTED_FILE_TYPE;
+ break;
+ default:
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_INTERNAL_ERROR;
+ break;
+ }
+ return status;
+}
+
+/* ========================================================================== */
+const char *bcmolt_user_mftp_status_to_str(bcmolt_image_transfer_status status)
+{
+ static const char *mftp_status_to_str_table[BCMOLT_IMAGE_TRANSFER_STATUS__NUM_OF] =
+ {
+ [BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS] = "Succeeded",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_MEMORY_ALLOCATION_FAILURE] = "ERROR: Memory allocation",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_UNSUPPORTED_FILE_TYPE] = "ERROR: Invalid image type",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_CRC_ERROR] = "ERROR: Checksum",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_BLOCK_OUT_OF_SYNC] = "ERROR: Block out-of-sync",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_INTERNAL_ERROR] = "ERROR: Buffer overflow",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_INVALID_STATE] = "ERROR: Invalid state",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_PREMATURE_TERMINATION] = "ERROR: Premature termination",
+ [BCMOLT_IMAGE_TRANSFER_STATUS_ACK_TIMEOUT] = "ERROR: ACK timeout"
+ };
+ return (status >= BCMOLT_IMAGE_TRANSFER_STATUS__NUM_OF) ? "<unknown>" : mftp_status_to_str_table[status];
+}
+
+/* ========================================================================== */
+static inline mftp_context *mftp_context_get(bcmolt_devid device)
+{
+ return &mftp_context_db[device];
+}
+
+/* ========================================================================== */
+/** convert device ID to module ID */
+static inline bcmos_module_id mftp_device_id_to_module_id(bcmolt_devid device)
+{
+ return (BCMOS_MODULE_ID_USER_APPL_IMAGE_TRANSFER0 + device);
+}
+
+/* ========================================================================== */
+/** examines the ACK response. */
+static bcmolt_image_transfer_status mftp_check_params(mftp_context *context,
+ bcmolt_device_image_transfer_complete_data *ack_data)
+{
+ bcmolt_image_transfer_status status = BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS;
+
+ if (context->block_num != ack_data->block_number)
+ {
+ BCM_LOG(DEBUG, context->log_id, "ACK block # mismatch: exp=%u ack=%u\n", context->block_num, ack_data->block_number);
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_BLOCK_OUT_OF_SYNC;
+ }
+ else if (context->image_type != ack_data->image_type)
+ {
+ BCM_LOG(DEBUG, context->log_id, "ACK image type mismatch: exp=%u ack=%u\n", context->image_type, ack_data->image_type);
+ status = BCMOLT_IMAGE_TRANSFER_STATUS_UNSUPPORTED_FILE_TYPE;
+ }
+ else if (ack_data->status != BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS)
+ {
+ BCM_LOG(DEBUG, context->log_id, "OLT returned an error: %s\n", bcmolt_user_mftp_status_to_str(ack_data->status));
+ status = ack_data->status;
+ }
+ return status;
+}
+
+/* ========================================================================== */
+static void mftp_terminate(bcmolt_devid device, bcmolt_device_image_type image_type,
+ uint32_t block_num, bcmolt_image_transfer_status status)
+{
+ mftp_context *context = mftp_context_get(device);
+ bcmos_errno rc = mftp_ind_rx_cb_unregister(device);
+
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, context->log_id, "Failed to un-register IND callback\n");
+ }
+ mftp_notify_user_cb(device, image_type, block_num, status);
+ context->status = MFTP_STATUS_DISABLED;
+}
+
+/* ========================================================================== */
+/** Sends the image transfer start operation to the OLT. */
+static bcmos_errno mftp_send_wrq(bcmolt_devid device, bcmolt_device_image_type image_type,
+ uint32_t image_size, uint32_t crc32, bcmolt_str_64 *image_name)
+{
+ bcmolt_device_image_transfer_start oper;
+ bcmolt_device_key key = { };
+
+ BCMOLT_OPER_INIT(&oper, device, image_transfer_start, key);
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, image_type, image_type);
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, image_size, image_size);
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, crc32, crc32);
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, image_name, *image_name);
+ return bcmolt_oper_submit(device, &oper.hdr);
+}
+
+/* ========================================================================== */
+/** Sends a data block to the OLT. */
+static bcmos_errno mftp_send_data(bcmolt_devid device, uint8_t *buf, uint32_t buf_size,
+ uint32_t block_number, bcmos_bool more_data)
+{
+ bcmolt_device_key key = {.reserved = 0};
+ bcmolt_device_image_transfer_data oper = {};
+ bcmolt_u8_list_u16_hex data;
+ data.len = buf_size;
+ data.val = buf;
+
+ /* Builds a transport message for BCMOLT_DEVICE_OPER_ID_IMAGE_TRANSFER_DATA */
+ BCMOLT_OPER_INIT(&oper, device, image_transfer_data, key);
+ oper.hdr.hdr.type = BCMOLT_MSG_TYPE_SET;
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_data, block_number, block_number);
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_data, more_data, more_data);
+ BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_data, data, data);
+ return bcmolt_oper_submit(device, &oper.hdr);
+}
+
+/* ========================================================================== */
+/* handler for the OPERATION device.image_transfer_start. */
+bcmos_errno bcmolt_user_mftp_start(bcmolt_devid device, bcmolt_device_image_type image_type)
+{
+ mftp_context *context = mftp_context_get(device);
+ uint32_t image_size;
+ uint32_t crc32;
+ const char *path_name;
+ char file_name[MFTP_MAX_PATH_NAME_LEN];
+ char *base_name;
+ bcmolt_str_64 image_name;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ if (context->status != MFTP_STATUS_DISABLED)
+ {
+ return BCM_ERR_IN_PROGRESS;
+ }
+
+ rc = bcmuser_image_transfer_file_size_get(device, image_type, &image_size);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, context->log_id, "Failed to get the image size\n");
+ return rc;
+ }
+
+ path_name = bcmuser_device_image_name_get(image_type);
+ if (path_name == NULL)
+ {
+ BCM_LOG(ERROR, context->log_id, "File not found\n");
+ return BCM_ERR_PARM;
+ }
+ if (strlen(path_name) >= sizeof(file_name))
+ {
+ BCM_LOG(ERROR, context->log_id, "Path name too long\n");
+ return BCM_ERR_PARM;
+ }
+ (void)strcpy(file_name, path_name); /* to make lint happy. */
+
+ base_name = basename(file_name);
+ if (strlen(base_name) >= sizeof(image_name))
+ {
+ BCM_LOG(ERROR, context->log_id, "File name too long\n");
+ return BCM_ERR_PARM;
+ }
+ (void)strcpy(image_name.str, base_name);
+
+ context->status = MFTP_STATUS_WAITING_ACK;
+ context->block_num = 0;
+ context->image_type = image_type;
+ crc32 = mftp_crc_get_cb(device, image_type);
+ BCM_LOG(DEBUG, context->log_id, "Image size=%u crc=0x%x\n", image_size, crc32);
+
+ rc = mftp_ind_rx_cb_register(device);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, context->log_id, "Failed to register IND callback\n");
+ BUG();
+ }
+
+ rc = mftp_send_wrq(device, image_type, image_size, crc32, &image_name);
+ if (BCM_ERR_OK != rc)
+ {
+ BCM_LOG(DEBUG, context->log_id, "WRQ OPER failed. %s\n", bcmos_strerror(rc));
+ (void)mftp_ind_rx_cb_unregister(device);
+ context->status = MFTP_STATUS_DISABLED;
+ }
+ else
+ {
+ bcmos_timer_start(&context->timer, MFTP_ACK_TIMEOUT_US);
+ }
+ return rc;
+}
+
+/* ========================================================================== */
+static void mftp_process_last_ack(bcmolt_devid device, bcmolt_device_image_transfer_complete_data *ack_data)
+{
+ bcmolt_image_transfer_status status;
+ mftp_context *context = mftp_context_get(device);
+
+ status = mftp_check_params(context, ack_data);
+ mftp_terminate(device, ack_data->image_type, ack_data->block_number, status);
+}
+
+/* ========================================================================== */
+/* reads a data block using customer-provided callback and sends it to the embedded. */
+static void mftp_process_ack(bcmolt_devid device, bcmolt_device_image_transfer_complete_data *ack_data)
+{
+ uint32_t offset = 0; /* file-position*/
+ uint32_t bytes_read = 0;
+ bcmos_bool more_data;
+ bcmos_errno rc = BCM_ERR_OK;
+ bcmolt_image_transfer_status status;
+ mftp_context *context = mftp_context_get(device);
+
+ status = mftp_check_params(context, ack_data);
+ if (status != BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS)
+ {
+ mftp_terminate(device, context->image_type, ack_data->block_number, status);
+ return;
+ }
+
+ offset = (context->block_num * context->block_size);
+ context->block_num++;
+
+ /* CALL CUSTOMER-PROVIDED CALLBACK */
+ bytes_read = mftp_read_data_cb(device, context->image_type, offset,
+ context->buf, context->block_size); /* bcmuser_mftp_read_data() */
+
+ more_data = !(bytes_read < context->block_size);
+
+ rc = mftp_send_data(device, context->buf, bytes_read, context->block_num, more_data);
+ if (BCM_ERR_OK != rc)
+ {
+ BCM_LOG(DEBUG, context->log_id, "DATA OPER failed. Block %u. %s\n", context->block_num, bcmos_strerror(rc));
+ mftp_terminate(device, ack_data->image_type, ack_data->block_number, mftp_err_to_status(rc));
+ return;
+ }
+
+ BCM_LOG(DEBUG, context->log_id, "Sent block#=%u buf_size=%u more=%u\n", context->block_num, bytes_read, more_data);
+
+ context->status = (more_data) ? MFTP_STATUS_WAITING_ACK : MFTP_STATUS_WAITING_LAST_ACK;
+
+ bcmos_timer_start(&context->timer, MFTP_ACK_TIMEOUT_US);
+}
+
+/* ========================================================================== */
+/* ACK indication callback */
+static void mftp_process_ind(bcmolt_devid olt, bcmolt_msg *msg)
+{
+ mftp_context *context = mftp_context_get(olt);
+ bcmolt_device_image_transfer_complete *ack;
+ bcmolt_device_image_transfer_complete_data *ack_data;
+
+ ack = (bcmolt_device_image_transfer_complete *)msg;
+ ack_data = &ack->data;
+
+ BCM_LOG(DEBUG, context->log_id, "## ACK: obj=%u, group=%u, image=%u, block=%u, status=%u\n",
+ msg->obj_type, msg->group, ack_data->image_type, ack_data->block_number, ack_data->status);
+
+ switch (context->status)
+ {
+ case MFTP_STATUS_WAITING_ACK:
+ bcmos_timer_stop(&context->timer);
+ mftp_process_ack(olt, ack_data);
+ break;
+ case MFTP_STATUS_WAITING_LAST_ACK:
+ bcmos_timer_stop(&context->timer);
+ mftp_process_last_ack(olt, ack_data);
+ break;
+ default:
+ /* This could happen when the START operation fails. */
+ BCM_LOG(DEBUG, context->log_id, "Unexpected ACK\n");
+ break;
+ }
+
+ bcmolt_msg_free(msg);
+}
+
+/* ========================================================================== */
+static bcmos_errno mftp_ind_rx_cb_register(bcmolt_devid device)
+{
+ bcmtr_handler_parm tparm = {
+ .group = BCMOLT_MGT_GROUP_AUTO,
+ .object = BCMOLT_OBJ_ID_DEVICE,
+ .subgroup = BCMOLT_DEVICE_AUTO_ID_IMAGE_TRANSFER_COMPLETE,
+ .app_cb = mftp_process_ind,
+ .flags = BCMOLT_AUTO_FLAGS_DISPATCH,
+ };
+ mftp_context *context = mftp_context_get(device);
+ uint8_t inst;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ tparm.module = context->module_id;
+
+ for (inst = 0; (inst < BCMTR_MAX_INSTANCES) && (rc == BCM_ERR_OK); inst++)
+ {
+ tparm.instance = inst;
+ rc = bcmtr_msg_handler_unregister(device, &tparm);
+ rc = (rc == BCM_ERR_OK) ? bcmtr_msg_handler_register(device, &tparm) : rc;
+ }
+ return rc;
+}
+
+/* ========================================================================== */
+static bcmos_errno mftp_ind_rx_cb_unregister(bcmolt_devid device)
+{
+ bcmtr_handler_parm tparm = {
+ .group = BCMOLT_MGT_GROUP_AUTO,
+ .object = BCMOLT_OBJ_ID_DEVICE,
+ .subgroup = BCMOLT_DEVICE_AUTO_ID_IMAGE_TRANSFER_COMPLETE,
+ };
+ uint8_t inst;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ for (inst = 0; (inst < BCMTR_MAX_INSTANCES) && (rc == BCM_ERR_OK); inst++)
+ {
+ tparm.instance = inst;
+ rc = bcmtr_msg_handler_unregister(device, &tparm);
+ }
+ return rc;
+}
+
+/* ========================================================================== */
+/* ACK response timeout handler */
+static bcmos_timer_rc mftp_ack_timeout_handler(bcmos_timer *timer, long data)
+{
+ bcmolt_devid device = (bcmolt_devid)data;
+ mftp_context *context = mftp_context_get(device);
+
+ if ((context->status == MFTP_STATUS_WAITING_ACK) || (context->status == MFTP_STATUS_WAITING_LAST_ACK))
+ {
+ mftp_terminate(device, context->image_type, context->block_num, BCMOLT_IMAGE_TRANSFER_STATUS_ACK_TIMEOUT);
+ }
+ else
+ {
+ BCM_LOG(INFO, context->log_id, "timer in irrelevant state\n");
+ }
+ return BCMOS_TIMER_STOP;
+}
+
+/* ========================================================================== */
+static bcmos_errno mftp_init_timers(bcmolt_devid device)
+{
+ mftp_context *context = mftp_context_get(device);
+ bcmos_timer_parm timer_params = {};
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* If timer was already created, then we don't need to re-create it */
+ if ((context->timer.flags & BCMOS_TIMER_FLAG_VALID) != 0)
+ return BCM_ERR_OK;
+
+ timer_params.name = context->name;
+ timer_params.owner = context->module_id;
+ timer_params.periodic = BCMOS_FALSE;
+ timer_params.handler = mftp_ack_timeout_handler;
+ timer_params.data = (long)device;
+
+ rc = bcmos_timer_create(&context->timer, &timer_params);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, context->log_id, "Timer creation failed. %s\n", bcmos_strerror(rc));
+ }
+ return rc;
+}
+
+/* ========================================================================== */
+static bcmos_errno mftp_init_tasks(bcmolt_devid device)
+{
+ mftp_context *context = mftp_context_get(device);
+ bcmos_task_parm task_params = {};
+ bcmos_errno rc = BCM_ERR_OK;
+
+ task_params.name = context->name;
+ task_params.priority = TASK_PRIORITY_USER_APPL_IMAGE_TRANSFER;
+ task_params.core = BCMOS_CPU_CORE_ANY; /* No CPU affinity */
+ task_params.init_handler = NULL;
+ task_params.data = (long)device;
+
+ rc = bcmos_task_create(&context->task, &task_params);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, context->log_id, "Task creation failed. %s \n", bcmos_strerror(rc));
+ }
+ return rc;
+}
+
+/* ========================================================================== */
+static bcmos_errno mftp_init_modules(bcmolt_devid device)
+{
+ mftp_context *context = mftp_context_get(device);
+ bcmos_module_parm module_params = {};
+ bcmos_errno rc = BCM_ERR_OK;
+
+ context->module_id = mftp_device_id_to_module_id(device);
+ module_params.qparm.name = context->name;
+ module_params.qparm.size = BCMOS_MSG_POOL_DEFAULT_SIZE;
+ module_params.init = NULL;
+
+ rc = bcmos_module_create(context->module_id, &context->task, &module_params);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, context->log_id, "Module creation failed. %s \n", bcmos_strerror(rc));
+ }
+ return rc;
+}
+
+/* ========================================================================== */
+/* Initializes the MFTP parameters. */
+void bcmolt_user_mftp_init(void)
+{
+ mftp_context *context;
+ bcmolt_devid device;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* The customer should provide these callback functions. */
+ mftp_read_data_cb = bcmuser_image_transfer_read_data;
+ mftp_notify_user_cb = bcmuser_image_transfer_notification_rx;
+ mftp_block_size_get = bcmuser_image_transfer_block_size_get;
+ mftp_crc_get_cb = bcmuser_image_transfer_crc_get;
+
+ /* initialize some parameters, i.e. the .status and .timer.
+ other parameters are (re)built upon START, so no need to init. */
+ for (device = 0; (device < BCMTR_MAX_OLTS) && (rc == BCM_ERR_OK); device++)
+ {
+ context = mftp_context_get(device);
+ context->status = MFTP_STATUS_DISABLED;
+ context->block_size = mftp_block_size_get();
+ context->buf = bcmos_calloc(context->block_size);
+ rc = (context->buf == NULL) ? BCM_ERR_NOMEM : rc;
+ snprintf(context->name, sizeof(context->name), "mftp%u", device);
+ rc = (rc == BCM_ERR_OK) ? mftp_init_tasks(device) : rc;
+ rc = (rc == BCM_ERR_OK) ? mftp_init_modules(device) : rc;
+ rc = (rc == BCM_ERR_OK) ? mftp_init_timers(device) : rc;
+ context->log_id = bcm_dev_log_id_register(context->name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
+ }
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer.h b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer.h
new file mode 100644
index 0000000..0ffea3c
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer.h
@@ -0,0 +1,86 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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.
+
+:>
+ */
+
+#ifndef _BCMOLT_IMAGE_TRANSFER_H_
+#define _BCMOLT_IMAGE_TRANSFER_H_
+
+#include <bcmolt_model_types.h>
+#include <bcmos_system.h>
+
+/* Returns a data block of given size of given image type at given offset. */
+typedef int (*bcmuser_mftp_image_read)(bcmolt_devid device, bcmolt_device_image_type image_type,
+ uint32_t offset, uint8_t *buf, uint32_t buf_size);
+
+/* Returns the size of data block which is used for the data transfer. */
+typedef uint32_t (*bcmuser_mftp_block_size_get)(void);
+
+/* Returns the CRC32 checksum of entire image. */
+typedef uint32_t (*bcmuser_mftp_crc_get)(bcmolt_devid device,
+ bcmolt_device_image_type image_type);
+
+/* Callback to notify the user of the completion of the image transfer. */
+typedef void (*bcmuser_mftp_notification_rx)(bcmolt_devid device,
+ bcmolt_device_image_type image_type, uint32_t block_num, bcmolt_image_transfer_status status);
+
+typedef enum
+{
+ MFTP_STATUS_DISABLED,
+ MFTP_STATUS_WAITING_ACK,
+ MFTP_STATUS_WAITING_LAST_ACK
+} mftp_status;
+
+typedef struct
+{
+ /* runtime parameters */
+ mftp_status status;
+ uint32_t block_num; /* block number sent. */
+ bcmolt_device_image_type image_type;
+
+ /* init-time parameters */
+ uint32_t block_size;
+ uint8_t *buf;
+ char name[MAX_TIMER_NAME_SIZE];
+ bcmos_task task;
+ bcmos_module_id module_id;
+ bcmos_timer timer;
+ dev_log_id log_id;
+} mftp_context;
+
+
+const char *bcmolt_user_mftp_status_to_str(bcmolt_image_transfer_status status);
+
+/** handler for the OPERATION device.image_transfer_start. */
+bcmos_errno bcmolt_user_mftp_start(bcmolt_devid device, bcmolt_device_image_type image_type);
+
+/** Initializes the MFTP parameters. */
+void bcmolt_user_mftp_init(void);
+
+#endif
+
+
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_cli.c b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_cli.c
new file mode 100644
index 0000000..a29243a
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_cli.c
@@ -0,0 +1,87 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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 <bcmcli.h>
+#include <bcmolt_model_types.h>
+#include <bcmolt_msg.h>
+#include <bcmolt_api.h>
+#include <bcm_api_cli_helpers.h>
+#include "bcmolt_image_transfer.h"
+#include "bcmolt_image_transfer_cli.h"
+
+static bcmos_bool bcmolt_user_appl_device_state_is_ready(bcmolt_devid device)
+{
+ bcmos_errno err;
+ bcmolt_device_cfg cfg = {};
+ bcmolt_device_key key = {};
+
+ BCMOLT_CFG_INIT(&cfg, device, key);
+ BCMOLT_CFG_PROP_GET(&cfg, device, state);
+ err = bcmolt_cfg_get(device, &cfg.hdr);
+ return (err == BCM_ERR_OK) && (cfg.data.state == BCMOLT_DEVICE_STATE_READY);
+}
+
+/** start the image transfer.
+ */
+static bcmos_errno bcmolt_user_appl_cli_image_transfer_start(bcmcli_session *session,
+ const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmolt_device_image_type image_type;
+ bcmolt_devid device = 0;
+ bcmos_errno err;
+
+ if (!bcmolt_user_appl_device_state_is_ready(device))
+ {
+ bcmcli_session_print(session, "device not connected\n");
+ return BCM_ERR_NOT_CONNECTED;
+ }
+
+ image_type = bcmcli_find_named_parm(session, "image_type")->value.unumber;
+
+ err = bcmolt_user_mftp_start(device, image_type);
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "%s\n", bcmos_strerror(err));
+ }
+ return err;
+}
+
+bcmos_errno bcmolt_user_appl_cli_image_transfer_init(bcmcli_entry *top_dir)
+{
+ bcmcli_entry *dir = bcmcli_dir_add(top_dir, "image_transfer",
+ "Image Transfer user application", BCMCLI_ACCESS_ADMIN, NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+
+ BCMCLI_MAKE_CMD(dir, "start", "Start Image Transfer application", bcmolt_user_appl_cli_image_transfer_start,
+ BCMCLI_MAKE_PARM_ENUM("image_type", "Image type", bcmolt_device_image_type_string_table, BCMCLI_PARM_FLAG_NONE));
+
+ return BCM_ERR_OK;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_cli.h b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_cli.h
new file mode 100644
index 0000000..469f6d8
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_cli.h
@@ -0,0 +1,38 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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.
+
+:>
+*/
+
+#ifndef _BCMOLT_IMAGE_TRANSFER_CLI_H_
+#define _BCMOLT_IMAGE_TRANSFER_CLI_H_
+
+#include <bcmos_system.h>
+#include <bcmcli.h>
+
+bcmos_errno bcmolt_user_appl_cli_image_transfer_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_user.c b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_user.c
new file mode 100644
index 0000000..b60fc83
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_user.c
@@ -0,0 +1,212 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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.
+
+:>
+ */
+
+/*
+ * This file is contains the customer-provided code.
+ * This file is provided as an example.
+ * The customer may change the file path/names.
+ * The customer may change the file access functions.
+ * The customer may adjust the data block size.
+ * Adding a new image type requires the object model change and sync up with
+ * the embedded size, which should be done by the Broadcom engineering.
+ */
+
+#include <bcmolt_conv.h>
+#include "bcmolt_image_transfer.h"
+#include "bcmolt_image_transfer_user.h"
+#include "bcmolt_utils.h"
+
+/* example names for debugging purpose only */
+#define BOOT_FILE "/opt/bcm68620/bcm68620_boot.bin"
+#define APPL_FILE "/opt/bcm68620/bcm68620_appl.bin"
+#define ITU_PON_ONU_FW_FILE "/opt/bcm68620/gpon_onu_fw.w"
+#define EPON_ONU_FW_FILE "/opt/bcm68620/epon_onu_fw.tkf"
+
+static int2str_t image_type2str[] =
+{
+ {BCMOLT_DEVICE_IMAGE_TYPE_BOOTLOADER, BOOT_FILE},
+ {BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION, APPL_FILE},
+ {BCMOLT_DEVICE_IMAGE_TYPE_ITU_PON_ONU_FIRMWARE, ITU_PON_ONU_FW_FILE},
+ {BCMOLT_DEVICE_IMAGE_TYPE_EPON_ONU_FIRMWARE, EPON_ONU_FW_FILE},
+ {-1}
+};
+
+/** Get the device file name.
+ *
+ * \param[in] image_type Image type. defined in the object model.
+ *
+ * \return string Pointer to device image name, including the path.
+ */
+const char *bcmuser_device_image_name_get(bcmolt_device_image_type image_type)
+{
+ if (image_type < BCMOLT_DEVICE_IMAGE_TYPE__NUM_OF)
+ {
+ return int2str(image_type2str, image_type);
+ }
+ return NULL;
+}
+
+/** Open a file with given image type.
+ *
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[out] fpp File pointer
+ *
+ * \return bcmos_errno Error code
+ */
+static bcmos_errno bcmuser_image_transfer_file_open(bcmolt_device_image_type image_type, FILE **fpp)
+{
+ bcmos_errno rc = BCM_ERR_PARM;
+
+ if (image_type < BCMOLT_DEVICE_IMAGE_TYPE__NUM_OF)
+ {
+ FILE *fp;
+ const char *fname = bcmuser_device_image_name_get(image_type);
+ fp = fopen(fname, "rb");
+ if (fp != NULL)
+ {
+ *fpp = fp;
+ rc = BCM_ERR_OK;
+ }
+ }
+ return rc;
+}
+
+/** Get the size of the file.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[out] fsize File size
+ *
+ * \return bcmos_errno
+ */
+bcmos_errno bcmuser_image_transfer_file_size_get(bcmolt_devid device,
+ bcmolt_device_image_type image_type, uint32_t *fsize)
+{
+ FILE *fp;
+ long fp_prev;
+ bcmos_errno rc = BCM_ERR_OK;
+ (void)device;
+
+ rc = bcmuser_image_transfer_file_open(image_type, &fp);
+ if (rc == BCM_ERR_OK)
+ {
+ fp_prev = ftell(fp);
+ BUG_ON(fp_prev < 0);
+ BUG_ON(fseek(fp, 0, SEEK_END) != 0);
+ *fsize = ftell(fp);
+ BUG_ON(fseek(fp, fp_prev, SEEK_SET) != 0); /* go back to where we were. */
+ BUG_ON(fclose(fp) != 0);
+ }
+ return rc;
+}
+
+/** Read data from given offset.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[in] offset File position indicator
+ * \param[out] buf Buffer to store the data
+ * \param[in] buf_size Buffer size
+ *
+ * \return Bytes read
+ */
+int bcmuser_image_transfer_read_data(bcmolt_devid device, bcmolt_device_image_type image_type,
+ uint32_t offset, uint8_t *buf, uint32_t buf_size)
+{
+ FILE *fp;
+ uint32_t bytes_read = 0;
+ (void)device;
+
+ if (bcmuser_image_transfer_file_open(image_type, &fp) == BCM_ERR_OK)
+ {
+ BUG_ON(fseek(fp, 0, SEEK_END) != 0);
+ if (offset < ftell(fp))
+ {
+ BUG_ON(fseek(fp, offset, SEEK_SET) != 0);
+ bytes_read = fread(buf, 1, buf_size, fp);
+ }
+ BUG_ON(fclose(fp) != 0);
+ }
+ return bytes_read;
+}
+
+/** Get the unit size of the transfer data block.
+ *
+ * \return Size of the block in bytes.
+ */
+uint32_t bcmuser_image_transfer_block_size_get(void)
+{
+ return MFTP_DATA_BLOCK_SIZE;
+}
+
+/** Returns the CRC32 checksum of entire image.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ *
+ * \return CRC32 checksum
+ */
+uint32_t bcmuser_image_transfer_crc_get(bcmolt_devid device, bcmolt_device_image_type image_type)
+{
+ FILE *fp;
+ uint32_t crc = 0;
+ (void)device;
+
+ if (bcmuser_image_transfer_file_open(image_type, &fp) == BCM_ERR_OK)
+ {
+ while (1)
+ {
+ char buf[512]; /* any size is OK. */
+ uint32_t bytes_read;
+
+ bytes_read = fread(buf, 1, sizeof(buf), fp);
+ if (bytes_read == 0)
+ break;
+ crc = eth_calc_crc32(crc, buf, bytes_read);
+ }
+ BUG_ON(fclose(fp) != 0);
+ }
+ return crc;
+}
+
+/** Process the notification from the image transfer module.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[in] block_num last block number
+ * \param[in] status Image transfer status
+ */
+void bcmuser_image_transfer_notification_rx(bcmolt_devid device,
+ bcmolt_device_image_type image_type, uint32_t block_num,
+ bcmolt_image_transfer_status status)
+{
+ const char *str = bcmolt_user_mftp_status_to_str(status);
+ bcmos_printf("Image transfer: %s: type=%u block=%u\n", str, image_type, block_num);
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_user.h b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_user.h
new file mode 100644
index 0000000..3a0366b
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/image_transfer/bcmolt_image_transfer_user.h
@@ -0,0 +1,100 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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.
+
+:>
+ */
+
+#ifndef _BCMOLT_IMAGE_TRANSFER_USER_H_
+#define _BCMOLT_IMAGE_TRANSFER_USER_H_
+
+#include <bcmolt_model_types.h>
+#include <bcmos_system.h>
+
+#define MFTP_DATA_BLOCK_SIZE 512
+
+#define MFTP_ACK_TIMEOUT_US (2 * 1000000)
+
+#define MFTP_MAX_PATH_NAME_LEN 256
+
+/** Get the device file name.
+ *
+ * \param[in] image_type Image type. defined in the object model.
+ *
+ * \return string Device image name, including the path.
+ */
+const char *bcmuser_device_image_name_get(bcmolt_device_image_type image_type);
+
+/** Get the size of the file.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[out] fsize File size
+ *
+ * \return bcmos_errno
+ */
+bcmos_errno bcmuser_image_transfer_file_size_get(bcmolt_devid device,
+ bcmolt_device_image_type image_type, uint32_t *fsize);
+
+/** Read data from given offset.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[in] offset File position indicator
+ * \param[out] buf Buffer to store the data
+ * \param[in] buf_size Buffer size
+ *
+ * \return Bytes read
+ */
+int bcmuser_image_transfer_read_data(bcmolt_devid device, bcmolt_device_image_type image_type,
+ uint32_t offset, uint8_t *buf, uint32_t buf_size);
+
+/** Get the unit size of the transfer data block.
+ *
+ * \return Size of the block in bytes.
+ */
+uint32_t bcmuser_image_transfer_block_size_get(void);
+
+/** Returns the CRC32 checksum of entire image.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ *
+ * \return CRC32 checksum
+ */
+uint32_t bcmuser_image_transfer_crc_get(bcmolt_devid device, bcmolt_device_image_type image_type);
+
+/** Process the notification from the image transfer module.
+ *
+ * \param[in] device Device ID
+ * \param[in] image_type Image type. defined in the object model.
+ * \param[in] block_num last block number
+ * \param[in] status Image transfer status
+ */
+void bcmuser_image_transfer_notification_rx(bcmolt_devid device,
+ bcmolt_device_image_type image_type, uint32_t block_num, bcmolt_image_transfer_status status);
+
+#endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/Makefile b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/Makefile
new file mode 100755
index 0000000..9756ee5
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/Makefile
@@ -0,0 +1,12 @@
+# ONU tuning application
+
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl_onu_tuning
+ MOD_TYPE = lib
+ MOD_DEPS = host_api common_gpon bcm_board
+ srcs = bcmolt_user_appl_onu_tuning.c bcmolt_user_appl_onu_tuning_cli.c
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning.c b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning.c
new file mode 100755
index 0000000..8d3b97b
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning.c
@@ -0,0 +1,312 @@
+/*
+<: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 <bcmolt_host_api.h>
+#include "bcmolt_user_appl_onu_tuning.h"
+
+#define ONU_TUNING_TASK_MSG_Q_SIZE 256
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ bcmolt_auto *ind;
+ bcmolt_devid device_id;
+} ot_task_msg;
+
+static bcmolt_onu_tuning_context ot_context[BCMTR_MAX_OLTS];
+
+
+static bcmos_errno onu_tuning_init_task(bcmolt_devid device_id)
+{
+ bcmos_errno rc;
+ bcmos_task_parm task_params = {};
+ snprintf(ot_context[device_id].task.name, sizeof(ot_context[device_id].task.name), "user_appl_onu_tuning%u", device_id);
+ task_params.name = ot_context[device_id].task.name;
+ task_params.priority = TASK_PRIORITY_USER_APPL_ONU_TUNING;
+ task_params.core = BCMOS_CPU_CORE_ANY; /* No CPU affinity */
+ task_params.init_handler = NULL;
+ task_params.data = (long)device_id;
+ rc = bcmos_task_create(&ot_context[device_id].task, &task_params);
+ BCMOS_TRACE_CHECK_RETURN(rc, rc, "bcmos_task_create()\n");
+
+ return rc;
+}
+
+static bcmos_errno onu_tuning_init_module(bcmolt_devid device_id)
+{
+ /* This value is used inside bcmolt_onu_tuning_module_init for timer owner */
+ ot_context[device_id].device = device_id;
+ bcmos_module_parm module_params =
+ {
+ .qparm = { .name = "user_appl_onu_tuning",
+ .size = ONU_TUNING_TASK_MSG_Q_SIZE },
+ };
+
+ return bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_ONU_TUNING_DEV0 + device_id, &ot_context[device_id].task, &module_params);
+}
+
+void bcmolt_onu_tuning_appl_init(bcmolt_devid device_id)
+{
+ bcmos_errno err;
+
+#ifdef ENABLE_LOG
+ char log_name[MAX_DEV_LOG_ID_NAME] = {};
+ snprintf(log_name, sizeof(log_name) - 1, "user_appl_onu_tuning%u", device_id);
+ ot_context[device_id].log_id = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_DEBUG, DEV_LOG_ID_TYPE_BOTH);
+#endif
+
+ err = bcmos_mutex_create(&ot_context[device_id].mutex, 0, NULL);
+ BUG_ON(err != BCM_ERR_OK);
+
+ err = onu_tuning_init_task(device_id);
+ BUG_ON(err != BCM_ERR_OK);
+
+ err = onu_tuning_init_module(device_id);
+ BUG_ON(err != BCM_ERR_OK);
+
+}
+
+bcmos_errno bcmolt_onu_tuning_appl_start(bcmolt_devid device_id)
+{
+ bcmos_errno ret = BCM_ERR_OK;
+
+ bcmos_mutex_lock(&ot_context[device_id].mutex);
+
+ if (ot_context[device_id].is_running)
+ {
+ BCM_LOG(ERROR, ot_context[device_id].log_id, "ONU tuning application already running on device=%u\n", device_id);
+ ret = BCM_ERR_STATE;
+ goto exit;
+ }
+
+ ot_context[device_id].is_running = BCMOS_TRUE;
+ ot_context[device_id].device = device_id;
+
+ BCM_LOG(INFO, ot_context[device_id].log_id, "ONU tuning application started on device=%u\n", device_id);
+ ret = BCM_ERR_OK;
+
+exit:
+ bcmos_mutex_unlock(&ot_context[device_id].mutex);
+ return ret;
+}
+
+bcmos_errno bcmolt_onu_tuning_appl_stop(bcmolt_devid device_id)
+{
+ bcmos_errno ret;
+
+ bcmos_mutex_lock(&ot_context[device_id].mutex);
+
+ if (!ot_context[device_id].is_running)
+ {
+ ret = BCM_ERR_STATE;
+ goto exit;
+ }
+
+ ot_context[device_id].is_running = BCMOS_FALSE;
+ bcmos_timer_stop(&ot_context[device_id].timer);
+ BCM_LOG(INFO, ot_context[device_id].log_id, "ONU tuning application stopped on device=%u\n", device_id);
+ ret = BCM_ERR_OK;
+
+exit:
+ bcmos_mutex_unlock(&ot_context[device_id].mutex);
+ return ret;
+}
+
+bcmos_bool bcmolt_onu_tuning_appl_is_running(bcmolt_devid device_id)
+{
+ bcmos_bool ret;
+
+ bcmos_mutex_lock(&ot_context[device_id].mutex);
+ ret = ot_context[device_id].is_running;
+ bcmos_mutex_unlock(&ot_context[device_id].mutex);
+
+ return ret;
+}
+
+static bcmos_bool bcmolt_onu_tuning_is_interested(bcmolt_auto *ind)
+{
+ switch (ind->hdr.obj_type)
+ {
+ case BCMOLT_OBJ_ID_XGPON_ONU:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_XGPON_ONU_AUTO_ID_TUNING_RESPONSE:
+ return BCMOS_TRUE;
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_TUNING_IN_COMPLETED:
+ return BCMOS_TRUE;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return BCMOS_FALSE;
+}
+
+static void bcmolt_onu_tuning_ind_cb(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ ot_task_msg *msg = (ot_task_msg *)os_msg;
+ bcmolt_pon_ni pon_ni;
+ bcmolt_pon_onu_id onu_id;
+ bcmolt_auto *ind = msg->ind;
+ bcmos_errno rc;
+
+ switch (msg->ind->hdr.obj_type)
+ {
+ case BCMOLT_OBJ_ID_XGPON_ONU:
+ switch (msg->ind->hdr.subgroup)
+ {
+ case BCMOLT_XGPON_ONU_AUTO_ID_TUNING_RESPONSE:
+ {
+ bcmolt_xgpon_onu_tuning_response *onu_tuning_response = (bcmolt_xgpon_onu_tuning_response *)ind;
+ onu_id = onu_tuning_response->key.onu_id;
+ pon_ni = onu_tuning_response->key.pon_ni;
+
+ if (onu_tuning_response->data.ack == BCMOS_TRUE && onu_tuning_response->data.result == BCMOLT_RESULT_SUCCESS)
+ {
+ BCM_LOG(INFO, ot_context[msg->device_id].log_id, "Indication BCMOLT_XGPON_ONU_AUTO_ID_TUNING_RESPONSE handled: PON:%u ONU:%u target_pon_ni=%u\n", pon_ni, onu_id, ot_context[msg->device_id].onu_db[onu_id].target_pon_ni);
+
+ /* start tuning in onu on target pon id */
+ bcmolt_xgpon_onu_key key = { .pon_ni = ot_context[msg->device_id].onu_db[onu_id].target_pon_ni, .onu_id = onu_id };
+ bcmolt_xgpon_onu_onu_tuning_in onu_tuning_in;
+
+ BCMOLT_OPER_INIT(&onu_tuning_in, xgpon_onu, onu_tuning_in, key);
+ rc = bcmolt_oper_submit(msg->device_id, &onu_tuning_in.hdr);
+ if (rc != BCM_ERR_OK)
+ BCM_LOG(ERROR, ot_context[msg->device_id].log_id, "ONU=%u tuning in to PON=%u failed\n", onu_id, ot_context[msg->device_id].onu_db[onu_id].target_pon_ni);
+ }
+
+ break;
+ }
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_TUNING_IN_COMPLETED:
+ {
+ bcmolt_xgpon_onu_onu_tuning_in_completed *onu_tuning_in_completed = (bcmolt_xgpon_onu_onu_tuning_in_completed *)ind;
+ onu_id = onu_tuning_in_completed->key.onu_id;
+ pon_ni = onu_tuning_in_completed->key.pon_ni;
+ if (onu_tuning_in_completed->data.result == BCMOLT_RESULT_SUCCESS)
+ {
+ BCM_LOG(INFO, ot_context[msg->device_id].log_id, "Indication BCMOLT_XGPON_ONU_AUTO_ID_ONU_TUNING_IN_COMPLETED handled: PON:%u ONU:%u\n", pon_ni, onu_id);
+
+ /* start tuning out onu on source pon id */
+ bcmolt_xgpon_onu_key key = { .pon_ni = ot_context[msg->device_id].onu_db[onu_id].source_pon_ni, .onu_id = onu_id };
+ bcmolt_xgpon_onu_onu_tuning_out onu_tuning_out;
+
+ BCMOLT_OPER_INIT(&onu_tuning_out, xgpon_onu, onu_tuning_out, key);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, target_ds_pon_id, ot_context[msg->device_id].onu_db[onu_id].target_pon_id);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, target_us_pon_id, ot_context[msg->device_id].onu_db[onu_id].target_pon_id);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, time_to_switch, 1000);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, rollback, BCMOS_FALSE);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, status, BCMOLT_STATUS_OFF);
+ rc = bcmolt_oper_submit(msg->device_id, &onu_tuning_out.hdr);
+ if (rc != BCM_ERR_OK)
+ BCM_LOG(ERROR, ot_context[msg->device_id].log_id, "ONU=%u tuning out from PON=%u failed\n", onu_id, ot_context[msg->device_id].onu_db[onu_id].source_pon_ni);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* free the cloned indication since we're done processing it */
+ bcmolt_msg_free(&msg->ind->hdr);
+ /* free the internal OS message handle */
+ bcmos_free(os_msg);
+}
+
+bcmos_errno bcmolt_onu_tuning_process_ind(bcmolt_devid device_id, bcmolt_auto *ind)
+{
+ bcmos_errno err;
+ bcmolt_msg *ind_clone = NULL;
+
+ /* if the application isn't running, don't listen to any indications */
+ if (!bcmolt_onu_tuning_appl_is_running(device_id))
+ return BCM_ERR_OK;
+
+ /* check to see if we have a handler for this type of indication */
+ if (!bcmolt_onu_tuning_is_interested(ind))
+ return BCM_ERR_OK;
+
+ /* clone the indication into newly-allocated memory so we can handle it after the original message has been freed */
+ err = bcmolt_msg_clone(&ind_clone, &ind->hdr);
+ if (err != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, ot_context[device_id].log_id, "Indication clone failed: %s\n", bcmos_strerror(err));
+ return err;
+ }
+
+ /* send the clone to the internal task's message queue */
+ ot_task_msg *msg = bcmos_calloc(sizeof(*msg));
+ if (msg == NULL)
+ {
+ BCM_LOG(ERROR, ot_context[device_id].log_id, "Message calloc failed: %s\n", bcmos_strerror(err));
+ bcmolt_msg_free(ind_clone);
+ return BCM_ERR_NOMEM;
+ }
+ msg->os_msg.handler = bcmolt_onu_tuning_ind_cb;
+ msg->os_msg.release = bcmolt_os_msg_release_cb;
+ msg->device_id = device_id;
+ msg->ind = (bcmolt_auto *)ind_clone;
+
+ err = bcmos_msg_send_to_module(TASK_PRIORITY_USER_APPL_ONU_TUNING + device_id, &msg->os_msg, 0);
+ if (err != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, ot_context[device_id].log_id, "Message send failed: %s\n", bcmos_strerror(err));
+ bcmolt_msg_free(ind_clone);
+ return err;
+ }
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno onu_tuning_update_onu_db (bcmolt_devid device_id, bcmolt_xgpon_onu_id onu_id, bcmolt_pon_ni source_pon_ni, bcmolt_pon_ni target_pon_ni, bcmolt_pon_id target_pon_id, bcmos_bool rollback)
+{
+ bcmos_errno ret;
+ bcmos_mutex_lock(&ot_context[device_id].mutex);
+
+ if (!ot_context[device_id].is_running)
+ {
+ ret = BCM_ERR_STATE;
+ goto exit;
+ }
+ ot_context[device_id].onu_db[onu_id].target_pon_ni = target_pon_ni;
+ ot_context[device_id].onu_db[onu_id].source_pon_ni = source_pon_ni;
+ ot_context[device_id].onu_db[onu_id].target_pon_id = target_pon_id;
+ ot_context[device_id].onu_db[onu_id].rollback = rollback;
+ ret = BCM_ERR_OK;
+
+exit:
+ bcmos_mutex_unlock(&ot_context[device_id].mutex);
+ return ret;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning.h b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning.h
new file mode 100755
index 0000000..681fcc2
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning.h
@@ -0,0 +1,98 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_USER_APPL_ONU_TUNING_H_
+#define _BCMOLT_USER_APPL_ONU_TUNING_H_
+
+#include <bcmolt_host_api.h>
+
+typedef struct
+{
+ bcmolt_pon_ni target_pon_ni;
+ bcmolt_pon_ni source_pon_ni;
+ bcmolt_pon_id target_pon_id;
+ uint32_t time_to_switch;
+ bcmos_bool rollback;
+}bcmolt_onu_tuning_onu_db;
+
+typedef struct
+{
+ bcmos_task task;
+ bcmos_msg_pool msg_pool;
+ bcmos_mutex mutex;
+ bcmos_timer timer;
+ dev_log_id log_id;
+ bcmos_bool is_running;
+ bcmolt_devid device;
+ uint32_t *timeouts;
+ bcmolt_onu_tuning_onu_db onu_db[XGPON_MAX_NUM_OF_ONUS];
+} bcmolt_onu_tuning_context;
+
+typedef enum
+{
+
+ ONU_TUNING_MODE,
+} bcmolt_onu_tuning_mode;
+
+/** Initialize the ONU Tuning application (this should be called as part of application startup). */
+void bcmolt_onu_tuning_appl_init(bcmolt_devid device_id);
+
+/** Start the ONU Tuning application.
+ *
+ * \param[in] device The device ID on which the application will be started.
+ * \return BCM_ERR_OK if the application was started successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_onu_tuning_appl_start(bcmolt_devid device_id);
+
+/** Stop the ONU Tuning application.
+ *
+ * \return BCM_ERR_OK if the application was stopped successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_onu_tuning_appl_stop(bcmolt_devid device_id);
+
+/** Query whether the ONU Tuning application is currently running.
+ *
+ * \return BCMOS_TRUE if the application is running, BCMOS_FALSE otherwise.
+ */
+bcmos_bool bcmolt_onu_tuning_appl_is_running(bcmolt_devid device_id);
+
+/** Process an indication received. If the ONU Tuning application is interested in the indication, the
+ * appropriate action will be taken. If the application is not interested, the indication will be ignored.
+ *
+ *
+ * Note: this function does not free the indication.
+ * The caller must free it using bcmos_msg_free() after calling this function.
+ *
+ * \param[in] ind The indication that was received.
+ * \return BCM_ERR_OK if the indication was processed successfully or ignored, <0 otherwise.
+ */
+bcmos_errno bcmolt_onu_tuning_process_ind(bcmolt_devid device_id, bcmolt_auto *ind);
+bcmos_errno onu_tuning_update_onu_db (bcmolt_devid device_id, bcmolt_xgpon_onu_id onu_id, bcmolt_pon_ni source_pon_ni, bcmolt_pon_ni target_pon_ni, bcmolt_pon_id target_pon_id, bcmos_bool rollback);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning_cli.c b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning_cli.c
new file mode 100755
index 0000000..9d529e3
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning_cli.c
@@ -0,0 +1,187 @@
+/*
+<: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 <bcmolt_host_api.h>
+#include "bcmolt_user_appl_onu_tuning.h"
+#include "bcmolt_user_appl_onu_tuning_cli.h"
+#include <bcmolt_dev_selector.h>
+
+static bcmos_errno bcmolt_user_appl_onu_tuning_cli_create(bcmcli_entry *top_dir);
+
+static bcmcli_entry *onu_tuning_cli_dir;
+static bcmcli_entry *onu_tuning_cli_top_dir;
+static bcmos_bool onu_tuning_is_registered;
+
+/* Destroy CLI commands */
+static void onu_tuning_cli_destroy(void)
+{
+ if (onu_tuning_cli_dir)
+ {
+ bcmcli_token_destroy(onu_tuning_cli_dir);
+ onu_tuning_cli_dir = NULL;
+ }
+}
+
+/* Current device change indication */
+static void bcmolt_user_appl_onu_tuning_device_change_ind(bcmcli_session *session, bcmolt_devid dev)
+{
+ bcmolt_system_mode system_mode;
+ bcmos_errno err = bcmolt_system_mode_get(dev, &system_mode);
+
+ if (err != BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "Error device Id\n");
+ return;
+ }
+
+ if (system_mode == BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G)
+ {
+ bcmcli_session_print(session, "Building onu_tuning CLI for device %d\n", dev);
+
+ err = bcmolt_user_appl_onu_tuning_cli_create(onu_tuning_cli_top_dir);
+ if (err)
+ {
+ bcmcli_session_print(session, "Error building onu_tuning CLI\n");
+ }
+ }
+ else
+ {
+ onu_tuning_cli_destroy();
+ }
+}
+
+static bcmos_errno onu_tuning_cmd_start(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ return bcmolt_onu_tuning_appl_start(current_device);
+}
+
+static bcmos_errno onu_tuning_cmd_stop(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ return bcmolt_onu_tuning_appl_stop(current_device);
+}
+
+static bcmos_errno onu_handover_cmd(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmolt_xgpon_onu_id onu_id;
+ bcmolt_pon_ni target_pon_ni;
+ bcmolt_pon_ni source_pon_ni;
+ uint32_t target_pon_administrative_label;
+ uint8_t target_pon_dwlch_id;
+ uint32_t time_to_switch; /* Time to switch in ms */
+ bcmcli_cmd_parm *rollback_parm = bcmcli_find_named_parm(session, "rollback");
+ bcmos_bool rollback;
+ bcmos_errno rc = BCM_ERR_OK;
+ bcmolt_pon_id target_pon_id;
+
+
+ source_pon_ni = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "source_pon_ni")->value.unumber;
+ target_pon_ni = (bcmolt_pon_ni)bcmcli_find_named_parm(session, "target_pon_ni")->value.unumber;
+ onu_id = (bcmolt_pon_onu_id)bcmcli_find_named_parm(session, "onu")->value.unumber;
+ target_pon_administrative_label = (uint32_t)bcmcli_find_named_parm(session, "target_pon_id_administrative_label")->value.unumber;
+ target_pon_dwlch_id = (uint8_t)bcmcli_find_named_parm(session, "target_pon_id_dwlch_id")->value.unumber;
+ target_pon_id.administrative_label = target_pon_administrative_label;
+ target_pon_id.dwlch_id = target_pon_dwlch_id;
+ time_to_switch = (uint32_t)bcmcli_find_named_parm(session, "time_to_switch")->value.unumber;
+
+ if (rollback_parm->value.unumber == 0)
+ rollback = BCMOS_FALSE;
+ else
+ rollback = BCMOS_TRUE;
+ /* update tuning in parameters in data base */
+ onu_tuning_update_onu_db(current_device, onu_id, source_pon_ni, target_pon_ni, target_pon_id, rollback);
+
+ bcmcli_session_print(session, "Tuning out ONU=%u source_pon_ni=%u target_pon_ni=%u \n", onu_id, source_pon_ni, target_pon_ni);
+
+ /* start tuning out onu on source pon id */
+ bcmolt_xgpon_onu_key key = { .pon_ni = source_pon_ni, .onu_id = onu_id };
+ bcmolt_xgpon_onu_onu_tuning_out onu_tuning_out;
+
+ BCMOLT_OPER_INIT(&onu_tuning_out, xgpon_onu, onu_tuning_out, key);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, target_ds_pon_id, target_pon_id);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, target_us_pon_id, target_pon_id);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, time_to_switch, time_to_switch);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, rollback, BCMOS_TRUE);
+ BCMOLT_OPER_PROP_SET(&onu_tuning_out, xgpon_onu, onu_tuning_out, status, BCMOLT_STATUS_ON);
+ rc = bcmolt_oper_submit(current_device, &onu_tuning_out.hdr);
+ if (rc != BCM_ERR_OK)
+ bcmcli_session_print(session, "ONU=%u tuning out from PON=%u failed\n", onu_id, source_pon_ni);
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno bcmolt_user_appl_onu_tuning_cli_init(bcmcli_entry *top_dir)
+{
+ bcmos_errno err = BCM_ERR_OK;
+
+ /* Subscribe for device change indication */
+ if (!onu_tuning_is_registered)
+ {
+ onu_tuning_is_registered = BCMOS_TRUE;
+
+ err = bcmolt_dev_sel_ind_register(bcmolt_user_appl_onu_tuning_device_change_ind);
+ }
+ return err ? err : bcmolt_user_appl_onu_tuning_cli_create(top_dir);
+}
+
+static bcmos_errno bcmolt_user_appl_onu_tuning_cli_create(bcmcli_entry *top_dir)
+{
+ bcmcli_entry *dir;
+
+ if (bcmcli_dir_find(top_dir, "onu_tuning"))
+ {
+ return BCM_ERR_OK;
+ }
+
+ dir = bcmcli_dir_add(
+ top_dir,
+ "onu_tuning",
+ "NGPON2 ONU tuning user application",
+ BCMCLI_ACCESS_ADMIN,
+ NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "start", "Start ONU tuning application", onu_tuning_cmd_start);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "stop", "Stop ONU tuning application", onu_tuning_cmd_stop);
+
+ BCMCLI_MAKE_CMD(dir, "onu_handover", "ONU handover", onu_handover_cmd,
+ BCMCLI_MAKE_PARM("onu", "ONU ID", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("source_pon_ni", "Source PON_NI", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("target_pon_ni", "Target PON_NI", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("target_pon_id_administrative_label", "Target PON ID Administrative label", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("target_pon_id_dwlch_id", "Target PON ID Channel id", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM("time_to_switch", "Time_To_Switch", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_NONE),
+ BCMCLI_MAKE_PARM_ENUM("rollback", "Rollback", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL));
+
+
+ onu_tuning_cli_dir = dir;
+ onu_tuning_cli_top_dir = top_dir;
+
+ return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning_cli.h b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning_cli.h
new file mode 100755
index 0000000..ba9d764
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/ngpon2_onu_tuning/bcmolt_user_appl_onu_tuning_cli.h
@@ -0,0 +1,37 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_USER_APPL_ONU_TUNING_CLI_H_
+#define _BCMOLT_USER_APPL_ONU_TUNING_CLI_H_
+
+#include <bcmolt_host_api.h>
+
+bcmos_errno bcmolt_user_appl_onu_tuning_cli_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/omon/Makefile b/bcm68620_release/release/host_reference/user_appl/omon/Makefile
new file mode 100644
index 0000000..e400562
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/omon/Makefile
@@ -0,0 +1,20 @@
+# EPON optical monitoring application
+
+ifeq ("$(ENABLE_CLI)", "y")
+
+ MOD_NAME = bcm_user_appl_omon
+ MOD_TYPE = lib
+ MOD_DEPS = host_api
+ ifneq ("$(SIMULATION_BUILD)", "y")
+ MOD_DEPS += bcm_board i2c_devs
+ endif
+
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+
+ ifneq ("$(SIMULATION_BUILD)", "y")
+ srcs = omon.c
+ endif
+
+endif
diff --git a/bcm68620_release/release/host_reference/user_appl/omon/omon.c b/bcm68620_release/release/host_reference/user_appl/omon/omon.c
new file mode 100644
index 0000000..f32ff7e
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/omon/omon.c
@@ -0,0 +1,1383 @@
+/*
+<: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 <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcm_dev_log.h>
+#include <bcmolt_board.h>
+#include "omon.h"
+#include "bcmolt_utils.h"
+/* These macros need to be defined before the following include. */
+#define BCM_I2C_DEV_ADDR_START typedef enum {
+#define BCM_I2C_DEV_ADDR(name, desc, val) name = val,
+#define BCM_I2C_DEV_ADDR_END } bcm_i2c_dev_addr;
+#include "bcmolt_i2c_devs_addr.h"
+
+
+/* Fixed sample delay exceeds the sum of the maximum grant size and a worst case
+ retrieval time based on various optics modules' data sheets */
+#define OMON_FIXED_SAMPLE_DELAY_US 12000
+#define SFF_8472_IDENTIFIER_ADDR 0x00 /* SFP and XFP Identifier byte*/
+#define SFF_8077i_IDENTIFIER_ADDR 128 /* XFP Identifier byte */
+#define SFF_8472_RX_POWER_ADDR 104 /* SFP offeset into A2 */
+#define SFF_8077i_RX_POWER_ADDR 104 /* XFP byte 104/105 into A0 (yes A0) */
+#define SFF_8077i_VENDOR_NAME_ADDR 148 /* XFP bytes 163-148 into A0 */
+#define SFF_8077i_VENDOR_NAME_LEN 16 /* XFP name length */
+#define SFF_8077i_VENDOR_PN_ADDR 168 /* XFP bytes 183-168 into A0 */
+#define SFF_8077i_VENDOR_PN_LEN 16 /* XFP part number length */
+#define SFF_8077i_VENDOR_REV_ADDR 184 /* XFP bytes 185-184 into A0 */
+#define SFF_8077i_VENDOR_REV_LEN 2 /* XFP revision len */
+#define SFF_8077i_VENDOR_SN_ADDR 196 /* XFP bytes 211-196 into A0 */
+#define SFF_8077i_VENDOR_SN_LEN 16 /* XFP serial number len */
+#define OMON_TASK_MSG_Q_SIZE 16 /* allow one message per port */
+#define SFF_PAGE_ADDR1 0 /* use SFP_I2C_ADDR1 for I2C read A0 */
+#define SFF_PAGE_ADDR2 1 /* use SFP_I2C_ADDR2 for 12C read A2 */
+
+#define SFF_8472_VENDOR_NAME_ADDR 20 /* SFP bytes 20-35 into A0 */
+#define SFF_8472_VENDOR_NAME_LEN 16 /* SFP name length */
+#define SFF_8472_VENDOR_OUI_ADDR 37 /* SFP bytes 37-39 into A0 */
+#define SFF_8472_VENDOR_OUI_LEN 3 /* SFP oui length */
+#define SFF_8472_VENDOR_PN_ADDR 40 /* SFP bytes 40-55 into A0 */
+#define SFF_8472_VENDOR_PN_LEN 16 /* SFP part number length */
+#define SFF_8472_VENDOR_REV_ADDR 56 /* SFP bytes 56-59 into A0 */
+#define SFF_8472_VENDOR_REV_LEN 4 /* SFP revision len */
+#define SFF_8472_VENDOR_SN_ADDR 68 /* SFP bytes 68-83 into A0 */
+#define SFF_8472_VENDOR_SN_LEN 16 /* SFP serial number len */
+#define SFF_8472_VENDOR_SFF_ADDR 94 /* SFP bytes 94 into A0 */
+#define SFF_8472_VENDOR_SFF_LEN 1 /* SFP SFF-8472 Compliance revision len */
+
+#define SFF_8472_DIAG_MON_ADDR 92 /* SFP bytes 92 into A0 */
+#define SFF_8472_ENHANCED_OPT_ADDR 93 /* SFP bytes 93 into A0 */
+#define SFF_8472_TEMPERATURE_ADDR 96 /* SFP bytes 96 into A0 */
+#define SFF_8472_DIAG_MON_LEN 1 /* SFP SFF-8472 Diagnostic Monitoring Type len */
+
+/* Table 32 5.2 Identifier values SFF8077i*/
+#define SFF_IDENTIFIER_SFP 0x03
+#define SFF_IDENTIFIER_XFP 0x06
+
+typedef uint32_t omon_rssi_value;
+
+
+/* Uniquely identifies specific EPON links under management by this
+ application */
+typedef struct
+{
+ bcmolt_devid device_id;
+ bcmolt_epon_ni epon_ni;
+ bcmos_mac_address mac_address;
+} omon_link_key;
+
+
+typedef struct
+{
+ uint32_t caddr; /* I2C switch control address */
+ uint32_t sctrl; /* I2C switch control command value */
+} i2c_coords;
+
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ omon_link_key link_key;
+ uint16_t trigger_width_ns;
+ uint16_t trigger_delay_ns;
+ uint16_t sample_period_us;
+ bcmcli_session *session;
+ bcmolt_epon_llid llid;
+ uint8_t scan_mode;
+} omon_task_msg;
+
+
+static dev_log_id omon_log_id = DEV_LOG_INVALID_ID;
+static bcmos_task omon_task;
+static bcmos_bool is_running = BCMOS_FALSE;
+
+
+/*******************************************************************************
+ * BCM968620 platform dependent I2C access
+ ******************************************************************************/
+
+
+uint8_t bcm968620_client_switchctrl_sfp[16][2] =
+{
+ { 0x72, 0x80, },
+ { 0x72, 0x40, },
+ { 0x72, 0x20, },
+ { 0x72, 0x10, },
+ { 0x72, 0x8, },
+ { 0x72, 0x4, },
+ { 0x72, 0x2, },
+ { 0x72, 0x1, },
+ { 0x73, 0x10, },
+ { 0x73, 0x20, },
+ { 0x73, 0x40, },
+ { 0x73, 0x80, },
+ { 0x73, 0x1, },
+ { 0x73, 0x2, },
+ { 0x73, 0x4, },
+ { 0x73, 0x8, },
+};
+
+
+uint8_t bcm968620_client_switchctrl_xfp[8][2] =
+{
+ /* caddr, sctrl */
+ { 0x72, 0x8, },
+ { 0x72, 0x4, },
+ { 0x72, 0x2, },
+ { 0x72, 0x1, },
+ { 0x73, 0x10, },
+ { 0x73, 0x20, },
+ { 0x73, 0x40, },
+ { 0x73, 0x80, },
+};
+
+static bcmos_bool omon_is_epon_ni_valid(bcmolt_epon_ni epon)
+{
+ bcmolt_system_mode system_mode;
+
+ bcmolt_system_mode_get(current_device, &system_mode);
+
+ switch (system_mode)
+ {
+ case BCMOLT_SYSTEM_MODE_EPON__16_X:
+ return epon < 16;
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X:
+ return epon < 8;
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X:
+ return epon < 4;
+ case BCMOLT_SYSTEM_MODE_EPON__2_X_10_G:
+ return epon < 2;
+ default:
+ return BCMOS_FALSE;
+ }
+}
+
+static bcmos_bool trx_id_from_epon_ni(bcmolt_epon_ni epon, uint8_t *trx_id)
+{
+ static const uint8_t epon_4x[4] = { 2, 3, 6, 7 };
+ static const uint8_t epon_2x_10g[2] = { 3, 4 };
+ bcmolt_system_mode system_mode;
+
+ bcmolt_system_mode_get(current_device, &system_mode);
+
+ switch (system_mode)
+ {
+ case BCMOLT_SYSTEM_MODE_EPON__16_X:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_10_G:
+ *trx_id = (uint8_t)epon;
+ return BCMOS_TRUE;
+ case BCMOLT_SYSTEM_MODE_EPON__4_X:
+ *trx_id = epon_4x[epon];
+ return BCMOS_TRUE;
+ case BCMOLT_SYSTEM_MODE_EPON__2_X_10_G:
+ *trx_id = epon_2x_10g[epon];
+ return BCMOS_TRUE;
+ default:
+ *trx_id = (uint8_t)-1;
+ return BCMOS_FALSE;
+ }
+}
+
+static char * bcmolt_get_scan_result(bcmolt_rogue_scan_status status)
+{
+ switch (status)
+ {
+ case BCMOLT_ROGUE_SCAN_STATUS_COMPLETE:
+ return "OK: EPON ROGUE SCAN IS COMPLETE";
+ case BCMOLT_ROGUE_SCAN_STATUS_LLID_STATE_IS_BAD:
+ return "FAIL: REQUESTED LLID IS IN USE";
+ case BCMOLT_ROGUE_SCAN_STATUS_SCAN_ERR_INTERNAL:
+ return "FAIL: SCAN HAD AN INTERNAL SW ERROR";
+ case BCMOLT_ROGUE_SCAN_STATUS_SCAN_ERR_NORES:
+ return "FAIL: NO RESOURCES AVAILABLE";
+ case BCMOLT_ROGUE_SCAN_STATUS_LLID_IS_OOR:
+ return "FAIL: REQUESTED LLID IS OUT OF RANGE";
+ default:
+ return "OK: BCMOLT_ROGUE_SCAN_STATUS_COMPLETE";
+ }
+}
+
+/**
+ * \brief Get I2C address
+ *
+ * This function returns the I2C mux target address for the given link_key
+ * object.
+ *
+ * \param epon_ni EPON port index
+ * \param coords I2C mux target container
+ *
+ * \return
+ * None
+ */
+static
+void omon_get_i2c_coords(uint8_t trx_id, i2c_coords* coords)
+{
+ bcmolt_system_mode system_mode;
+
+ bcmolt_system_mode_get(0, &system_mode);
+
+ if (system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA)
+ {
+ coords->caddr = bcm968620_client_switchctrl_xfp[trx_id][0];
+ coords->sctrl = bcm968620_client_switchctrl_xfp[trx_id][1];
+ }
+ else
+ {
+ coords->caddr = bcm968620_client_switchctrl_sfp[trx_id][0];
+ coords->sctrl = bcm968620_client_switchctrl_sfp[trx_id][1];
+ }
+} /* omon_get_i2c_coords */
+
+
+/**
+ * \brief Configure I2C mux
+ *
+ * This function configures the I2C mux to target the correct optics module for
+ * the currently executing RSSI measurement.
+ *
+ * \param epon_ni EPON port index
+ *
+ * \return
+ * BCM_ERR_OK if successful, error code if not
+ */
+static
+bcmos_errno omon_platform_configure_i2c(uint8_t trx_id, uint8_t page)
+{
+
+ i2c_coords coords;
+ bcmos_errno rc;
+
+ omon_get_i2c_coords(trx_id, &coords);
+
+ rc = bcm_board_dev_change(coords.caddr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(WARNING, omon_log_id,
+ "bcm_board_dev_change(%u) failed\n", coords.caddr);
+ }
+ else
+ {
+ rc = bcm_board_switch_write(coords.sctrl);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(WARNING, omon_log_id,
+ "bcm_board_switch_write(%u) failed\n", coords.sctrl);
+ }
+ else
+ {
+ if (page == SFF_PAGE_ADDR1)
+ {
+ rc = bcm_board_dev_change(SFP_I2C_ADDR1);
+ }
+ else
+ { // SFF_PAGE_ADDR2
+ rc = bcm_board_dev_change(SFP_I2C_ADDR2);
+ }
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(WARNING, omon_log_id,
+ "bcm_board_dev_change(%u) failed\n", (SFP_I2C_ADDR1+page));
+ }
+ }
+ }
+ return rc;
+
+} /* omon_platform_configure_i2c */
+
+
+/**
+ * \brief Read Manufacturing data from Optic Module
+ *
+ * This function reads the XFP Tranceiver Data (according to
+ * SFF8077i for an XFP) from an optics module. The result is
+ * displayed for this sample code.
+ *
+ * \param epon_ni EPON NI to take measurement
+ *
+ * \return
+ * BCM_ERR_OK if successful, error code if not
+ */
+static
+bcmos_errno omon_trx_status_read(bcmolt_epon_ni epon_ni, bcmcli_session *session)
+{
+ uint32_t sff_identifier = 1;
+ bcmos_errno rc = BCM_ERR_OK;
+ uint8_t i;
+
+ // 80771 XFP Specific stuff
+ uint32_t raw_vendorId_8077i;
+ uint8_t vendorId_8077i[SFF_8077i_VENDOR_NAME_LEN+1];
+ uint32_t raw_vendorPn_8077i;
+ uint8_t vendorPn_8077i[SFF_8077i_VENDOR_PN_LEN+1];
+ uint32_t raw_vendorSn_8077i;
+ uint8_t vendorSn_8077i[SFF_8077i_VENDOR_SN_LEN+1];
+ uint32_t raw_vendorRev_8077i;
+ uint8_t vendorRev_8077i[SFF_8077i_VENDOR_REV_LEN+1];
+
+ memset(&vendorId_8077i, 0, sizeof(vendorId_8077i));
+ memset(&vendorPn_8077i, 0, sizeof(vendorPn_8077i));
+ memset(&vendorSn_8077i, 0, sizeof(vendorSn_8077i));
+ memset(&vendorRev_8077i, 0, sizeof(vendorRev_8077i));
+
+ // 8472 SFP Specific stuff
+ uint32_t raw_vendorId_8472;
+ uint8_t vendorId_8472[SFF_8472_VENDOR_NAME_LEN+1];
+ uint32_t raw_vendorPn_8472;
+ uint8_t vendorPn_8472[SFF_8472_VENDOR_PN_LEN+1];
+ uint32_t raw_vendorSn_8472;
+ uint8_t vendorSn_8472[SFF_8472_VENDOR_SN_LEN+1];
+ uint32_t raw_vendorRev_8472;
+ uint8_t vendorRev_8472[SFF_8472_VENDOR_REV_LEN+1];
+ uint32_t raw_vendorSff_8472;
+ uint8_t vendorSff_8472[SFF_8472_VENDOR_SFF_LEN+1];
+ uint32_t raw_vendorOui_8472;
+ uint8_t vendorOui_8472[SFF_8472_VENDOR_OUI_LEN+1];
+ uint32_t raw_diagMon_8472;
+ uint8_t diagMon_8472 = 0;
+ uint32_t raw_enhancedOpt_8472;
+ uint8_t enhancedOpt_8472 = 0;
+
+ memset(&vendorId_8472, 0, sizeof(vendorId_8472));
+ memset(&vendorPn_8472, 0, sizeof(vendorPn_8472));
+ memset(&vendorSn_8472, 0, sizeof(vendorSn_8472));
+ memset(&vendorRev_8472, 0, sizeof(vendorRev_8472));
+ memset(&vendorSff_8472, 0, sizeof(vendorSff_8472));
+ memset(&vendorOui_8472, 0, sizeof(vendorOui_8472));
+
+ uint8_t* tmpresult;
+ int16_t temp_result = 0;
+ uint32_t raw_temp_result = 0;
+
+ uint8_t trx_id;
+
+ if ( ! trx_id_from_epon_ni(epon_ni, &trx_id) )
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "TRX Id is invalid for EPON NI %d \n", epon_ni);
+ return BCM_ERR_RANGE;
+ }
+
+ char *pSfpType;
+
+ pSfpType = "UNKN";
+
+
+ // Check for transevier presence
+ if ( (rc = omon_platform_configure_i2c(trx_id, SFF_PAGE_ADDR1)) == BCM_ERR_OK)
+ {
+ /* Read module identifier */
+ if ( (rc = bcm_board_dev_read(8, SFF_8472_IDENTIFIER_ADDR, &sff_identifier)) == BCM_ERR_OK)
+ {
+ switch (sff_identifier & 0x000F)
+ {
+ case SFF_IDENTIFIER_SFP:
+ pSfpType = "SFP\0";
+ // Read the vendor Id
+ for (i=0; i<SFF_8472_VENDOR_NAME_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8472_VENDOR_NAME_ADDR+i, &raw_vendorId_8472);
+ vendorId_8472[i] = (uint8_t)raw_vendorId_8472;
+ }
+
+ // Read the part Number, serial number, SFF revision, and Revision
+ for (i=0; i<SFF_8472_VENDOR_PN_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8472_VENDOR_PN_ADDR+i, &raw_vendorPn_8472);
+ vendorPn_8472[i] = (uint8_t)raw_vendorPn_8472;
+ }
+
+ for (i=0; i<SFF_8472_VENDOR_SN_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8472_VENDOR_SN_ADDR+i, &raw_vendorSn_8472);
+ vendorSn_8472[i] = (uint8_t)raw_vendorSn_8472;
+ }
+
+ for (i=0; i<SFF_8472_VENDOR_REV_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8472_VENDOR_REV_ADDR+i, &raw_vendorRev_8472);
+ vendorRev_8472[i] = (uint8_t)raw_vendorRev_8472;
+ }
+
+ for (i=0; i<SFF_8472_VENDOR_SFF_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8472_VENDOR_SFF_ADDR+i, &raw_vendorSff_8472);
+ vendorSff_8472[i] = (uint8_t)raw_vendorSff_8472;
+ }
+ for (i=0; i<SFF_8472_VENDOR_OUI_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8472_VENDOR_OUI_ADDR+i, &raw_vendorOui_8472);
+ vendorOui_8472[i] = (uint8_t)raw_vendorOui_8472;
+ }
+
+ rc = rc | bcm_board_dev_read(8, SFF_8472_DIAG_MON_ADDR, &raw_diagMon_8472);
+ diagMon_8472 = (uint8_t)raw_diagMon_8472;
+
+ rc = rc | bcm_board_dev_read(8, SFF_8472_ENHANCED_OPT_ADDR, &raw_enhancedOpt_8472);
+ enhancedOpt_8472 = (uint8_t)raw_enhancedOpt_8472;
+
+ // Shift to A2
+ rc = rc | omon_platform_configure_i2c(trx_id, SFF_PAGE_ADDR2);
+ rc = rc | bcm_board_dev_read(16, SFF_8472_TEMPERATURE_ADDR, &raw_temp_result);
+
+ tmpresult = (uint8_t*)(&raw_temp_result);
+
+ temp_result = tmpresult[3];
+ temp_result = (temp_result << 8) | tmpresult[2];
+
+ break;
+
+ case SFF_IDENTIFIER_XFP:
+
+ pSfpType = "XFP\0";
+ // Read the vendor Id
+ for (i=0; i<SFF_8077i_VENDOR_NAME_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8077i_VENDOR_NAME_ADDR+i, &raw_vendorId_8077i);
+ vendorId_8077i[i] = (uint8_t)raw_vendorId_8077i;
+ }
+
+ // Read the part Number, SN, and Revision
+ for (i=0; i<SFF_8077i_VENDOR_PN_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8077i_VENDOR_PN_ADDR+i, &raw_vendorPn_8077i);
+ vendorPn_8077i[i] = (uint8_t)raw_vendorPn_8077i;
+ }
+
+ for (i=0; i<SFF_8077i_VENDOR_SN_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8077i_VENDOR_SN_ADDR+i, &raw_vendorSn_8077i);
+ vendorSn_8077i[i] = (uint8_t)raw_vendorSn_8077i;
+ }
+
+ for (i=0; i<SFF_8077i_VENDOR_REV_LEN; i++)
+ {
+ rc = rc | bcm_board_dev_read(8, SFF_8077i_VENDOR_REV_ADDR+i, &raw_vendorRev_8077i);
+ vendorRev_8077i[i] = (uint8_t)raw_vendorRev_8077i;
+ }
+
+ break;
+ default:
+ rc = BCM_ERR_NODEV;
+ break;
+ }
+ }
+ else
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "TRX Could not read device : SFF_8472_IDENTIFIER_ADDR for PON %d rc %d (%s)\n",
+ epon_ni, rc, bcmos_strerror(rc));
+ }
+ }
+ else
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "TRX Could not configure platform : SFF_PAGE_ADDR1 for PON %d rc %d (%s)\n",
+ epon_ni, rc, bcmos_strerror(rc));
+ rc = BCM_ERR_NODEV;
+ }
+
+
+ if (rc == BCM_ERR_OK)
+ {
+ if ( (sff_identifier & 0x000F) == SFF_IDENTIFIER_XFP)
+ {
+ bcmcli_session_print(session,
+ "\n[OMON] %s Tranceiver is present on PON %d\r\n"
+ " [OMON] Trx Vendor Id is: %s\r\n "
+ " [OMON] Vendor Part Number: %s\r\n "
+ " [OMON] Vendor Serial Number: %s\r\n "
+ " [OMON] Vendor Revision: %s\r\n ",
+ pSfpType, epon_ni, vendorId_8077i,
+ vendorPn_8077i, vendorSn_8077i, vendorRev_8077i);
+ }
+ else
+ {
+ // SFP stuff
+ bcmcli_session_print(session,
+ "\n[OMON] %s Tranceiver is present on PON %d\r\n"
+ " [OMON] Trx Vendor Id is: %s\r\n "
+ " [OMON] Vendor Part Number: %s\r\n "
+ " [OMON] Vendor Serial Number: %s\r\n "
+ " [OMON] Vendor Revision: %s\r\n "
+ " [OMON] Vendor OUI: %s\r\n "
+ " [OMON] Transceiver Temperature: %dC\r\n "
+ " [OMON] Enhanced Options SFF-8472 Byte 93: %x\r\n "
+ " [OMON] Diagnostic Monitor Type SFF-8472 Byte 92: %x\r\n ",
+ pSfpType, epon_ni, vendorId_8472,
+ vendorPn_8472, vendorSn_8472, vendorRev_8472, vendorOui_8472,
+ temp_result/256, enhancedOpt_8472, diagMon_8472 );
+ }
+ }
+
+ return rc;
+} /* omon_rssi_read */
+
+/**
+ * \brief Read RSSI results from I2C
+ *
+ * This function reads the RX Power Measurement (SFF8472 for an
+ * SFP and SFF8077i for an XFP) from an optics module. The
+ * result is stored in the global omon_state.
+ *
+ * \param epon_ni EPON NI to take measurement
+ * \param rssi_value Returned RSSI value if successful
+ *
+ * \return
+ * BCM_ERR_OK if successful, error code if not
+ */
+static
+bcmos_errno omon_rssi_read(bcmolt_epon_ni epon_ni, uint32_t *rssi_value)
+{
+ uint32_t raw_rssi_result = 0;
+ uint32_t sff_identifier = 1;
+ uint8_t trx_id;
+ uint32_t rssi_result = 0;
+ bcmos_errno rc = BCM_ERR_OK;
+ uint8_t* result;
+
+ if ( ! trx_id_from_epon_ni(epon_ni, &trx_id) )
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "TRX Id is invalid for EPON NI %d \n", epon_ni);
+ return BCM_ERR_RANGE;
+ }
+
+ // Check for transevier presence
+ if (bcm_board_trx_present(trx_id) == BCM_ERR_OK)
+ {
+ rc = omon_platform_configure_i2c(trx_id, SFF_PAGE_ADDR1);
+ /* Read module identifier */
+
+ if (rc == BCM_ERR_OK)
+ {
+ /* Read module identifier */
+ rc = bcm_board_dev_read(8, SFF_8472_IDENTIFIER_ADDR, &sff_identifier);
+
+ if (rc == BCM_ERR_OK)
+ {
+ result = (uint8_t*)(&raw_rssi_result);
+
+ switch (sff_identifier & 0x000F)
+ {
+ case SFF_IDENTIFIER_SFP:
+ rc = omon_platform_configure_i2c(trx_id, SFF_PAGE_ADDR2);
+ if (rc == BCM_ERR_OK)
+ {
+ rc = bcm_board_dev_read(16, SFF_8472_RX_POWER_ADDR, &raw_rssi_result);
+ }
+ break;
+ case SFF_IDENTIFIER_XFP:
+
+ rc = bcm_board_dev_read(16, SFF_8077i_RX_POWER_ADDR, &raw_rssi_result);
+
+ break;
+ default:
+ rc = BCM_ERR_NODEV;
+ break;
+ }
+
+ if (rc == BCM_ERR_OK)
+ {
+ rssi_result = result[3];
+ rssi_result = (rssi_result << 8) | result[2];
+
+ *rssi_value = rssi_result;
+
+ }
+ }
+ else
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "TRX Could not read device : SFF_8472_IDENTIFIER_ADDR for PON %d rc %d (%s)\n",
+ epon_ni, rc, bcmos_strerror(rc));
+ rc = BCM_ERR_IO;
+ }
+ }
+ else
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "TRX Could not read platform : SFF_PAGE_ADDR1 for PON %d rc %d (%s)\n",
+ epon_ni, rc, bcmos_strerror(rc));
+ rc = BCM_ERR_IO;
+ }
+ }
+ else
+ {
+ rc = BCM_ERR_NODEV;
+ }
+
+ return rc;
+} /* omon_rssi_read */
+
+
+/*******************************************************************************
+ * Platform independent RSSI measurement
+ ******************************************************************************/
+
+/**
+ * \brief Read XFP/SFP Data from Transceiver Module1
+ *
+ * This function reads the manufacturing data from the SFP
+ * module via I2C.
+ *
+ * \param link_key EPON NI
+ *
+ * \return
+ * None
+ */
+static
+void omon_trx_status_initiate(bcmcli_session *session,
+ omon_link_key *link_key)
+{
+
+ bcmos_errno rc;
+
+ /* Perform the I2C operations specified in SFF-8472 (SFP)/SFF-8077i (XFP) to
+ retrieve the pmanufacturer data from the optics module. */
+
+ if (! omon_is_epon_ni_valid(link_key->epon_ni))
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "[OMON] TRX Status: epon_ni of (%d) is out of range for platform\n", link_key->epon_ni);
+ return ;
+ }
+ // Check for transceiver presence
+
+ uint8_t trx_id;
+
+ if ( ! trx_id_from_epon_ni(link_key->epon_ni, &trx_id) )
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "[OMON] TRX Id is invalid for EPON NI %d \n", link_key->epon_ni);
+ return;
+ }
+
+ rc = bcm_board_trx_present(trx_id);
+
+ if (rc == BCM_ERR_NODEV)
+ {
+ bcmcli_session_print(session,
+ "[OMON] TRX Status: Trx_id %d Not Present on PON %d\r\n ", trx_id, link_key->epon_ni);
+ return;
+ }
+
+ rc = omon_trx_status_read(trx_id, session);
+
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "[OMON] Issue TRX Status: Read operation failed with rc %d (%s)\n",
+ rc, bcmos_strerror(rc));
+ return;
+ }
+
+ /* Notify external host code of the outcome through an optional callback,
+ defaulting to output in the CLI session where the command was initiated
+ if no callback is supplied. */
+
+ bcmcli_session_print(session,
+ "\n[OMON] TRX Status: Data on PON %d Complete\r\n", link_key->epon_ni);
+}
+/**
+ * \brief Read RSSI Measurement Result from Transceiver Module
+ *
+ * This function reads the stored RSSI measurement from the
+ * XFP/SFP module via I2C.
+ *
+ * \param link_key EPON NI
+ *
+ * \return
+ * None
+ */
+static
+void omon_rssi_read_initiate(bcmcli_session *session,
+ omon_link_key *link_key)
+{
+ uint32_t rssi_value = 0;
+ bcmos_errno rc;
+
+ if (! omon_is_epon_ni_valid(link_key->epon_ni))
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "[OMON] Rssi Read: epon_ni of (%d) is out of range for platform\n", link_key->epon_ni);
+ return ;
+ }
+
+ /* Perform the I2C operations specified in SFF-8472 (SFP)/SFF-8077i (XFP) to
+ retrieve the power measurements from the optics module. */
+ rc = omon_rssi_read(link_key->epon_ni, &rssi_value);
+
+ if (rc == BCM_ERR_NODEV)
+ {
+ bcmcli_session_print(session,
+ "[OMON] Rssi Read: TRX Not Present on PON %d\r\n ", link_key->epon_ni);
+ return;
+ }
+
+ if (rc == BCM_ERR_IO)
+ {
+ bcmcli_session_print(session,
+ "[OMON] Rssi Read: Could not read device for PON %d \n",link_key->epon_ni);
+ return;
+ }
+
+ if (rc != BCM_ERR_OK)
+ {
+ bcmcli_session_print(session,
+ "Issue RSSI Read operation failed with rc %d (%s)\n",
+ rc, bcmos_strerror(rc));
+ return;
+ }
+ /* Notify external host code of the outcome through an optional callback,
+ defaulting to output in the CLI session where the command was initiated
+ if no callback is supplied. */
+
+ bcmcli_session_print(session,
+ "[OMON] RSSI Read RX Power: Pon_ni=%d rx power %d.%duW\r\n",
+ link_key->epon_ni,
+ rssi_value/10,
+ rssi_value%10);
+
+ bcmcli_session_print(session,
+ "\n[OMON] RSSI Read on PON %d Complete\r\n",
+ link_key->epon_ni);
+}
+/*omon_rssi_read_initiate*/
+
+
+/**
+ * \brief Request Maple Firmware to run the Rogue ONU LLID scan.
+ *
+ * This function sends an API message to the OLT to run a scan
+ * on a specific EPON LLID or across all LLIDs in the PON.
+ * Results are returned upon completion. Rogue LLIDs are marked
+ * and quarentined for the host to examine.
+ *
+ * \param link_key EPON link information
+ * \param epopn_llid Specific LLID to scan
+ * \param scan_mode Specifies a single LLID or All.
+ *
+ * \return
+ * None
+ */
+
+static
+void omon_run_rogue_llid_scan(bcmcli_session *session,
+ omon_link_key *link_key,
+ bcmolt_epon_llid llid,
+ uint8_t scan_mode)
+{
+ const bcmolt_epon_ni_key ni_key = { .epon_ni = link_key->epon_ni };
+ bcmolt_epon_ni_rogue_llid_scan issue_llid_scan_op;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Perform an API operation directing the Maple firmware to issue an bcmolt_epon_ni_issue_rogue_rx_power
+ RSSI grant to the specified EPON link. BCMOLT_EPON_NI_OPER_ID_ISSUE_ROGUE_RX_POWER*/
+
+ BCMOLT_OPER_INIT(&issue_llid_scan_op, epon_ni, rogue_llid_scan , ni_key);
+ BCMOLT_OPER_PROP_SET(&issue_llid_scan_op, epon_ni, rogue_llid_scan, mode, scan_mode);
+ BCMOLT_OPER_PROP_SET(&issue_llid_scan_op, epon_ni, rogue_llid_scan, llid, llid);
+
+ BCM_LOG_CALLER_FMT(INFO, omon_log_id,
+ "Issue Rogue Scan Operation for LLID 0x%x, Pon_ni=%d\r\n", llid, link_key->epon_ni);
+
+ rc = bcmolt_oper_submit(link_key->device_id, &issue_llid_scan_op.hdr);
+
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "Rogue Scan Operation Failed with rc %d (%s)\n",
+ rc, bcmos_strerror(rc));
+
+ return;
+ }
+}/*omon_run_rogue_llid_scan*/
+
+/**
+ * \brief Execute RSSI measurement
+ *
+ * This function sends an API message to the OLT to issue an RSSI grant. It
+ * then reads back the results via I2C. Set grant_length_tq to 0 to retreive the
+ * RSSI value without issuing the strobe command to the OLT.
+ *
+ * \param link_key EPON RSSI link information
+ * \param grant_length_tq Size of the RSSI grant
+ * \param start_offset Strobe offset from start of the RSSI grant
+ * \param end_offset Strobe offset from the end of the RSSI grant
+ *
+ * \return
+ * None
+ */
+static
+void omon_rssi_measurement_initiate(bcmcli_session *session,
+ omon_link_key *link_key,
+ uint16_t trigger_width_tq,
+ uint16_t trigger_delay_tq,
+ uint16_t sample_period)
+{
+ const bcmolt_epon_ni_key ni_key = { .epon_ni = link_key->epon_ni };
+ bcmolt_epon_ni_issue_rssi_grant issue_rssi_grant_op;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ if (! omon_is_epon_ni_valid(link_key->epon_ni))
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "[OMON] Rx Power: epon_ni of (%d) is out of range for platform\n", link_key->epon_ni);
+ return ;
+ }
+
+ uint8_t trx_id;
+
+ if ( ! trx_id_from_epon_ni(link_key->epon_ni, &trx_id) )
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "[OMON] Rx Power: TRX Id is invalid for EPON NI %d \n", link_key->epon_ni);
+ return;
+ }
+
+ // Check for transevier presence
+ rc = bcm_board_trx_present(trx_id);
+
+ if (rc == BCM_ERR_NODEV)
+ {
+ bcmcli_session_print(session,
+ "[OMON] Rx Power: TRX Not Present on PON %d\r\n ", link_key->epon_ni);
+ return;
+ }
+
+ /* Perform an API operation directing the Maple firmware to issue an bcmolt_epon_ni_issue_rogue_rx_power
+ RSSI grant to the specified EPON link. BCMOLT_EPON_NI_OPER_ID_ISSUE_ROGUE_RX_POWER*/
+
+ BCMOLT_OPER_INIT(&issue_rssi_grant_op, epon_ni, issue_rssi_grant , ni_key);
+ BCMOLT_OPER_PROP_SET(&issue_rssi_grant_op, epon_ni, issue_rssi_grant,
+ granted_link, link_key->mac_address);
+ BCMOLT_OPER_PROP_SET(&issue_rssi_grant_op, epon_ni, issue_rssi_grant,
+ trigger_width, trigger_width_tq);
+ BCMOLT_OPER_PROP_SET(&issue_rssi_grant_op, epon_ni, issue_rssi_grant,
+ trigger_delay, trigger_delay_tq);
+ BCMOLT_OPER_PROP_SET(&issue_rssi_grant_op, epon_ni, issue_rssi_grant,
+ sample_period, sample_period);
+
+ rc = bcmolt_oper_submit(link_key->device_id, &issue_rssi_grant_op.hdr);
+ if (rc != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, omon_log_id,
+ "issue RSSI grant operation failed with rc %d (%s)\n",
+ rc, bcmos_strerror(rc));
+
+ return;
+ }
+}
+/*omon_rssi_measurement_initiate*/
+
+// rssi specific indication handler interface --
+static bcmos_errno bcmolt_user_appl_rssi_handle_ind(bcmolt_devid device_id,
+ uint8_t instance,
+ bcmolt_auto *ind)
+{
+
+ bcmolt_epon_ni_rssi_measurement_completed* omon_ind;
+ uint32_t rssi_value = 0;
+ bcmolt_epon_ni epon_ni;
+ bcmos_errno rc = BCM_ERR_OK;
+ char tBuf[40];
+
+ omon_ind = (bcmolt_epon_ni_rssi_measurement_completed *) ind;
+ epon_ni = omon_ind->key.epon_ni;
+
+ if (omon_ind->data.status == BCMOLT_RESULT_SUCCESS)
+ {
+ /* Perform the I2C operations specified in SFF-8472 (SFP)/SFF-8077i (XFP) to
+ retrieve the power measurements from the optics module. */
+ rc = omon_rssi_read(epon_ni, &rssi_value);
+
+ }
+ /* Notify external host code of the outcome through an optional callback,
+ defaulting to output in the CLI session where the command was initiated
+ if no callback is supplied. */
+
+ BCM_LOG_CALLER_FMT(INFO, omon_log_id,
+ " RSSI Indication Rcv: for Link Mac %s, LLID 0x%x, Dev=%01d, Pon_ni=%d\r\n",
+ bcmos_mac_2_str(&omon_ind->data.link_mac, tBuf),
+ omon_ind->data.llid,
+ device_id,
+ omon_ind->key.epon_ni);
+
+ if (rc == BCM_ERR_OK)
+ {
+ BCM_LOG(INFO, omon_log_id,
+ " %s measured rx power %d.%duW raw_data %d\n",
+ (omon_ind->data.status == BCMOLT_RESULT_SUCCESS ? "Succeeded" : "Failed"),
+ rssi_value/10, rssi_value%10, rssi_value);
+ }
+ else
+ {
+ BCM_LOG(INFO, omon_log_id,
+ "pon %d, failed with errno %d (%s)\n",
+ epon_ni, rc, bcmos_strerror(rc));
+ }
+ return rc;
+}
+
+// epon rogue onu specific indication handler interface --
+static bcmos_errno bcmolt_user_appl_rogue_handle_ind(bcmolt_devid device_id,
+ uint8_t instance,
+ bcmolt_auto *ind)
+{
+ (void)instance;
+ bcmolt_epon_denied_link_rogue_violation* rogue_ind;
+ char tBuf[40];
+
+ rogue_ind = (bcmolt_epon_denied_link_rogue_violation *) ind;
+
+ /* Notify external host code of the outcome through an optional callback,
+ defaulting to output in the CLI session where the command was initiated
+ if no callback is supplied. */
+
+ BCM_LOG_CALLER_FMT(INFO, omon_log_id,
+ "Rogue ONU Scan Violation: AlmState = %s, Link Mac %s, LLID 0x%x, Range=%0d, Pon_ni=%2d\r\n",
+ (rogue_ind->data.alarm_status.alarm_status == BCMOLT_STATUS_ON ? "ON" : "OFF"),
+ bcmos_mac_2_str(&rogue_ind->key.mac_address, tBuf),
+ rogue_ind->data.alarm_status.denied_llid,
+ rogue_ind->data.alarm_status.denied_range,
+ rogue_ind->key.epon_ni);
+
+ return BCM_ERR_OK;
+}
+
+// epon rogue scan complete specific indication handler interface --
+static bcmos_errno bcmolt_user_appl_rogue_complete_handle_ind(bcmolt_devid device_id,
+ uint8_t instance,
+ bcmolt_auto *ind)
+{
+ (void)instance;
+ bcmolt_epon_ni_rogue_scan_complete* rogue_ind;
+
+ rogue_ind = (bcmolt_epon_ni_rogue_scan_complete *) ind;
+
+ /* Notify external host code of the outcome through an optional callback,
+ defaulting to output in the CLI session where the command was initiated
+ if no callback is supplied. */
+
+ BCM_LOG_CALLER_FMT(INFO, omon_log_id,
+ "Rogue ONU scan complete for Pon %d, Status = %s\r\n",
+ rogue_ind->key.epon_ni,
+ bcmolt_get_scan_result(rogue_ind->data.return_ind_status));
+ return BCM_ERR_OK;
+}
+
+// public indication handler interface -- called in transport layer context
+bcmos_errno bcmolt_user_appl_omon_handle_ind(bcmolt_devid device_id, uint8_t instance, bcmolt_auto *ind)
+{
+
+ // Not an error, we just don't care about this indication.
+ bcmos_errno rc = BCM_ERR_OK;
+
+ // We look at message targetting epon ni.
+ if (ind->hdr.obj_type == BCMOLT_OBJ_ID_EPON_NI)
+ {
+ // We look at RSSI Completion event indications.
+ if (ind->hdr.subgroup == (uint16_t)BCMOLT_EPON_NI_AUTO_ID_RSSI_MEASUREMENT_COMPLETED)
+ {
+ rc = bcmolt_user_appl_rssi_handle_ind(device_id,instance,ind);
+ }
+ else
+ {
+ // We look for Rogue Scan Complete event indications.
+ if (ind->hdr.subgroup == (uint16_t)BCMOLT_EPON_NI_AUTO_ID_ROGUE_SCAN_COMPLETE)
+ {
+ rc = bcmolt_user_appl_rogue_complete_handle_ind(device_id, instance, ind);
+ }
+ }
+
+ if (rc != BCM_ERR_OK )
+ {
+ BCM_LOG_CALLER_FMT(INFO, omon_log_id,
+ "OMON Handle Indication Error: Pon %d, Type %d, Rc %d\r\n",
+ instance, ind->hdr.subgroup, rc);
+ }
+
+ // Not an error, we just don't care about this indication.
+ return BCM_ERR_OK;
+ }
+
+ // We look at message targetting epon denied links.
+ if (ind->hdr.obj_type == BCMOLT_OBJ_ID_EPON_DENIED_LINK)
+ {
+ // We look at Denied Link Events of this type.
+ if (ind->hdr.subgroup == BCMOLT_EPON_DENIED_LINK_AUTO_ID_ROGUE_VIOLATION)
+ {
+ rc = bcmolt_user_appl_rogue_handle_ind(device_id, instance, ind);
+ }
+ else
+ {
+ // Not an error, we just don't care about this object.
+ return BCM_ERR_OK;
+ }
+ }
+ return rc;
+
+} /* bcmolt_user_appl_omon_handle_ind */
+
+/**
+ * \brief Optical monitoring task message handler
+ *
+ * This function is the optical monitoring task handler. At this time the only
+ * message is BCMOS_MSG_ID_INITIATE_RSSI_SAMPLE which starts the RSSI
+ * measurement on the OLT.
+ *
+ * \param module_id BCMOS_MODULE_ID_USER_APPL_OMON
+ * \param os_msg Message contents
+ */
+static
+void omon_os_msg_handle(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ omon_task_msg *msg = (omon_task_msg *)os_msg;
+
+ switch (msg->os_msg.type)
+ {
+ case BCMOS_MSG_ID_INITIATE_RX_POWER:
+ omon_rssi_measurement_initiate(msg->session,
+ &msg->link_key,
+ msg->trigger_width_ns ,
+ msg->trigger_delay_ns,
+ msg->sample_period_us);
+ break;
+ case BCMOS_MSG_ID_INITIATE_TRX_STATUS:
+
+ omon_trx_status_initiate(msg->session,
+ &msg->link_key);
+ break;
+ case BCMOS_MSG_ID_INITIATE_ROGUE_SCAN:
+ omon_run_rogue_llid_scan(msg->session,
+ &msg->link_key,
+ msg->llid,
+ msg->scan_mode);
+
+ break;
+ case BCMOS_MSG_ID_INITIATE_RSSI_READ:
+ omon_rssi_read_initiate(msg->session,
+ &msg->link_key);
+ break;
+ default:
+ break;
+ }
+ bcmos_free(os_msg);
+} /* omon_os_msg_handle */
+
+
+/**
+ * \brief Start the optical monitoring task
+ *
+ * This function starts the user level optical monitoring task and creates the
+ * optical monitoring module. It should only be called once at start up.
+ *
+ * \return
+ * None
+ */
+void bcmolt_epon_omon_appl_init(void)
+{
+ bcmos_errno rc;
+ bcmos_task_parm task_params =
+ {
+ .name = "user_appl_omon",
+ .priority = TASK_PRIORITY_USER_APPL_OMON,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+ bcmos_module_parm module_params =
+ {
+ .qparm =
+ {
+ .name = "user_appl_omon",
+ .size = OMON_TASK_MSG_Q_SIZE
+ }
+ };
+
+ if (is_running)
+ {
+ return;
+ }
+
+ omon_log_id = bcm_dev_log_id_register("user_appl_omon",
+ DEV_LOG_LEVEL_INFO,
+ DEV_LOG_ID_TYPE_BOTH);
+ BUG_ON(DEV_LOG_INVALID_ID == omon_log_id);
+
+ rc = bcmos_task_create(&omon_task, &task_params);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ rc = bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_OMON, &omon_task,
+ &module_params);
+ BUG_ON(rc != BCM_ERR_OK);
+
+ is_running = BCMOS_TRUE;
+} /* bcmolt_epon_omon_appl_init */
+
+
+/*******************************************************************************
+ * CLI
+ ******************************************************************************/
+
+
+/**
+ * \brief RX power sample CLI command
+ *
+ * This function handles the "sample" CLI command. It processes the CLI
+ * parameters and dispatches a start sample message to the optical monitoring
+ * task.
+ *
+ * \param session CLI session ID
+ * \param parm Command parameters
+ * \param n_parms Number of parameters
+ *
+ * \return
+ * BCM_ERR_OK if successful, error code if not
+ */
+static
+bcmos_errno omon_cmd_rx_power(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ omon_link_key link_key;
+ bcmos_errno rc;
+ bcmcli_cmd_parm *epon_ni;
+ bcmcli_cmd_parm *mac_addr;
+ bcmcli_cmd_parm *Twidth_ns;
+ bcmcli_cmd_parm *Tdelay_ns;
+ bcmcli_cmd_parm *Tsample_us;
+ omon_task_msg *msg;
+
+ /* Get the CLI parameters. */
+ epon_ni = bcmcli_find_named_parm(session, "epon_ni");
+ mac_addr = bcmcli_find_named_parm(session, "granted_link");
+ Twidth_ns = bcmcli_find_named_parm(session, "trigger_width"); // RSSI Twidth in ns
+ Tdelay_ns = bcmcli_find_named_parm(session, "trigger_delay"); // RSSI Tdelay in ns
+ Tsample_us = bcmcli_find_named_parm(session, "sample_period"); // RSSI Tsample in us
+
+ link_key.device_id = current_device;
+ link_key.epon_ni = (bcmolt_epon_ni)epon_ni->value.number;
+ link_key.mac_address = (bcmos_mac_address)mac_addr->value.mac;
+
+ /* Send an indication to the internal task's message queue to be
+ processed. */
+ msg = bcmos_calloc(sizeof(*msg));
+ BUG_ON(msg == NULL);
+ msg->os_msg.type = BCMOS_MSG_ID_INITIATE_RX_POWER;
+ msg->os_msg.handler = omon_os_msg_handle;
+ msg->link_key = link_key;
+ msg->trigger_width_ns = Twidth_ns->value.number;
+ msg->trigger_delay_ns = Tdelay_ns->value.number;
+ msg->sample_period_us = Tsample_us->value.number;
+ msg->session = session;
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_OMON,
+ &msg->os_msg, 0);
+ BUG_ON(rc);
+
+
+ return rc;
+} /* omon_cmd_sample */
+
+static
+bcmos_errno omon_cmd_trx_data(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ omon_link_key link_key;
+ bcmos_errno rc;
+ bcmcli_cmd_parm *epon_ni;
+ omon_task_msg *msg;
+
+ /* Get the CLI parameters. */
+ epon_ni = bcmcli_find_named_parm(session, "epon_ni");
+
+ link_key.device_id = current_device;
+ link_key.epon_ni = (bcmolt_epon_ni)epon_ni->value.number;
+
+ /* Send an indication to the internal task's message queue to be
+ processed. */
+ msg = bcmos_calloc(sizeof(*msg));
+ BUG_ON(msg == NULL);
+ msg->os_msg.type = BCMOS_MSG_ID_INITIATE_TRX_STATUS;
+ msg->os_msg.handler = omon_os_msg_handle;
+ msg->link_key = link_key;
+ msg->session = session;
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_OMON,
+ &msg->os_msg, 0);
+ BUG_ON(rc);
+
+
+ return rc;
+}
+/* omon_cmd_trx_data */
+
+static
+bcmos_errno omon_cmd_read_rssi_result(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ omon_link_key link_key;
+ bcmos_errno rc;
+ bcmcli_cmd_parm *epon_ni;
+ omon_task_msg *msg;
+
+ /* Get the CLI parameters. */
+ epon_ni = bcmcli_find_named_parm(session, "epon_ni");
+
+ link_key.device_id = current_device;
+ link_key.epon_ni = (bcmolt_epon_ni)epon_ni->value.number;
+
+ /* Send an indication to the internal task's message queue to be
+ processed. */
+ msg = bcmos_calloc(sizeof(*msg));
+ BUG_ON(msg == NULL);
+ msg->os_msg.type = BCMOS_MSG_ID_INITIATE_RSSI_READ;
+ msg->os_msg.handler = omon_os_msg_handle;
+ msg->link_key = link_key;
+ msg->session = session;
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_OMON,
+ &msg->os_msg, 0);
+ BUG_ON(rc);
+
+
+ return rc;
+}
+/* omon_cmd_read_rssi_result */
+
+static
+bcmos_errno omon_cmd_llid_scan(bcmcli_session *session,
+ const bcmcli_cmd_parm parm[],
+ uint16_t n_parms)
+{
+ omon_link_key link_key;
+ bcmos_errno rc;
+ bcmcli_cmd_parm *epon_ni;
+ bcmcli_cmd_parm *epon_llid;
+ bcmcli_cmd_parm *epon_mode;
+ omon_task_msg *msg;
+
+ /* Get the CLI parameters. */
+ epon_ni = bcmcli_find_named_parm(session, "epon_ni");
+ epon_mode = bcmcli_find_named_parm(session, "scan_mode");
+ epon_llid = bcmcli_find_named_parm(session, "epon_llid");
+
+ link_key.device_id = current_device;
+ link_key.epon_ni = (bcmolt_epon_ni)epon_ni->value.number;
+
+ /* Send an indication to the internal task's message queue to be
+ processed. */
+ msg = bcmos_calloc(sizeof(*msg));
+ BUG_ON(msg == NULL);
+ msg->os_msg.type = BCMOS_MSG_ID_INITIATE_ROGUE_SCAN;
+ msg->os_msg.handler = omon_os_msg_handle;
+ msg->link_key = link_key;
+ msg->session = session;
+ msg->llid = epon_llid->value.number;
+ msg->scan_mode = epon_mode->value.number;
+ rc = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_OMON,
+ &msg->os_msg, 0);
+ BUG_ON(rc);
+
+
+ return rc;
+}
+
+
+/**
+ * \brief Install CLI commands
+ *
+ * This function creates and optical monitoring command directoy "omon" and
+ * installs optical monitoring CLI commands to this directory.
+ *
+ * \param top_dir Parent of "omon" directory
+ *
+ * \return
+ * BCM_ERR_OK if successful, error code if not
+ */
+void bcmolt_user_appl_epon_omon_cli_init(bcmcli_entry *top_dir)
+{
+ static const char *dir_name = "omon";
+
+ if (bcmcli_dir_find(top_dir, dir_name))
+ {
+ return;
+ }
+
+ bcmcli_entry *dir = bcmcli_dir_add(top_dir,
+ dir_name,
+ "EPON optical monitoring commands",
+ BCMCLI_ACCESS_ADMIN, NULL);
+ BUG_ON(dir == NULL);
+
+ BCMCLI_MAKE_CMD(dir, "rx_power", "Issue RX Power Measurement", omon_cmd_rx_power,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("granted_link", "Link mac address, 000000000000 for Idle Power", BCMCLI_PARM_MAC, 0),
+ BCMCLI_MAKE_PARM("trigger_width",
+ "RSSI Trigger Width (Tw) in ns, Desired width of RSSI Trigger based on the Optical Module Specifications."
+ "This is a mandatory parameter used for all RX Power measurements ( including Idle power) and must not be 0."
+ "Note: The granularity of the device is 1 TQ (16ns) the input will be rounded up to the next TQ."
+ , BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("trigger_delay",
+ "RSSI Trigger Delay (Td) in ns, Desired delay to meet the trigger delay timing requirement of the Optical Module Specifications."
+ "The rssi trigger delay is measured from the start of sync_time and is adjusted as needed. "
+ "The trigger delay moves the assertion of the RSSI Trigger strobe into the grant window."
+ "Note: The granularity of the device is 1 TQ (16ns) the input will be rounded up to the next TQ."
+ , BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("sample_period",
+ "(Ti2c) Sample period in uS, internal I2C hold/prohibit time where access to device is not allowed."
+ "During this period the internal RSSI data is invalid and I2C opertions on this device must not be executed."
+ "A value of 500uS is recommended for most applications. "
+ , BCMCLI_PARM_NUMBER, 0));
+
+ BCMCLI_MAKE_CMD(dir, "trx_data", "Read Tranceiver Data", omon_cmd_trx_data,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, 0));
+
+ BCMCLI_MAKE_CMD(dir, "read_rssi", "Read RSSI Result from Tranceiver", omon_cmd_read_rssi_result,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, 0));
+
+ BCMCLI_MAKE_CMD(dir, "llid_scan", "Run Rogue ONU LLID Scan", omon_cmd_llid_scan,
+ BCMCLI_MAKE_PARM("epon_ni", "EPON NI", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("scan_mode", "LLID scan mode 1 for full pon scan, 0 for targeted (llid required for targeted)", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("epon_llid", "EPON llid to scan when mode=0", BCMCLI_PARM_HEX, 0));
+}
+/* bcmolt_user_appl_epon_omon_cli_init */
+
+
+/* End of file omon.c */
+
diff --git a/bcm68620_release/release/host_reference/user_appl/omon/omon.h b/bcm68620_release/release/host_reference/user_appl/omon/omon.h
new file mode 100644
index 0000000..0e31b7a
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/omon/omon.h
@@ -0,0 +1,69 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _OMON_H_
+#define _OMON_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcmolt_utils.h>
+#include <bcm_dev_log.h>
+
+
+
+/**
+ * \brief Start the optical monitoring task
+ *
+ * This function starts the user level optical monitoring task and creates the
+ * optical monitoring module. It should only be called once at start up.
+ *
+ * \return
+ * None
+ */
+extern
+void bcmolt_epon_omon_appl_init (void);
+
+
+/**
+ * \brief Install CLI commands
+ *
+ * This function creates and optical monitoring command directoy "omon" and
+ * installs optical monitoring CLI commands to this directory.
+ *
+ * \param top_dir Parent of "omon" directory
+ */
+
+void bcmolt_user_appl_epon_omon_cli_init(bcmcli_entry *top_dir);
+
+
+bcmos_errno bcmolt_user_appl_omon_handle_ind(bcmolt_devid device_id, uint8_t instance, bcmolt_auto *ind);
+
+
+#endif /* End of file omon.h */
diff --git a/bcm68620_release/release/host_reference/user_appl/playback/Makefile b/bcm68620_release/release/host_reference/user_appl/playback/Makefile
new file mode 100644
index 0000000..0374028
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/playback/Makefile
@@ -0,0 +1,12 @@
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl_playback
+ MOD_TYPE = lib
+ MOD_DEPS = utils dev_log common_api transport
+ srcs = bcmolt_user_appl_playback.c
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+
+ USE_LINT=yes
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/playback/bcmolt_user_appl_playback.c b/bcm68620_release/release/host_reference/user_appl/playback/bcmolt_user_appl_playback.c
new file mode 100644
index 0000000..a3b463b
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/playback/bcmolt_user_appl_playback.c
@@ -0,0 +1,1573 @@
+/*
+ <: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);
+ }
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/playback/bcmolt_user_appl_playback.h b/bcm68620_release/release/host_reference/user_appl/playback/bcmolt_user_appl_playback.h
new file mode 100644
index 0000000..cb37c61
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/playback/bcmolt_user_appl_playback.h
@@ -0,0 +1,40 @@
+/*
+<: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.
+
+:>
+ */
+
+#ifndef _BCMOLT_USER_APPL_PLAYBACK_H_
+#define _BCMOLT_USER_APPL_PLAYBACK_H_
+
+#include "bcmcli.h"
+
+void bcmolt_user_appl_playback_cli_init(bcmcli_entry *top_dir);
+
+void bcmolt_user_appl_playback_init(void);
+
+#endif /* _BCMOLT_USER_APPL_PLAYBACK_H_ */
+
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/Makefile b/bcm68620_release/release/host_reference/user_appl/protection_switching/Makefile
new file mode 100644
index 0000000..dff2e55
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/Makefile
@@ -0,0 +1,17 @@
+# protection switching user application
+
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl_ps
+ MOD_TYPE = lib
+ MOD_DEPS = host_api bcm_board
+ srcs = bcmolt_user_appl_ps.c \
+ bcmolt_user_appl_ps_cli.c \
+ bcmolt_user_appl_ps_gpon.c \
+ bcmolt_user_appl_ps_epon.c \
+ bcmolt_user_appl_ps_xgpon.c
+
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps.c b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps.c
new file mode 100644
index 0000000..3fc8d31
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps.c
@@ -0,0 +1,756 @@
+/*
+<: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 <bcmolt_api.h>
+#include <bcmolt_msg_pack.h>
+#include <bcmolt_model_types.h>
+#include <bcm_dev_log.h>
+#include "bcmolt_user_appl_ps.h"
+#include "bcmolt_user_appl_ps_internal.h"
+
+#ifndef SIMULATION_BUILD
+#include <bcmolt_board.h>
+#endif
+
+#define PS_DEFAULT_MAX_PAIRS 16
+#define PS_TASK_MSG_Q_SIZE 64
+
+typedef struct
+{
+ bcmolt_ps_pair pair;
+ bcmos_bool is_valid;
+} ps_pair_state;
+
+typedef struct
+{
+ bcmos_bool is_running;
+ bcmolt_ps_global_cfg cfg;
+ ps_pair_state *pairs;
+ uint32_t switch_start_time_us;
+} ps_global_state;
+
+typedef struct
+{
+ bcmos_msg os_msg;
+ bcmolt_ps_pon pon;
+ bcmolt_auto *ind;
+} ps_task_msg;
+
+typedef enum
+{
+ PS_PON_MODE_INVALID,
+ PS_PON_MODE_GPON,
+ PS_PON_MODE_EPON,
+ PS_PON_MODE_XGPON,
+} ps_pon_mode;
+
+static ps_global_state ps_state = {};
+static bcmos_mutex ps_mutex;
+static bcmos_task ps_task;
+
+#ifdef ENABLE_LOG
+static dev_log_id ps_log_id;
+dev_log_id ps_get_log_id(void)
+{
+ return ps_log_id;
+}
+#endif
+
+static inline bcmos_bool ps_pon_equal(const bcmolt_ps_pon *a, const bcmolt_ps_pon *b)
+{
+ return a->device_id == b->device_id && a->pon_id == b->pon_id;
+}
+
+static inline bcmos_bool ps_pair_equal(const bcmolt_ps_pair *a, const bcmolt_ps_pair *b)
+{
+ return ps_pon_equal(&a->standby, &b->standby) && ps_pon_equal(&a->working, &b->working);
+}
+
+static bcmos_errno bcmolt_ps_pon_pair_get(const bcmolt_ps_pon *pon, bcmolt_ps_pair **pair)
+{
+ uint16_t i;
+
+ if (!ps_state.is_running)
+ {
+ *pair = NULL;
+ PS_ERR("PS application not running\n");
+ return BCM_ERR_STATE;
+ }
+
+ for (i = 0; i < ps_state.cfg.max_num_pairs; i++)
+ {
+ if (ps_state.pairs[i].is_valid)
+ {
+ if (ps_pon_equal(pon, &ps_state.pairs[i].pair.standby) ||
+ ps_pon_equal(pon, &ps_state.pairs[i].pair.working))
+ {
+ *pair = &ps_state.pairs[i].pair;
+ return BCM_ERR_OK;
+ }
+ }
+ }
+
+ return BCM_ERR_NOENT;
+}
+
+static bcmos_errno ps_init_task(void)
+{
+ bcmos_task_parm task_params =
+ {
+ .name = "user_appl_ps",
+ .priority = TASK_PRIORITY_USER_APPL_PS,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+
+ return bcmos_task_create(&ps_task, &task_params);
+}
+
+static bcmos_errno ps_init_module(void)
+{
+ bcmos_module_parm module_params =
+ {
+ .qparm = { .name = "user_appl_ps", .size = PS_TASK_MSG_Q_SIZE }
+ };
+
+ return bcmos_module_create(BCMOS_MODULE_ID_USER_APPL_PS, &ps_task, &module_params);
+}
+
+static ps_pon_mode ps_pon_mode_get(bcmolt_devid dev)
+{
+ bcmos_errno err;
+ bcmolt_system_mode system_mode;
+
+ err = bcmolt_system_mode_get(dev, &system_mode);
+ if (err != BCM_ERR_OK)
+ {
+ return PS_PON_MODE_INVALID;
+ }
+ switch (system_mode)
+ {
+ case BCMOLT_SYSTEM_MODE_GPON__4_X:
+ case BCMOLT_SYSTEM_MODE_GPON__8_X:
+ case BCMOLT_SYSTEM_MODE_GPON__16_X:
+ return PS_PON_MODE_GPON;
+ case BCMOLT_SYSTEM_MODE_EPON__16_X:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA:
+ case BCMOLT_SYSTEM_MODE_EPON__8_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__4_X_10_G:
+ case BCMOLT_SYSTEM_MODE_EPON__2_X_10_G:
+ return PS_PON_MODE_EPON;
+ case BCMOLT_SYSTEM_MODE_XGPON_1__4_X:
+ case BCMOLT_SYSTEM_MODE_XGPON_1__8_X:
+ case BCMOLT_SYSTEM_MODE_XGS__2_X_10_G:
+ case BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G:
+ return PS_PON_MODE_XGPON;
+ default:
+ return PS_PON_MODE_INVALID;
+ }
+}
+
+static void ps_handle_msg(bcmos_module_id module_id, bcmos_msg *os_msg)
+{
+ ps_task_msg *msg = (ps_task_msg *)os_msg;
+ bcmos_errno err;
+
+ /* process the indication */
+ if (msg->ind->hdr.obj_type == BCMOLT_OBJ_ID_DEVICE || msg->ind->hdr.obj_type == BCMOLT_OBJ_ID_DEBUG)
+ {
+ /* there's no need to mirror device-level indications */
+ err = BCM_ERR_OK;
+ }
+ else
+ {
+ switch (ps_pon_mode_get(msg->pon.device_id))
+ {
+ case PS_PON_MODE_GPON:
+ err = ps_process_ind_gpon(&msg->pon, msg->ind);
+ break;
+ case PS_PON_MODE_EPON:
+ err = ps_process_ind_epon(&msg->pon, msg->ind);
+ break;
+ case PS_PON_MODE_XGPON:
+ err = ps_process_ind_xgpon(&msg->pon, msg->ind);
+ break;
+ default:
+ err = BCM_ERR_STATE; /* not a supported system mode */
+ break;
+ }
+ }
+
+ if (err != BCM_ERR_OK)
+ {
+ bcmolt_auto *ind = msg->ind;
+
+ PS_ERR("Error processing indication (obj_type=%u, subgroup=%u): %s\n", ind->hdr.obj_type, ind->hdr.subgroup, bcmos_strerror(err));
+ }
+
+ /* free the cloned indication since we're done processing it */
+ bcmolt_msg_free(&msg->ind->hdr);
+
+ /* free the internal OS message handle */
+ bcmos_free(os_msg);
+}
+
+void bcmolt_ps_appl_init(void)
+{
+ bcmos_errno err;
+
+#ifdef ENABLE_LOG
+ ps_log_id = bcm_dev_log_id_register("user_appl_ps", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
+#endif
+
+ err = bcmos_mutex_create(&ps_mutex, 0, NULL);
+ BUG_ON(err != BCM_ERR_OK);
+
+ err = ps_init_task();
+ BUG_ON(err != BCM_ERR_OK);
+
+ err = ps_init_module();
+ BUG_ON(err != BCM_ERR_OK);
+}
+
+bcmos_errno bcmolt_ps_appl_start(const bcmolt_ps_global_cfg *cfg)
+{
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (ps_state.is_running)
+ {
+ bcmos_mutex_unlock(&ps_mutex);
+ PS_ERR("PS application already running\n");
+ return BCM_ERR_STATE;
+ }
+
+ ps_state.cfg = *cfg;
+ ps_state.cfg.max_num_pairs = (ps_state.cfg.max_num_pairs == 0) ? PS_DEFAULT_MAX_PAIRS : ps_state.cfg.max_num_pairs;
+ ps_state.pairs = bcmos_calloc(sizeof(*ps_state.pairs) * ps_state.cfg.max_num_pairs);
+ if (ps_state.pairs == NULL)
+ {
+ BCM_MEMZERO_STRUCT(&ps_state);
+ bcmos_mutex_unlock(&ps_mutex);
+ PS_ERR("memory allocation failed\n");
+ return BCM_ERR_NOMEM;
+ }
+
+ ps_state.is_running = BCMOS_TRUE;
+
+ bcmos_mutex_unlock(&ps_mutex);
+ PS_INFO("PS application started\n");
+ return BCM_ERR_OK;
+}
+
+bcmos_errno bcmolt_ps_appl_stop(void)
+{
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (!ps_state.is_running)
+ {
+ bcmos_mutex_unlock(&ps_mutex);
+ PS_ERR("PS application not running\n");
+ return BCM_ERR_STATE;
+ }
+
+ ps_state.is_running = BCMOS_FALSE;
+
+ bcmos_free(ps_state.pairs);
+ BCM_MEMZERO_STRUCT(&ps_state);
+
+ bcmos_mutex_unlock(&ps_mutex);
+ PS_INFO("PS application stopped\n");
+ return BCM_ERR_OK;
+}
+
+bcmos_bool bcmolt_ps_appl_is_running(void)
+{
+ bcmos_bool ret;
+ bcmos_mutex_lock(&ps_mutex);
+ ret = ps_state.is_running;
+ bcmos_mutex_unlock(&ps_mutex);
+ return ret;
+}
+
+bcmos_errno bcmolt_ps_global_cfg_get(bcmolt_ps_global_cfg *cfg)
+{
+ bcmos_errno err;
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (!ps_state.is_running)
+ {
+ PS_ERR("PS application not running\n");
+ err = BCM_ERR_STATE;
+ }
+ else
+ {
+ *cfg = ps_state.cfg;
+ err = BCM_ERR_OK;
+ }
+
+ bcmos_mutex_unlock(&ps_mutex);
+ return err;
+}
+
+bcmos_errno bcmolt_ps_global_cfg_update(const bcmolt_ps_global_cfg *cfg)
+{
+ bcmos_errno err;
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (!ps_state.is_running)
+ {
+ PS_ERR("PS application not running\n");
+ err = BCM_ERR_STATE;
+ }
+ else if (cfg->max_num_pairs != ps_state.cfg.max_num_pairs)
+ {
+ PS_ERR("cannot change max number of protected pairs while running\n");
+ err = BCM_ERR_PARM;
+ }
+ else
+ {
+ ps_state.cfg = *cfg;
+ err = BCM_ERR_OK;
+ }
+
+ bcmos_mutex_unlock(&ps_mutex);
+ return err;
+}
+
+bcmos_errno bcmolt_ps_pairs_get(uint16_t array_len, bcmolt_ps_pair *pairs, uint16_t *num_written)
+{
+ bcmos_errno err;
+ uint16_t i;
+
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (!ps_state.is_running)
+ {
+ PS_ERR("PS application not running\n");
+ err = BCM_ERR_STATE;
+ }
+ else
+ {
+ *num_written = 0;
+ err = BCM_ERR_OK;
+ for (i = 0; i < ps_state.cfg.max_num_pairs; i++)
+ {
+ if (ps_state.pairs[i].is_valid)
+ {
+ if (*num_written == array_len)
+ {
+ PS_ERR("array size too small to collect all pairs\n");
+ err = BCM_ERR_PARM;
+ break;
+ }
+ pairs[*num_written] = ps_state.pairs[i].pair;
+ (*num_written)++;
+ }
+ }
+ }
+
+ bcmos_mutex_unlock(&ps_mutex);
+ return err;
+}
+
+bcmos_errno bcmolt_ps_pair_add(const bcmolt_ps_pair *pair)
+{
+ uint16_t i;
+ bcmos_errno err;
+
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (!ps_state.is_running)
+ {
+ PS_ERR("PS application not running\n");
+ err = BCM_ERR_STATE;
+ }
+ else if (ps_pon_equal(&pair->working, &pair->standby))
+ {
+ PS_ERR("A PON cannot protect itself\n");
+ err = BCM_ERR_PARM;
+ }
+ else
+ {
+ err = BCM_ERR_OK;
+ for (i = 0; i < ps_state.cfg.max_num_pairs; i++)
+ {
+ if (ps_state.pairs[i].is_valid &&
+ (ps_pon_equal(&pair->working, &ps_state.pairs[i].pair.working) ||
+ ps_pon_equal(&pair->working, &ps_state.pairs[i].pair.standby) ||
+ ps_pon_equal(&pair->standby, &ps_state.pairs[i].pair.working) ||
+ ps_pon_equal(&pair->standby, &ps_state.pairs[i].pair.standby)))
+ {
+ PS_ERR("One of the PONs was already present in an existing pair\n");
+ err = BCM_ERR_ALREADY;
+ break;
+ }
+ }
+
+ if (err == BCM_ERR_OK)
+ {
+ err = BCM_ERR_NORES;
+ for (i = 0; i < ps_state.cfg.max_num_pairs; i++)
+ {
+ if (!ps_state.pairs[i].is_valid)
+ {
+ ps_state.pairs[i].pair = *pair;
+ ps_state.pairs[i].is_valid = BCMOS_TRUE;
+ err = BCM_ERR_OK;
+ break;
+ }
+ }
+ if (err == BCM_ERR_NORES)
+ {
+ PS_ERR("Not enough memory available to add pair\n");
+ }
+ }
+ }
+
+ bcmos_mutex_unlock(&ps_mutex);
+ return err;
+}
+
+bcmos_errno bcmolt_ps_pon_remove(const bcmolt_ps_pon *pon)
+{
+ uint16_t i;
+ bcmos_errno err;
+
+ bcmos_mutex_lock(&ps_mutex);
+
+ if (!ps_state.is_running)
+ {
+ PS_ERR("PS application not running\n");
+ err = BCM_ERR_STATE;
+ }
+ else
+ {
+ err = BCM_ERR_NOENT;
+ for (i = 0; i < ps_state.cfg.max_num_pairs; i++)
+ {
+ if (ps_state.pairs[i].is_valid &&
+ (ps_pon_equal(pon, &ps_state.pairs[i].pair.working) ||
+ ps_pon_equal(pon, &ps_state.pairs[i].pair.standby)))
+ {
+ ps_state.pairs[i].is_valid = BCMOS_FALSE;
+ err = BCM_ERR_OK;
+ break;
+ }
+ }
+ if (err == BCM_ERR_NOENT)
+ {
+ PS_ERR("Specified PON not found\n");
+ }
+ }
+
+ bcmos_mutex_unlock(&ps_mutex);
+ return err;
+}
+
+bcmos_errno bcmolt_ps_pon_state_get(const bcmolt_ps_pon *pon, bcmolt_ps_pon_state *state, bcmolt_ps_pon *partner)
+{
+ bcmos_errno err;
+ bcmolt_ps_pair *pair;
+
+ bcmos_mutex_lock(&ps_mutex);
+ err = bcmolt_ps_pon_pair_get(pon, &pair);
+ bcmos_mutex_unlock(&ps_mutex);
+
+ if (err == BCM_ERR_OK)
+ {
+ if (ps_pon_equal(pon, &pair->standby))
+ {
+ *state = BCMOLT_PS_PON_STATE_STANDBY;
+ *partner = pair->working;
+ }
+ else
+ {
+ *state = BCMOLT_PS_PON_STATE_WORKING;
+ *partner = pair->standby;
+ }
+ return BCM_ERR_OK;
+ }
+ else if (err == BCM_ERR_NOENT)
+ {
+ *state = BCMOLT_PS_PON_STATE_UNASSOCIATED;
+ return BCM_ERR_OK;
+ }
+ else
+ {
+ return err;
+ }
+}
+
+bcmos_errno bcmolt_ps_process_ind(const bcmolt_ps_pon *pon, bcmolt_auto *ind)
+{
+ bcmos_errno err;
+ bcmolt_msg *ind_clone = NULL;
+
+ if (!bcmolt_ps_appl_is_running())
+ {
+ return BCM_ERR_OK;
+ }
+
+ /* Make a copy of all indications received so they can be handled later asynchronously. We don't validate PON state
+ * here since it might change before we get around to handling it.
+ *
+ * Note that this could be optimized to filter out indications that aren't relevant for protection switching before
+ * copying them, but this is left out for simplicity. */
+ err = bcmolt_msg_clone(&ind_clone, &ind->hdr);
+ if (err != BCM_ERR_OK)
+ {
+ PS_ERR("Indication clone failed: %s\n", bcmos_strerror(err));
+ return err;
+ }
+
+ /* send this indication to the internal task's message queue to be processed */
+ ps_task_msg *msg = bcmos_calloc(sizeof(*msg));
+ if (msg == NULL)
+ {
+ PS_ERR("Message calloc failed\n");
+ bcmolt_msg_free(ind_clone);
+ return BCM_ERR_NOMEM;
+ }
+
+ msg->os_msg.handler = ps_handle_msg;
+ msg->os_msg.release = bcmolt_os_msg_release_cb;
+ msg->pon = *pon;
+ msg->ind = (bcmolt_auto *)ind_clone;
+
+ err = bcmos_msg_send_to_module(BCMOS_MODULE_ID_USER_APPL_PS, &msg->os_msg, 0);
+ if (err != BCM_ERR_OK)
+ {
+ PS_ERR("Message send failed: %s\n", bcmos_strerror(err));
+ return err;
+ }
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno bcmolt_ps_change_pon_to_standby(const bcmolt_ps_pon *pon)
+{
+ bcmos_errno err;
+
+ PS_INFO("Switching PON <%d:%d> working->standby\n", pon->device_id, pon->pon_id);
+ switch (ps_pon_mode_get(pon->device_id))
+ {
+ case PS_PON_MODE_GPON:
+ err = ps_move_to_standby_gpon(pon);
+ break;
+ case PS_PON_MODE_EPON:
+ err = ps_move_to_standby_epon(pon);
+ break;
+ case PS_PON_MODE_XGPON:
+ err = ps_move_to_standby_xgpon(pon);
+ break;
+ default:
+ err = BCM_ERR_STATE; /* not a supported system mode */
+ break;
+ }
+ if (err != BCM_ERR_OK)
+ {
+ PS_ERR("<%d:%d> working->standby API failed: %s\n", pon->device_id, pon->pon_id, bcmos_strerror(err));
+ }
+ return err;
+}
+
+static bcmos_errno bcmolt_ps_change_pon_to_working(const bcmolt_ps_pon *pon)
+{
+ bcmos_errno err;
+
+ PS_INFO("Switching PON <%d:%d> standby->working\n", pon->device_id, pon->pon_id);
+ switch (ps_pon_mode_get(pon->device_id))
+ {
+ case PS_PON_MODE_GPON:
+ err = ps_move_to_working_gpon(pon);
+ break;
+ case PS_PON_MODE_EPON:
+ err = ps_move_to_working_epon(pon);
+ break;
+ case PS_PON_MODE_XGPON:
+ err = ps_move_to_working_xgpon(pon);
+ break;
+ default:
+ err = BCM_ERR_STATE; /* not a supported system mode */
+ break;
+ }
+ if (err != BCM_ERR_OK)
+ {
+ PS_ERR("<%d:%d> standby->working API failed: %s\n", pon->device_id, pon->pon_id, bcmos_strerror(err));
+ }
+ return err;
+}
+
+static bcmos_errno bcmolt_ps_disable_tx_optics(const bcmolt_ps_pon *pon)
+{
+ PS_INFO("Disabling TRX for PON <%d:%d:%d>\n", pon->device_id, pon->pon_id, pon->transceiver_id);
+
+ /* This disables the TX optical channel on a given PON port on the BCM68620 SVK board.
+ For other boards, this must be changed to match the board design. */
+#ifndef SIMULATION_BUILD
+ return bcm_board_trx_enable(pon->transceiver_id, BCMOS_FALSE);
+#else
+ return BCM_ERR_OK;
+#endif
+}
+
+static bcmos_errno bcmolt_ps_enable_tx_optics(const bcmolt_ps_pon *pon)
+{
+#ifndef SIMULATION_BUILD
+ bcmos_errno ret;
+#endif
+
+ PS_INFO("Enabling TRX for PON <%d:%d:%d>\n", pon->device_id, pon->pon_id, pon->transceiver_id);
+
+ /* This enables the TX optical channel on a given PON port on the BCM68620 SVK board.
+ For other boards, this must be changed to match the board design. */
+#ifndef SIMULATION_BUILD
+ ret = bcm_board_trx_enable(pon->transceiver_id, BCMOS_TRUE);
+ bcmos_usleep(ps_state.cfg.trx_warming_delay);
+ return ret;
+#else
+ return BCM_ERR_OK;
+#endif
+}
+
+bcmos_errno bcmolt_ps_switch_perform(const bcmolt_ps_pon *pon)
+{
+ bcmos_errno err;
+ bcmolt_ps_pair *pair;
+ bcmolt_ps_pon temp_pon;
+
+ bcmos_mutex_lock(&ps_mutex);
+ ps_state.switch_start_time_us = bcmos_timestamp();
+ err = bcmolt_ps_pon_pair_get(pon, &pair);
+ bcmos_mutex_unlock(&ps_mutex);
+
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ if (ps_state.cfg.switch_sequence == BCMOLT_PS_SWITCH_SEQUENCE_TRX_FIRST)
+ {
+ /* step 1: disable optical TX channel of the working PON */
+ err = bcmolt_ps_disable_tx_optics(&pair->working);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ /* step 2: enable optical TX channel of the standby PON */
+ err = bcmolt_ps_enable_tx_optics(&pair->standby);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ /* step 3: change the standby PON state to working */
+ err = bcmolt_ps_change_pon_to_working(&pair->standby);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ /* step 4: change the working PON state to standby */
+ err = bcmolt_ps_change_pon_to_standby(&pair->working);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+ }
+ else /* ps_state.cfg.switch_sequence == BCMOLT_PS_SWITCH_SEQUENCE_STANDARD */
+ {
+ /* step 1: change the working PON state to standby */
+ err = bcmolt_ps_change_pon_to_standby(&pair->working);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ /* step 2: disable optical TX channel of the working PON */
+ err = bcmolt_ps_disable_tx_optics(&pair->working);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ /* step 3: enable optical TX channel of the standby PON */
+ err = bcmolt_ps_enable_tx_optics(&pair->standby);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+
+ /* step 4: change the standby PON state to working */
+ err = bcmolt_ps_change_pon_to_working(&pair->standby);
+ if (err != BCM_ERR_OK)
+ {
+ goto exit;
+ }
+ }
+
+ /* step 5: update internal database for new PON states */
+ bcmos_mutex_lock(&ps_mutex);
+ temp_pon = pair->working;
+ pair->working = pair->standby;
+ pair->standby = temp_pon;
+ bcmos_mutex_unlock(&ps_mutex);
+
+exit:
+ if (err == BCM_ERR_OK)
+ {
+ PS_INFO(
+ "<%d:%d> switchover started successfully (<%d:%d> is now standby)\n",
+ pair->working.device_id,
+ pair->working.pon_id,
+ pair->standby.device_id,
+ pair->standby.pon_id);
+ }
+ else if (pair)
+ {
+ PS_INFO(
+ "<%d:%d> switchover encountered an error: %s\n",
+ pair->working.device_id,
+ pair->working.pon_id,
+ bcmos_strerror(err));
+ }
+
+ return err;
+}
+
+uint32_t ps_get_last_switch_start_time_us(void)
+{
+ uint32_t ret;
+ bcmos_mutex_lock(&ps_mutex);
+ ret = ps_state.switch_start_time_us;
+ bcmos_mutex_unlock(&ps_mutex);
+ return ret;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps.h b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps.h
new file mode 100644
index 0000000..50224be
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps.h
@@ -0,0 +1,210 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_USER_APPL_PS_H_
+#define _BCMOLT_USER_APPL_PS_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+
+/** Conditions that can cause a protection switch to be performed. */
+typedef enum
+{
+ BCMOLT_PS_SWITCH_CONDITION_LOS, /**< Switch when LoS alarm is received for a working PON. */
+ BCMOLT_PS_SWITCH_CONDITION_MANUAL, /**< Do not switch automatically. */
+} bcmolt_ps_switch_condition;
+
+/** How provisioning should be mirrored from working to standby. */
+typedef enum
+{
+ BCMOLT_PS_MIRROR_MODE_AUTO, /**< Whenever an indication is received on the working PON, mirror all necessary
+ configuration to standby PON. */
+ BCMOLT_PS_MIRROR_MODE_NONE, /**< Do not automatically mirror any provisioning from working to standby. */
+} bcmolt_ps_mirror_mode;
+
+/** What the order of operations should be when performing a switchover. */
+typedef enum
+{
+ BCMOLT_PS_SWITCH_SEQUENCE_STANDARD, /**< "standard" order:
+ 1: Change the working PON state to standby.
+ 2: Disable optical TX channel of working PON.
+ 3: Enable optical TX channel of standby PON.
+ 4: Change the standby PON state to working. */
+ BCMOLT_PS_SWITCH_SEQUENCE_TRX_FIRST, /**< Perform transceiver enable/disable first:
+ 1: Disable optical TX channel of working PON.
+ 2: Enable optical TX channel of standby PON.
+ 3: Change the standby PON state to working.
+ 4: Change the working PON state to standby. */
+} bcmolt_ps_switch_sequence;
+
+/** Global configuration for the protection switching application. */
+typedef struct
+{
+ uint16_t max_num_pairs; /**< Maximum number of protected pairs supported. */
+ bcmolt_ps_switch_condition switch_condition; /**< Condition that should cause a switchover. */
+ bcmolt_ps_switch_sequence switch_sequence; /**< Order of operations when performing a switchover. */
+
+ /* The following properties are only used in GPON mode. */
+ bcmolt_ps_mirror_mode mirror_mode; /**< How provisioning should be mirrored from working to standby. */
+ bcmos_bool mirror_mac_entries; /**< If TRUE, mirror all MAC learning entries to the standby PON
+ when learning indications are received from the working PON.
+ This will also cause entries to be automatically deleted when
+ aging indications are received and moved when MAC move
+ indications are received. */
+ bcmos_bool static_mac_entries; /**< If TRUE, when entries are mirrored to the standby PON, treat
+ them as "static" (not aged). */
+ uint32_t trx_warming_delay; /**< Delay (in usec) to wait after TRX enable, in order to
+ guarantee that POPUP PLOAMs are correctly seen by ONUs. */
+} bcmolt_ps_global_cfg;
+
+/** A single PON on an OLT device - half of a protected pair. */
+typedef struct
+{
+ bcmolt_devid device_id;
+ bcmolt_pon_ni pon_id;
+ bcmolt_pon_ni transceiver_id;
+} bcmolt_ps_pon;
+
+/** A protected pair of PONs, on the same OLT device or different devices. */
+typedef struct
+{
+ bcmolt_ps_pon working;
+ bcmolt_ps_pon standby;
+} bcmolt_ps_pair;
+
+/** Possible states for a single PON from a protection switching point-of-view. */
+typedef enum
+{
+ BCMOLT_PS_PON_STATE_UNASSOCIATED, /**< The PON is not part of a protected pair. */
+ BCMOLT_PS_PON_STATE_WORKING, /**< The PON is designated as working with the TRX enabled while active. */
+ BCMOLT_PS_PON_STATE_STANDBY, /**< The PON is designated as standby with the TRX disabled while active. */
+} bcmolt_ps_pon_state;
+
+/** Initialize the protection switching application (this should be called as part of application startup). */
+void bcmolt_ps_appl_init(void);
+
+/** Start the protection switching application (this is global for the entire system).
+ *
+ * \param[in] cfg Global protection switching configuration.
+ * \return BCM_ERR_OK if the application was started successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_appl_start(const bcmolt_ps_global_cfg *cfg);
+
+/** Stop the protection switching application (this is global for the entire system).
+ *
+ * \return BCM_ERR_OK if the application was stopped successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_appl_stop(void);
+
+/** Query whether the protection switching application is currently running.
+ *
+ * \return BCMOS_TRUE if the application is running, BCMOS_FALSE otherwise.
+ */
+bcmos_bool bcmolt_ps_appl_is_running(void);
+
+/** Get the configuration of the running protection switching application (this is global for the entire system).
+ *
+ * \param[out] cfg Global protection switching configuration.
+ * \return BCM_ERR_OK if the configuration was retrieved successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_global_cfg_get(bcmolt_ps_global_cfg *cfg);
+
+/** Change the configuration of the running protection switching application (this is global for the entire system).
+ *
+ * \param[in] cfg Global protection switching configuration.
+ * \return BCM_ERR_OK if the configuration was updated successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_global_cfg_update(const bcmolt_ps_global_cfg *cfg);
+
+/** Get the list of all protected pairs currently tracked by the protection switching application.
+ *
+ * \param[in] array_len The length of the pairs array.
+ * \param[out] pairs An array of protected pairs to fill.
+ * \param[out] num_written The number of pairs written to the array.
+ * \return BCM_ERR_OK if the array was written successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_pairs_get(uint16_t array_len, bcmolt_ps_pair *pairs, uint16_t *num_written);
+
+/** Add a protected pair to the protection switching database.
+ *
+ * After this has been called, provisioning will be mirrored from the working PON to the standby PON according to the
+ * mirror_mode global parameter, and a switch will be performed per the switch_condition global parameter.
+ *
+ * \param[in] pair A pair of PONs to add - they can be on the same OLT device or different devices.
+ * \return BCM_ERR_OK if the pair was added successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_pair_add(const bcmolt_ps_pair *pair);
+
+/** Remove the protected pair containing the specified PON from the protection switching database.
+*
+* \param[in] pon A PON that is a member of a protection pair.
+* \return BCM_ERR_OK if the PON was removed successfully, <0 otherwise.
+*/
+bcmos_errno bcmolt_ps_pon_remove(const bcmolt_ps_pon *pon);
+
+/** Query for the current state of a protected PON.
+*
+* \param[in] pon The PON to query.
+* \param[out] state The protection state of the PON (e.g. working/standby).
+* \param[out] partner If this PON is protected, the other half of the protected pair.
+* \return BCM_ERR_OK if the PON was queried successfully, <0 otherwise.
+*/
+bcmos_errno bcmolt_ps_pon_state_get(const bcmolt_ps_pon *pon, bcmolt_ps_pon_state *state, bcmolt_ps_pon *partner);
+
+/** Process an indication received by the working PON.
+ *
+ * The action taken will depend on the object/indication type and the mirror_mode global parameter.
+ * If the application isn't running, the supplied PON isn't in a protected working state or the indication isn't
+ * relevant for protection switching, the indication will be ignored.
+ *
+ * Note: this function does not free the indication.
+ * The caller must free it using bcmos_msg_free() after calling this function.
+ *
+ * \param[in] pon The PON on which the indication was received.
+ * \param[in] ind The indication that was received.
+ * \return BCM_ERR_OK if the indication was processed successfully or ignored, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_process_ind(const bcmolt_ps_pon *pon, bcmolt_auto *ind);
+
+/** Immediately perform a protection switch on the given PON, which must belong to a protected pair.
+ *
+ * This function is primarily intended for use with the BCMOLT_PS_SWITCH_CONDITION_MANUAL switch condition, or for
+ * debugging purposes.
+ *
+ * Note that this doesn't wait for the switch to complete - it merely just configures the transceivers and initiates
+ * the PON state transitions. To wait for completion, the host must wait for the PON NI state change complete or
+ * "protection_switching_traffic_resume" indications to be received.
+ *
+ * \param[in] pair The PON on which to perform the switch (either the working or standby PON of a protected pair).
+ * \return BCM_ERR_OK if the switch was successful, <0 otherwise.
+ */
+bcmos_errno bcmolt_ps_switch_perform(const bcmolt_ps_pon *pon);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_cli.c b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_cli.c
new file mode 100644
index 0000000..d75bd00
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_cli.c
@@ -0,0 +1,291 @@
+/*
+<: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 <bcmcli.h>
+#include "bcmolt_user_appl_ps_cli.h"
+#include "bcmolt_user_appl_ps.h"
+
+static bcmos_errno ps_cmd_start(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmcli_cmd_parm *parm;
+ bcmolt_ps_global_cfg cfg = {};
+
+ parm = bcmcli_find_named_parm(session, "max_pairs");
+ if (parm != NULL)
+ {
+ cfg.max_num_pairs = (uint16_t)parm->value.number;
+ }
+
+ parm = bcmcli_find_named_parm(session, "switch_condition");
+ if (parm != NULL)
+ {
+ cfg.switch_condition = (bcmolt_ps_switch_condition)parm->value.number;
+ }
+
+ parm = bcmcli_find_named_parm(session, "switch_sequence");
+ if (parm != NULL)
+ {
+ cfg.switch_sequence = (bcmolt_ps_switch_sequence)parm->value.number;
+ }
+
+ parm = bcmcli_find_named_parm(session, "mirror_mode");
+ if (parm != NULL)
+ {
+ cfg.mirror_mode = (bcmolt_ps_mirror_mode)parm->value.number;
+ }
+
+ parm = bcmcli_find_named_parm(session, "mirror_mac_entries");
+ if (parm != NULL)
+ {
+ cfg.mirror_mac_entries = (bcmos_bool)parm->value.number;
+ }
+
+ parm = bcmcli_find_named_parm(session, "static_mac_entries");
+ if (parm != NULL)
+ {
+ cfg.static_mac_entries = (bcmos_bool)parm->value.number;
+ }
+
+ parm = bcmcli_find_named_parm(session, "trx_warming_delay");
+ if (parm != NULL)
+ {
+ cfg.trx_warming_delay = parm->value.number;
+ }
+
+ return bcmolt_ps_appl_start(&cfg);
+}
+
+static bcmos_errno ps_cmd_stop(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ return bcmolt_ps_appl_stop();
+}
+
+static bcmos_errno ps_cmd_get_cfg(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmos_errno err;
+ bcmolt_ps_global_cfg cfg;
+
+ err = bcmolt_ps_global_cfg_get(&cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ bcmcli_session_print(session, "max_pairs=%d\n", cfg.max_num_pairs);
+ bcmcli_session_print(
+ session,
+ "switch_condition=%s\n",
+ cfg.switch_condition == BCMOLT_PS_SWITCH_CONDITION_LOS ? "los" : "manual");
+ bcmcli_session_print(
+ session,
+ "switch_sequence=%s\n",
+ cfg.switch_sequence == BCMOLT_PS_SWITCH_SEQUENCE_STANDARD ? "std" : "trx_first");
+ bcmcli_session_print(
+ session,
+ "mirror_mode=%s\n",
+ cfg.mirror_mode == BCMOLT_PS_MIRROR_MODE_AUTO ? "auto" : "none");
+ bcmcli_session_print(session, "mirror_mac_entries=%s\n", cfg.mirror_mac_entries ? "yes" : "no");
+ bcmcli_session_print(session, "static_mac_entries=%s\n", cfg.static_mac_entries ? "yes" : "no");
+ bcmcli_session_print(session, "trx_warming_delay=%u\n", cfg.trx_warming_delay);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno ps_cmd_show_pairs(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmos_errno err;
+ bcmolt_ps_global_cfg cfg;
+ bcmolt_ps_pair *pairs;
+ uint16_t num_written;
+
+ err = bcmolt_ps_global_cfg_get(&cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ pairs = bcmos_calloc(sizeof(*pairs) * cfg.max_num_pairs);
+
+ err = bcmolt_ps_pairs_get(cfg.max_num_pairs, pairs, &num_written);
+ if (err == BCM_ERR_OK)
+ {
+ uint16_t i;
+
+ bcmcli_session_print(session, " working | standby\n");
+ bcmcli_session_print(session, " =========+=========\n");
+ for (i = 0; i < num_written; i++)
+ {
+ bcmcli_session_print(
+ session,
+ "[%2d]: %2d.%2d.%2d | %2d.%2d.%2d\n",
+ i,
+ pairs[i].working.device_id,
+ pairs[i].working.pon_id,
+ pairs[i].working.transceiver_id,
+ pairs[i].standby.device_id,
+ pairs[i].standby.pon_id,
+ pairs[i].standby.transceiver_id);
+ }
+ }
+
+ bcmos_free(pairs);
+ return err;
+}
+
+static bcmos_errno ps_cmd_add_pair(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmcli_cmd_parm *working_device = bcmcli_find_named_parm(session, "working.device");
+ bcmcli_cmd_parm *working_pon = bcmcli_find_named_parm(session, "working.pon");
+ bcmcli_cmd_parm *working_transceiver = bcmcli_find_named_parm(session, "working.transceiver");
+ bcmcli_cmd_parm *standby_device = bcmcli_find_named_parm(session, "standby.device");
+ bcmcli_cmd_parm *standby_pon = bcmcli_find_named_parm(session, "standby.pon");
+ bcmcli_cmd_parm *standby_transceiver = bcmcli_find_named_parm(session, "standby.transceiver");
+
+ bcmolt_ps_pair pair =
+ {
+ .working =
+ {
+ .device_id = (bcmolt_devid)working_device->value.number,
+ .pon_id = (bcmolt_pon_ni)working_pon->value.number,
+ .transceiver_id = (bcmolt_pon_ni)working_transceiver->value.number
+ },
+ .standby =
+ {
+ .device_id = (bcmolt_devid)standby_device->value.number,
+ .pon_id = (bcmolt_pon_ni)standby_pon->value.number,
+ .transceiver_id = (bcmolt_pon_ni)standby_transceiver->value.number
+ },
+ };
+
+ // Set transceiver_id to pon_id if default
+ if (pair.working.transceiver_id == (bcmolt_pon_ni)-1)
+ {
+ pair.working.transceiver_id = pair.working.pon_id;
+ }
+ if (pair.standby.transceiver_id == (bcmolt_pon_ni)-1)
+ {
+ pair.standby.transceiver_id = pair.standby.pon_id;
+ }
+
+ return bcmolt_ps_pair_add(&pair);
+}
+
+static bcmos_errno ps_cmd_remove_pon(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmcli_cmd_parm *device = bcmcli_find_named_parm(session, "device");
+ bcmcli_cmd_parm *pon = bcmcli_find_named_parm(session, "pon");
+
+ bcmolt_ps_pon ps_pon =
+ {
+ .device_id = (bcmolt_devid)device->value.number,
+ .pon_id = (bcmolt_pon_ni)pon->value.number
+ };
+
+ return bcmolt_ps_pon_remove(&ps_pon);
+}
+
+static bcmos_errno ps_cmd_perform_switch(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmcli_cmd_parm *device = bcmcli_find_named_parm(session, "device");
+ bcmcli_cmd_parm *pon = bcmcli_find_named_parm(session, "pon");
+
+ bcmolt_ps_pon ps_pon =
+ {
+ .device_id = (bcmolt_devid)device->value.number,
+ .pon_id = (bcmolt_pon_ni)pon->value.number
+ };
+
+ return bcmolt_ps_switch_perform(&ps_pon);
+}
+
+bcmos_errno bcmolt_user_appl_ps_cli_init(bcmcli_entry *top_dir)
+{
+ static bcmcli_enum_val switch_cond_table[] =
+ {
+ { .name = "los", .val = BCMOLT_PS_SWITCH_CONDITION_LOS },
+ { .name = "manual", .val = BCMOLT_PS_SWITCH_CONDITION_MANUAL },
+ BCMCLI_ENUM_LAST
+ };
+
+ static bcmcli_enum_val switch_seq_table[] =
+ {
+ { .name = "std", .val = BCMOLT_PS_SWITCH_SEQUENCE_STANDARD },
+ { .name = "trx_first", .val = BCMOLT_PS_SWITCH_SEQUENCE_TRX_FIRST },
+ BCMCLI_ENUM_LAST
+ };
+
+ static bcmcli_enum_val mirror_mode_table[] =
+ {
+ { .name = "auto", .val = BCMOLT_PS_MIRROR_MODE_AUTO },
+ { .name = "none", .val = BCMOLT_PS_MIRROR_MODE_NONE },
+ BCMCLI_ENUM_LAST
+ };
+
+ bcmcli_entry *dir =
+ bcmcli_dir_add(top_dir, "ps", "Protection switching user application", BCMCLI_ACCESS_ADMIN, NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+
+ BCMCLI_MAKE_CMD(dir, "start", "Start protection switching application", ps_cmd_start,
+ BCMCLI_MAKE_PARM("max_pairs", "Max number of protected pairs", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM_ENUM("switch_condition", "Switch condition", switch_cond_table, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM_ENUM("switch_sequence", "Switch sequence", switch_seq_table, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM_ENUM("mirror_mode", "Mirror mode", mirror_mode_table, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM_ENUM(
+ "mirror_mac_entries", "Mirror MAC entries", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM_ENUM(
+ "static_mac_entries", "Static MAC entries", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL),
+ BCMCLI_MAKE_PARM("trx_warming_delay", "TRX warming delay (in usec)", BCMCLI_PARM_UDECIMAL, BCMCLI_PARM_FLAG_OPTIONAL));
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "stop", "Stop protection switching application", ps_cmd_stop);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "get_cfg", "Show running configuration parameters", ps_cmd_get_cfg);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "show_pairs", "Show current protected pairs", ps_cmd_show_pairs);
+
+ BCMCLI_MAKE_CMD(dir, "add_pair", "Add protected pair", ps_cmd_add_pair,
+ BCMCLI_MAKE_PARM("working.device", "Device ID of the working PON", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("working.pon", "PON NI of the working PON", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM_DEFVAL("working.transceiver", "Transceiver Id of the working PON",
+ BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL, -1),
+ BCMCLI_MAKE_PARM("standby.device", "Device ID of the standby PON", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("standby.pon", "PON NI of the standby PON", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM_DEFVAL("standby.transceiver", "Transceiver Id of the standby PON",
+ BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL, -1));
+
+ BCMCLI_MAKE_CMD(dir, "remove_pon", "Remove protected pair for (either) PON", ps_cmd_remove_pon,
+ BCMCLI_MAKE_PARM("device", "Device ID of the either working/standby PON", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("pon", "PON NI of the either working/standby PON", BCMCLI_PARM_NUMBER, 0));
+
+ BCMCLI_MAKE_CMD(dir, "perform_switch", "Perform protection switch manually", ps_cmd_perform_switch,
+ BCMCLI_MAKE_PARM("device", "Device ID of the either working/standby PON", BCMCLI_PARM_NUMBER, 0),
+ BCMCLI_MAKE_PARM("pon", "PON NI of the either working/standby PON", BCMCLI_PARM_NUMBER, 0));
+
+ return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_cli.h b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_cli.h
new file mode 100644
index 0000000..db77c09
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_cli.h
@@ -0,0 +1,38 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_USER_APPL_PS_CLI_H_
+#define _BCMOLT_USER_APPL_PS_CLI_H_
+
+#include <bcmos_system.h>
+#include <bcmcli.h>
+
+bcmos_errno bcmolt_user_appl_ps_cli_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_epon.c b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_epon.c
new file mode 100644
index 0000000..dab93cb
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_epon.c
@@ -0,0 +1,188 @@
+/*
+<: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 <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcm_dev_log.h>
+#include <bcmos_common.h>
+#include "bcmolt_user_appl_ps.h"
+#include "bcmolt_user_appl_ps_internal.h"
+
+
+static bcmos_errno ps_handle_ni_no_reports(bcmolt_ps_global_cfg * global_cfg, const bcmolt_ps_pon *pon, const bcmolt_epon_ni_no_reports * ind)
+{
+ if (ind->data.alarm_status == BCMOLT_STATUS_OFF)
+ {
+ PS_INFO("<%d:%d> LoS alarm cleared\n", pon->device_id, pon->pon_id);
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("<%d:%d> LoS alarm raised\n", pon->device_id, pon->pon_id);
+
+ if (global_cfg->switch_condition == BCMOLT_PS_SWITCH_CONDITION_MANUAL)
+ {
+ /* don't react to the switch if we're in manual switch mode */
+ return BCM_ERR_OK;
+ }
+ else
+ {
+ return bcmolt_ps_switch_perform(pon);
+ }
+}
+
+static bcmos_errno ps_handle_logical_link_discovered(bcmolt_ps_global_cfg * global_cfg, const bcmolt_ps_pon *pon, const bcmolt_epon_link_mpcp_discovered * ind)
+{
+ bcmolt_ps_pon_state state;
+ bcmolt_ps_pon partner;
+ bcmos_errno result;
+
+ if (global_cfg->mirror_mode != BCMOLT_PS_MIRROR_MODE_AUTO)
+ {
+ return BCM_ERR_OK;
+ }
+
+ //At this point we know we have a link discovered on a PON that is in the PROTECTED WORKING state.
+ //This means we want to make sure that is is setup on the PROCTECTED STANDBY PON. It is possible
+ //That this link has been previously configured on the STANDBY PON so we have to handle that case
+ //gracefully as well.
+ result = bcmolt_ps_pon_state_get(pon, &state, &partner);
+ PS_INFO("pon %d, bcmolt_ps_pon_state_get result == %d, state == %d, partner.pon_id == %d\n", pon->pon_id, result, state, partner.pon_id);
+ if ((BCM_ERR_OK == result) && (state == BCMOLT_PS_PON_STATE_WORKING))
+ {
+ bcmolt_epon_ni_add_protected_standby_link add_standby = {};
+ bcmolt_epon_ni_key key = {};
+ PS_INFO("Mirroring received link discovered on working PON\n");
+
+ key.epon_ni = (bcmolt_epon_ni)partner.pon_id; //Setup the standby PON as the location to add the link.
+
+ //Setup operation key for api call.
+ BCMOLT_OPER_INIT(&add_standby, epon_ni, add_protected_standby_link, key);
+
+ //Copy over the data from the received messages:
+ BCMOLT_OPER_PROP_SET(&add_standby, epon_ni, add_protected_standby_link, mac_address, ind->key.mac_address);
+ BCMOLT_OPER_PROP_SET(&add_standby, epon_ni, add_protected_standby_link, working_link_info, ind->data.link_info);
+
+ result = bcmolt_oper_submit(partner.device_id, &add_standby.hdr);
+
+ //We have multiple acceptable return codes here:
+ // BCM_ERR_OK - Indicates new entry created.
+ // BCM_ERR_ALREADY - Indicates the entry already exists (possible after deregistration/reregistration).
+ PS_INFO("Mirroring add protected standby link result %d\n", result);
+
+ if ( (result == BCM_ERR_OK) || (result == BCM_ERR_ALREADY) )
+ {
+ bcmolt_epon_link_static_registration static_reg = {};
+ bcmolt_epon_link_key link_key = {};
+
+ link_key.epon_ni = (bcmolt_epon_ni)partner.pon_id;
+ link_key.mac_address = ind->key.mac_address;
+
+ //Setup operation key for api call.
+ BCMOLT_OPER_INIT(&static_reg, epon_link, static_registration, link_key);
+ BCMOLT_OPER_PROP_SET(&static_reg, epon_link, static_registration, laseron_time_tq, ind->data.link_info.onu_laser_on_time_tq);
+ BCMOLT_OPER_PROP_SET(&static_reg, epon_link, static_registration, laseroff_time_tq, ind->data.link_info.onu_laser_off_time_tq);
+ BCMOLT_OPER_PROP_SET(&static_reg, epon_link, static_registration, range_value_tq, ind->data.link_info.range_value_tq);
+ BCMOLT_OPER_PROP_SET(&static_reg, epon_link, static_registration, pending_grants, ind->data.link_info.pending_grants);
+ result = bcmolt_oper_submit(partner.device_id, &static_reg.hdr);
+ if (result != BCM_ERR_OK)
+ {
+ PS_ERR("Static registration failed %d\n", result);
+ }
+ else
+ {
+ PS_INFO("Static registration success\n");
+ }
+ }
+ else
+ {
+ PS_ERR("Add protected standby failed %d\n", result);
+ }
+ }
+ return BCM_ERR_OK;
+}
+
+bcmos_errno ps_process_ind_epon(const bcmolt_ps_pon *pon, const bcmolt_auto *ind)
+{
+ bcmos_errno err;
+ bcmolt_ps_global_cfg global_cfg;
+
+ err = bcmolt_ps_global_cfg_get(&global_cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ PS_INFO("ps_process_ind_epon Type: %d CFG_ID: %d\n", ind->hdr.obj_type, ind->hdr.subgroup);
+ switch (ind->hdr.obj_type)
+ {
+ case BCMOLT_OBJ_ID_EPON_NI:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_EPON_NI_AUTO_CFG_ID_NO_REPORTS:
+ return ps_handle_ni_no_reports(&global_cfg, pon, ((const bcmolt_epon_ni_no_reports *)ind));
+ default:
+ return BCM_ERR_OK;
+ }
+ case BCMOLT_OBJ_ID_EPON_LINK:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_EPON_LINK_AUTO_CFG_ID_MPCP_DISCOVERED:
+ return ps_handle_logical_link_discovered(&global_cfg, pon, ((const bcmolt_epon_link_mpcp_discovered *)ind));
+ default:
+ return BCM_ERR_OK;
+ }
+ default:
+ return BCM_ERR_OK;
+ }
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno ps_move_to_standby_epon(const bcmolt_ps_pon *pon)
+{
+ bcmolt_epon_ni_set_epon_ni_en_state set_pon_state;
+ bcmolt_epon_ni_key key = {};
+ key.epon_ni = pon->pon_id;
+
+ BCMOLT_OPER_INIT(&set_pon_state, epon_ni, set_epon_ni_en_state, key);
+ BCMOLT_OPER_PROP_SET(&set_pon_state, epon_ni, set_epon_ni_en_state, new_state, BCMOLT_EPON_NI_EN_STATE_PROTECTED_STANDBY);
+ return bcmolt_oper_submit(pon->device_id, &set_pon_state.hdr);
+}
+
+bcmos_errno ps_move_to_working_epon(const bcmolt_ps_pon *pon)
+{
+ bcmolt_epon_ni_set_epon_ni_en_state set_pon_state;
+ bcmolt_epon_ni_key key = {};
+ key.epon_ni = pon->pon_id;
+
+ BCMOLT_OPER_INIT(&set_pon_state, epon_ni, set_epon_ni_en_state, key);
+ BCMOLT_OPER_PROP_SET(&set_pon_state, epon_ni, set_epon_ni_en_state, new_state, BCMOLT_EPON_NI_EN_STATE_PROTECTED_WORKING);
+ return bcmolt_oper_submit(pon->device_id, &set_pon_state.hdr);
+}
\ No newline at end of file
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_gpon.c b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_gpon.c
new file mode 100644
index 0000000..f43e7fb
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_gpon.c
@@ -0,0 +1,878 @@
+/*
+<: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 <bcmolt_host_api.h>
+#include "bcmolt_user_appl_ps.h"
+#include "bcmolt_user_appl_ps_internal.h"
+
+static bcmos_errno ps_handle_ni_los(
+ const bcmolt_ps_global_cfg *global_cfg,
+ const bcmolt_ps_pon *pon,
+ const bcmolt_gpon_ni_los *ind)
+{
+ if (ind->data.status == BCMOLT_STATUS_OFF)
+ {
+ PS_INFO("<%d:%d> LoS alarm cleared\n", pon->device_id, pon->pon_id);
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("<%d:%d> LoS alarm raised\n", pon->device_id, pon->pon_id);
+
+ if (global_cfg->switch_condition == BCMOLT_PS_SWITCH_CONDITION_MANUAL)
+ {
+ /* don't react to the switch if we're in manual switch mode */
+ return BCM_ERR_OK;
+ }
+ else
+ {
+ return bcmolt_ps_switch_perform(pon);
+ }
+}
+
+static bcmos_errno ps_handle_ni_state_changed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_ni_state_change_completed *ind)
+{
+ bcmolt_gpon_ni_key key = { partner->pon_id };
+ bcmolt_gpon_ni_set_pon_state oper;
+
+ BCMOLT_OPER_INIT(&oper, gpon_ni, set_pon_state, key);
+
+ if (ind->data.previous_state == BCMOLT_PON_STATE_INACTIVE &&
+ ind->data.new_state == BCMOLT_PON_STATE_ACTIVE_WORKING)
+ {
+ PS_INFO("%s: activating PON <%d:%d>\n", __FUNCTION__, partner->device_id, key.pon_ni);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_STANDBY);
+ }
+ else if (ind->data.previous_state == BCMOLT_PON_STATE_ACTIVE_WORKING &&
+ ind->data.new_state == BCMOLT_PON_STATE_INACTIVE)
+ {
+ PS_INFO("%s: deactivating PON <%d:%d>\n", __FUNCTION__, partner->device_id, key.pon_ni);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_INACTIVE);
+ }
+ else
+ {
+ /* only change the standby PON's state if we're pre-provisioning the working PON
+ (the switchover is handled separately) */
+ return BCM_ERR_OK;
+ }
+
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_ni_traffic_resume(
+ const bcmolt_ps_pon *pon,
+ const bcmolt_gpon_ni_protection_switching_traffic_resume *ind)
+{
+ uint32_t after_us = bcmos_timestamp();
+ uint32_t before_us = ps_get_last_switch_start_time_us();
+ uint32_t time_taken_us = (after_us > before_us) ? after_us - before_us : before_us - after_us;
+ const char *traffic_resume_result_str = NULL;
+
+ switch (ind->data.result)
+ {
+ case BCMOLT_TRAFFIC_RESUME_RESULT_SUCCESS:
+ traffic_resume_result_str = "success";
+ break;
+ case BCMOLT_TRAFFIC_RESUME_RESULT_FAILURE:
+ traffic_resume_result_str = "failure";
+ break;
+ case BCMOLT_TRAFFIC_RESUME_RESULT_SUSPECTED_LOS:
+ traffic_resume_result_str = "suspected_los";
+ break;
+ default:
+ break;
+ }
+
+ PS_INFO(
+ "<%d:%d> Traffic resume %s. Time since start of switch: %d ms.\n",
+ pon->device_id,
+ pon->pon_id,
+ traffic_resume_result_str,
+ time_taken_us / 1000);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno ps_handle_ni_onus_ranged(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_ni_protection_switching_onus_ranged *ind)
+{
+ uint32_t i;
+ bcmos_errno last_err = BCM_ERR_OK;
+
+ /* apply all of the new ONU EQDs to the standby PON */
+ for (i = 0; i < ind->data.onus.len; i++)
+ {
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->data.onus.val[i].onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmos_errno err;
+
+ PS_INFO(
+ "%s: updating EQD <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.onus.val[i].eqd);
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, ranging_time, ind->data.onus.val[i].eqd);
+ err = bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ PS_ERR("EQD update failed: %s\n", bcmos_strerror(err));
+ last_err = err;
+ }
+ }
+
+ return last_err;
+}
+
+static bcmos_errno ps_handle_onu_ranging_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_ranging_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+
+ /* only react to success indications */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO(
+ "%s: updating EQD <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.eqd);
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, ranging_time, ind->data.eqd);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_key_exchange_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_key_exchange_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+
+ PS_INFO("%s: updating AES key <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, aes_encryption_key, ind->data.new_key);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_password_authentication_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_password_authentication_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+
+ /* only react to success indications */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: updating password <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, password, ind->data.password);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_activation_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_onu_activation_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_set_onu_state oper;
+
+ /* only react to success indications */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: activating <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, gpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_ACTIVE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_onu_deactivation_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_onu_deactivation_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_set_onu_state oper;
+
+ /* Note: we react to this indication whether or not the deactivation was successful. Failure indicates that the
+ * ONU didn't respond to the deactivation, but we still remove the ONU from OLT hardware so it must be mirrored. */
+
+ PS_INFO("%s: deactivating <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, gpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_INACTIVE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_onu_enable_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_onu_enable_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_set_onu_state oper;
+
+ /* don't try to mirror broadcast enable/disable */
+ if (key.onu_id == BCMOLT_GPON_ONU_ID_INVALID)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: enabling <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, gpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_ENABLE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_onu_disable_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_onu_disable_completed *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_set_onu_state oper;
+
+ /* don't try to mirror broadcast enable/disable */
+ if (key.onu_id == BCMOLT_GPON_ONU_ID_INVALID)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: disabling <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, gpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_DISABLE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static void ps_alarm_state_init(bcmolt_gpon_onu_alarm_state *alarm_state)
+{
+ alarm_state->losi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->lofi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->loami = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->dgi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->tiwi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->dowi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->sufi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->sfi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->sdi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->dfi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->loai = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->loki = BCMOLT_STATUS_NO_CHANGE;
+}
+
+static bcmos_errno ps_handle_onu_alarm(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_onu_alarm *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.losi = ind->data.onu_alarm.losi;
+ alarm_state.lofi = ind->data.onu_alarm.lofi;
+ alarm_state.loami = ind->data.onu_alarm.loami;
+
+ PS_INFO(
+ "%s: updating ONU alarms <%d:%d> ONU %d -> %d/%d/%d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ alarm_state.losi,
+ alarm_state.lofi,
+ alarm_state.loami);
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_dowi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_dowi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating DOWi <%d:%d> ONU %d -> %d (EQD %d)\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status,
+ ind->data.new_eqd);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.dowi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+
+ if (ind->data.alarm_status == BCMOLT_STATUS_ON)
+ {
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, ranging_time, ind->data.new_eqd);
+ }
+
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_sfi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_sfi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating SFI <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.sfi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_sdi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_sdi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating SDi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.sdi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_dfi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_dfi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating DFi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.dfi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_sufi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_sufi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating SUFi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.sufi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_loai(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_loai *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating LOAi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.loai = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_dgi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_dgi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating DGi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.dgi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_tiwi(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_tiwi *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating TIWi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.tiwi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_loki(const bcmolt_ps_pon *partner, const bcmolt_gpon_onu_loki *ind)
+{
+ bcmolt_gpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_gpon_onu_cfg cfg;
+ bcmolt_gpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating LOKi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.loki = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, gpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_omci_port_configuration_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_onu_omci_port_id_configuration_completed *ind)
+{
+ bcmolt_gpon_gem_port_key key = { partner->pon_id, ind->data.gem_port };
+ bcmolt_gpon_gem_port_set_state oper;
+
+ BCMOLT_OPER_INIT(&oper, gpon_gem_port, set_state, key);
+
+ if (ind->data.operation == BCMOLT_OMCI_PORT_ID_OPERATION_CONFIGURE)
+ {
+ /* don't react to unsuccessful activations */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: activating <%d:%d> GEM port ID %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.gem_port_id);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_gem_port, set_state, state, BCMOLT_GEM_PORT_OPERATION_ACTIVATE);
+ }
+ else
+ {
+ PS_INFO(
+ "%s: deactivating <%d:%d> GEM port ID %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.gem_port_id);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_gem_port, set_state, state, BCMOLT_GEM_PORT_OPERATION_DEACTIVATE);
+ }
+
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_mac_table_new_mac(
+ const bcmolt_ps_global_cfg *global_cfg,
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_iwf_mac_table_new_mac *ind)
+{
+ bcmolt_gpon_iwf_mac_table_key key = { partner->pon_id, ind->key.mac_address, ind->key.vlan };
+ bcmolt_gpon_iwf_mac_table_cfg cfg;
+
+ if (!global_cfg->mirror_mac_entries)
+ {
+ return BCM_ERR_OK; /* we are not configured to mirror MAC entries */
+ }
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_mac_table, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_mac_table, flow_id, ind->data.flow_id);
+
+ if (global_cfg->static_mac_entries)
+ {
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_mac_table, stat, BCMOS_TRUE);
+ }
+ else
+ {
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_mac_table, stat, BCMOS_FALSE);
+ }
+
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_mac_table_mac_aged(
+ const bcmolt_ps_global_cfg *global_cfg,
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_iwf_mac_table_mac_aged *ind)
+{
+ bcmolt_gpon_iwf_mac_table_key key = { partner->pon_id, ind->key.mac_address, ind->key.vlan };
+ bcmolt_gpon_iwf_mac_table_cfg cfg;
+
+ if (!global_cfg->mirror_mac_entries)
+ {
+ return BCM_ERR_OK; /* we are not configured to mirror MAC entries */
+ }
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_mac_table, key);
+ return bcmolt_cfg_clear(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_mac_table_mac_move(
+ const bcmolt_ps_global_cfg *global_cfg,
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_iwf_mac_table_mac_move *ind)
+{
+ bcmolt_gpon_iwf_mac_table_key key = { partner->pon_id, ind->key.mac_address, ind->key.vlan };
+ bcmolt_gpon_iwf_mac_table_cfg cfg;
+
+ if (!global_cfg->mirror_mac_entries)
+ {
+ return BCM_ERR_OK; /* we are not configured to mirror MAC entries */
+ }
+
+ BCMOLT_CFG_INIT(&cfg, gpon_iwf_mac_table, key);
+ BCMOLT_CFG_PROP_SET(&cfg, gpon_iwf_mac_table, flow_id, ind->data.new_flow_id);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_alloc_id_configuration_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_alloc_configuration_completed *ind)
+{
+ bcmolt_gpon_alloc_key key = { partner->pon_id, ind->key.alloc_id };
+ bcmolt_gpon_alloc_set_state oper;
+
+ BCMOLT_OPER_INIT(&oper, gpon_alloc, set_state, key);
+
+ if (ind->data.new_state == BCMOLT_ALLOC_STATE_ACTIVE)
+ {
+ /* don't react to unsuccessful activations */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: activating <%d:%d> alloc %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.alloc_id);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_alloc, set_state, state, BCMOLT_ALLOC_OPERATION_ACTIVATE);
+ }
+ else if (ind->data.new_state == BCMOLT_ALLOC_STATE_INACTIVE)
+ {
+ PS_INFO("%s: deactivating <%d:%d> alloc %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.alloc_id);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_alloc, set_state, state, BCMOLT_ALLOC_OPERATION_DEACTIVATE);
+ }
+ else /* ind->data.new_state is something else (e.g. unprovisioned, if the alloc was cleared) */
+ {
+ return BCM_ERR_OK;
+ }
+
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_gem_port_configuration_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_gpon_gem_port_configuration_completed *ind)
+{
+ bcmolt_gpon_gem_port_key key = { partner->pon_id, ind->key.gem_port_id };
+ bcmolt_gpon_gem_port_set_state oper;
+
+ BCMOLT_OPER_INIT(&oper, gpon_gem_port, set_state, key);
+
+ if (ind->data.new_state == BCMOLT_GPON_GEM_PORT_STATE_ACTIVE)
+ {
+ /* don't react to unsuccessful activations */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: activating <%d:%d> GEM port ID %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.gem_port_id);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_gem_port, set_state, state, BCMOLT_GEM_PORT_OPERATION_ACTIVATE);
+ }
+ else if (ind->data.new_state == BCMOLT_GPON_GEM_PORT_STATE_INACTIVE)
+ {
+ PS_INFO(
+ "%s: deactivating <%d:%d> GEM port ID %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.gem_port_id);
+ BCMOLT_OPER_PROP_SET(&oper, gpon_gem_port, set_state, state, BCMOLT_GEM_PORT_OPERATION_DEACTIVATE);
+ }
+ else /* ind->data.new_state is something else (e.g. unprovisioned, if the GEM port was cleared) */
+ {
+ return BCM_ERR_OK;
+ }
+
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+bcmos_errno ps_process_ind_gpon(const bcmolt_ps_pon *pon, const bcmolt_auto *ind)
+{
+ bcmos_errno err;
+ bcmolt_ps_pon_state state;
+ bcmolt_ps_pon partner;
+ bcmolt_ps_global_cfg global_cfg;
+
+ err = bcmolt_ps_global_cfg_get(&global_cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ if (global_cfg.mirror_mode != BCMOLT_PS_MIRROR_MODE_AUTO &&
+ ind->hdr.subgroup != BCMOLT_GPON_NI_AUTO_ID_LOS)
+ {
+ /* only handle most indications while in mirror mode 'auto', but perform a switchover on LoS in any case */
+ return BCM_ERR_OK;
+ }
+
+ err = bcmolt_ps_pon_state_get(pon, &state, &partner);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ if (state != BCMOLT_PS_PON_STATE_WORKING)
+ {
+ return BCM_ERR_OK; /* ignore indications from PONs that aren't protected working */
+ }
+
+ switch (ind->hdr.obj_type)
+ {
+ case BCMOLT_OBJ_ID_GPON_NI:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_GPON_NI_AUTO_ID_LOS:
+ return ps_handle_ni_los(&global_cfg, pon, ((const bcmolt_gpon_ni_los *)ind));
+ case BCMOLT_GPON_NI_AUTO_ID_STATE_CHANGE_COMPLETED:
+ return ps_handle_ni_state_changed(&partner, ((const bcmolt_gpon_ni_state_change_completed *)ind));
+ case BCMOLT_GPON_NI_AUTO_ID_PROTECTION_SWITCHING_TRAFFIC_RESUME:
+ return ps_handle_ni_traffic_resume(pon, ((const bcmolt_gpon_ni_protection_switching_traffic_resume *)ind));
+ case BCMOLT_GPON_NI_AUTO_ID_PROTECTION_SWITCHING_ONUS_RANGED:
+ return ps_handle_ni_onus_ranged(&partner, ((const bcmolt_gpon_ni_protection_switching_onus_ranged *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other NI indications */
+ }
+
+ case BCMOLT_OBJ_ID_GPON_ONU:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_GPON_ONU_AUTO_ID_RANGING_COMPLETED:
+ return ps_handle_onu_ranging_completed(&partner, ((const bcmolt_gpon_onu_ranging_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_KEY_EXCHANGE_COMPLETED:
+ return ps_handle_onu_key_exchange_completed(
+ &partner,
+ ((const bcmolt_gpon_onu_key_exchange_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_PASSWORD_AUTHENTICATION_COMPLETED:
+ return ps_handle_onu_password_authentication_completed(
+ &partner,
+ ((const bcmolt_gpon_onu_password_authentication_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_ONU_ACTIVATION_COMPLETED:
+ return ps_handle_onu_activation_completed(
+ &partner,
+ ((const bcmolt_gpon_onu_onu_activation_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_ONU_DEACTIVATION_COMPLETED:
+ return ps_handle_onu_deactivation_completed(
+ &partner,
+ ((const bcmolt_gpon_onu_onu_deactivation_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_ONU_ENABLE_COMPLETED:
+ return ps_handle_onu_enable_completed(&partner, ((const bcmolt_gpon_onu_onu_enable_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_ONU_DISABLE_COMPLETED:
+ return ps_handle_onu_disable_completed(&partner, ((const bcmolt_gpon_onu_onu_disable_completed *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_ONU_ALARM:
+ return ps_handle_onu_alarm(&partner, ((const bcmolt_gpon_onu_onu_alarm *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_DOWI:
+ return ps_handle_onu_dowi(&partner, ((const bcmolt_gpon_onu_dowi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_SFI:
+ return ps_handle_onu_sfi(&partner, ((const bcmolt_gpon_onu_sfi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_SDI:
+ return ps_handle_onu_sdi(&partner, ((const bcmolt_gpon_onu_sdi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_DFI:
+ return ps_handle_onu_dfi(&partner, ((const bcmolt_gpon_onu_dfi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_SUFI:
+ return ps_handle_onu_sufi(&partner, ((const bcmolt_gpon_onu_sufi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_LOAI:
+ return ps_handle_onu_loai(&partner, ((const bcmolt_gpon_onu_loai *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_DGI:
+ return ps_handle_onu_dgi(&partner, ((const bcmolt_gpon_onu_dgi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_TIWI:
+ return ps_handle_onu_tiwi(&partner, ((const bcmolt_gpon_onu_tiwi *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_LOKI:
+ return ps_handle_onu_loki(&partner, ((const bcmolt_gpon_onu_loki *)ind));
+ case BCMOLT_GPON_ONU_AUTO_ID_OMCI_PORT_ID_CONFIGURATION_COMPLETED:
+ return ps_handle_onu_omci_port_configuration_completed(
+ &partner,
+ ((const bcmolt_gpon_onu_omci_port_id_configuration_completed *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other ONU indications */
+ }
+
+ case BCMOLT_OBJ_ID_GPON_ALLOC:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_GPON_ALLOC_AUTO_ID_CONFIGURATION_COMPLETED:
+ return ps_handle_alloc_id_configuration_completed(
+ &partner,
+ ((const bcmolt_gpon_alloc_configuration_completed *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other alloc indications */
+ }
+
+ case BCMOLT_OBJ_ID_GPON_GEM_PORT:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_GPON_GEM_PORT_AUTO_ID_CONFIGURATION_COMPLETED:
+ return ps_handle_gem_port_configuration_completed(
+ &partner,
+ ((const bcmolt_gpon_gem_port_configuration_completed *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other GEM port indications */
+ }
+
+ case BCMOLT_OBJ_ID_GPON_IWF_MAC_TABLE:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_GPON_IWF_MAC_TABLE_AUTO_CFG_ID_NEW_MAC:
+ return ps_handle_mac_table_new_mac(
+ &global_cfg,
+ &partner,
+ (const bcmolt_gpon_iwf_mac_table_new_mac *)ind);
+ case BCMOLT_GPON_IWF_MAC_TABLE_AUTO_CFG_ID_MAC_AGED:
+ return ps_handle_mac_table_mac_aged(
+ &global_cfg,
+ &partner,
+ (const bcmolt_gpon_iwf_mac_table_mac_aged *)ind);
+ case BCMOLT_GPON_IWF_MAC_TABLE_AUTO_CFG_ID_MAC_MOVE:
+ return ps_handle_mac_table_mac_move(
+ &global_cfg,
+ &partner,
+ (const bcmolt_gpon_iwf_mac_table_mac_move *)ind);
+ default:
+ return BCM_ERR_OK; /* silently ignore all other mac table indications */
+ }
+
+ default:
+ return BCM_ERR_NOT_SUPPORTED; /* someone else needs to handle this indication */
+ }
+}
+
+bcmos_errno ps_move_to_standby_gpon(const bcmolt_ps_pon *pon)
+{
+ bcmolt_gpon_ni_set_pon_state set_pon_state;
+ bcmolt_gpon_ni_key key = { (bcmolt_gpon_ni)pon->pon_id };
+
+ BCMOLT_OPER_INIT(&set_pon_state, gpon_ni, set_pon_state, key);
+ BCMOLT_OPER_PROP_SET(&set_pon_state, gpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_STANDBY);
+ return bcmolt_oper_submit(pon->device_id, &set_pon_state.hdr);
+}
+
+bcmos_errno ps_move_to_working_gpon(const bcmolt_ps_pon *pon)
+{
+ bcmolt_gpon_ni_set_pon_state set_pon_state;
+ bcmolt_gpon_ni_key key = { (bcmolt_gpon_ni)pon->pon_id };
+
+ BCMOLT_OPER_INIT(&set_pon_state, gpon_ni, set_pon_state, key);
+ BCMOLT_OPER_PROP_SET(&set_pon_state, gpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_WORKING);
+ return bcmolt_oper_submit(pon->device_id, &set_pon_state.hdr);
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_internal.h b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_internal.h
new file mode 100644
index 0000000..7c3f7d1
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_internal.h
@@ -0,0 +1,66 @@
+/*
+<: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.
+
+:>
+*/
+
+/* This file is for internal use within the protection switching user application -
+ no functions here should be called from anywhere else! */
+
+#ifndef _BCMOLT_USER_APPL_PS_INTERNAL_H_
+#define _BCMOLT_USER_APPL_PS_INTERNAL_H_
+
+#include <bcm_dev_log.h>
+#include "bcmolt_user_appl_ps.h"
+
+#ifdef ENABLE_LOG
+dev_log_id ps_get_log_id(void);
+#define PS_ERR(...) BCM_LOG(ERROR, ps_get_log_id(), __VA_ARGS__)
+#define PS_INFO(...) BCM_LOG(INFO, ps_get_log_id(), __VA_ARGS__)
+#else
+#define PS_ERR(...) bcmos_printf("[PS ERROR] " __VA_ARGS__)
+#define PS_INFO(...) bcmos_printf("[PS INFO] " __VA_ARGS__)
+#endif
+
+/* Helper functions */
+uint32_t ps_get_last_switch_start_time_us(void);
+
+/* GPON-specific functionality */
+bcmos_errno ps_process_ind_gpon(const bcmolt_ps_pon *pon, const bcmolt_auto *ind);
+bcmos_errno ps_move_to_standby_gpon(const bcmolt_ps_pon *pon);
+bcmos_errno ps_move_to_working_gpon(const bcmolt_ps_pon *pon);
+
+/* EPON-specific functionality */
+bcmos_errno ps_process_ind_epon(const bcmolt_ps_pon *pon, const bcmolt_auto *ind);
+bcmos_errno ps_move_to_standby_epon(const bcmolt_ps_pon *pon);
+bcmos_errno ps_move_to_working_epon(const bcmolt_ps_pon *pon);
+
+/* XGPON-specific functionality */
+bcmos_errno ps_process_ind_xgpon(const bcmolt_ps_pon *pon, const bcmolt_auto *ind);
+bcmos_errno ps_move_to_standby_xgpon(const bcmolt_ps_pon *pon);
+bcmos_errno ps_move_to_working_xgpon(const bcmolt_ps_pon *pon);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_xgpon.c b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_xgpon.c
new file mode 100644
index 0000000..faa167a
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/protection_switching/bcmolt_user_appl_ps_xgpon.c
@@ -0,0 +1,655 @@
+/*
+<: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 <bcmolt_host_api.h>
+#include "bcmolt_user_appl_ps.h"
+#include "bcmolt_user_appl_ps_internal.h"
+
+static bcmos_errno ps_handle_ni_los(
+ const bcmolt_ps_global_cfg *global_cfg,
+ const bcmolt_ps_pon *pon,
+ const bcmolt_xgpon_ni_los *ind)
+{
+ if (ind->data.status == BCMOLT_STATUS_OFF)
+ {
+ PS_INFO("<%d:%d> LoS alarm cleared\n", pon->device_id, pon->pon_id);
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("<%d:%d> LoS alarm raised\n", pon->device_id, pon->pon_id);
+
+ if (global_cfg->switch_condition == BCMOLT_PS_SWITCH_CONDITION_MANUAL)
+ {
+ /* don't react to the switch if we're in manual switch mode */
+ return BCM_ERR_OK;
+ }
+ else
+ {
+ return bcmolt_ps_switch_perform(pon);
+ }
+}
+
+static bcmos_errno ps_handle_ni_state_changed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_ni_state_change_completed *ind)
+{
+ bcmolt_xgpon_ni_key key = { partner->pon_id };
+ bcmolt_xgpon_ni_set_pon_state oper;
+
+ BCMOLT_OPER_INIT(&oper, xgpon_ni, set_pon_state, key);
+
+ if (ind->data.previous_state == BCMOLT_PON_STATE_INACTIVE &&
+ ind->data.new_state == BCMOLT_PON_STATE_ACTIVE_WORKING)
+ {
+ PS_INFO("%s: activating PON <%d:%d>\n", __FUNCTION__, partner->device_id, key.pon_ni);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_STANDBY);
+ }
+ else if (ind->data.previous_state == BCMOLT_PON_STATE_ACTIVE_WORKING &&
+ ind->data.new_state == BCMOLT_PON_STATE_INACTIVE)
+ {
+ PS_INFO("%s: deactivating PON <%d:%d>\n", __FUNCTION__, partner->device_id, key.pon_ni);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_INACTIVE);
+ }
+ else
+ {
+ /* only change the standby PON's state if we're pre-provisioning the working PON
+ (the switchover is handled separately) */
+ return BCM_ERR_OK;
+ }
+
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_ni_traffic_resume(
+ const bcmolt_ps_pon *pon,
+ const bcmolt_xgpon_ni_protection_switching_traffic_resume *ind)
+{
+ uint32_t after_us = bcmos_timestamp();
+ uint32_t before_us = ps_get_last_switch_start_time_us();
+ uint32_t time_taken_us = (after_us > before_us) ? after_us - before_us : before_us - after_us;
+ const char *traffic_resume_result_str = NULL;
+
+ switch (ind->data.result)
+ {
+ case BCMOLT_TRAFFIC_RESUME_RESULT_SUCCESS:
+ traffic_resume_result_str = "success";
+ break;
+ case BCMOLT_TRAFFIC_RESUME_RESULT_FAILURE:
+ traffic_resume_result_str = "failure";
+ break;
+ case BCMOLT_TRAFFIC_RESUME_RESULT_SUSPECTED_LOS:
+ traffic_resume_result_str = "suspected_los";
+ break;
+ default:
+ break;
+ }
+
+ PS_INFO(
+ "<%d:%d> Traffic resume %s. Time since start of switch: %d ms.\n",
+ pon->device_id,
+ pon->pon_id,
+ traffic_resume_result_str,
+ time_taken_us / 1000);
+
+ return BCM_ERR_OK;
+}
+
+static bcmos_errno ps_handle_ni_onus_ranged(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_ni_protection_switching_onus_ranged *ind)
+{
+ uint32_t i;
+ bcmos_errno last_err = BCM_ERR_OK;
+
+ /* apply all of the new ONU EQDs to the standby PON */
+ for (i = 0; i < ind->data.onus.len; i++)
+ {
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->data.onus.val[i].onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmos_errno err;
+
+ PS_INFO(
+ "%s: updating EQD <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.onus.val[i].eqd);
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, ranging_time, ind->data.onus.val[i].eqd);
+ err = bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ PS_ERR("EQD update failed: %s\n", bcmos_strerror(err));
+ last_err = err;
+ }
+ }
+
+ return last_err;
+}
+
+static bcmos_errno ps_handle_onu_ranging_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_onu_ranging_completed *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+
+ /* only react to success indications */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO(
+ "%s: updating EQD <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.eqd);
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, ranging_time, ind->data.eqd);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_key_exchange_completed(
+ const bcmolt_ps_pon *pon,
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_onu_key_exchange_completed *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+
+ PS_INFO("%s: updating AES key <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, current_encryption_key, ind->data.new_key);
+ return bcmolt_cfg_set(pon->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_activation_completed(
+ const bcmolt_ps_pon *pon,
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_onu_onu_activation_completed *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_set_onu_state oper;
+ bcmos_errno err;
+
+ /* only react to success indications */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO(
+ "%s: provisioning registration ID / keys for newly-activated ONU <%d:%d> ONU %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id);
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, registration_id, ind->data.registration_id);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, registration_encryption_keys, ind->data.registration_encryption_keys);
+ err = bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ PS_INFO("%s: activating <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, xgpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_ACTIVE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_onu_deactivation_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_onu_onu_deactivation_completed *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_set_onu_state oper;
+
+ /* Note: we react to this indication whether or not the deactivation was successful. Failure indicates that the
+ * ONU didn't respond to the deactivation, but we still remove the ONU from OLT hardware so it must be mirrored. */
+
+ PS_INFO("%s: deactivating <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, xgpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_INACTIVE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_onu_enable_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_onu_onu_enable_completed *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_set_onu_state oper;
+
+ /* don't try to mirror broadcast enable/disable */
+ if (key.onu_id == BCMOLT_XGPON_ONU_ID_INVALID)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: enabling <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, xgpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_ENABLE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static bcmos_errno ps_handle_onu_disable_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_onu_onu_disable_completed *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_set_onu_state oper;
+
+ /* don't try to mirror broadcast enable/disable */
+ if (key.onu_id == BCMOLT_XGPON_ONU_ID_INVALID)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: disabling <%d:%d> ONU %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.onu_id);
+
+ BCMOLT_OPER_INIT(&oper, xgpon_onu, set_onu_state, key);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_onu, set_onu_state, onu_state, BCMOLT_ONU_OPERATION_DISABLE);
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+static void ps_alarm_state_init(bcmolt_xgpon_onu_alarm_state *alarm_state)
+{
+ alarm_state->losi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->lobi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->lopci = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->lopci_mic_error = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->looci = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->tiwi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->dowi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->sufi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->sfi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->sdi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->dfi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->dgi = BCMOLT_STATUS_NO_CHANGE;
+ alarm_state->pqsi = BCMOLT_STATUS_NO_CHANGE;
+}
+
+static bcmos_errno ps_handle_onu_alarm(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_onu_alarm *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.losi = ind->data.onu_alarm.losi;
+ alarm_state.lobi = ind->data.onu_alarm.lobi;
+ alarm_state.lopci = ind->data.onu_alarm.lopci_miss;
+ alarm_state.lopci_mic_error = ind->data.onu_alarm.lopci_mic_error;
+
+ PS_INFO(
+ "%s: updating ONU alarms <%d:%d> ONU %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id);
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_dowi(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_dowi *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating DOWi <%d:%d> ONU %d -> %d (EQD %d)\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status,
+ ind->data.new_eqd);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.dowi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+
+ if (ind->data.alarm_status == BCMOLT_STATUS_ON)
+ {
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, ranging_time, ind->data.new_eqd);
+ }
+
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_sfi(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_sfi *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating SFI <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.sfi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_sdi(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_sdi *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating SDi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.sdi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_dfi(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_dfi *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating DFi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.dfi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_sufi(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_sufi *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating SUFi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.sufi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_tiwi(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_tiwi *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating TIWi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.tiwi = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_onu_looci(const bcmolt_ps_pon *partner, const bcmolt_xgpon_onu_looci *ind)
+{
+ bcmolt_xgpon_onu_key key = { partner->pon_id, ind->key.onu_id };
+ bcmolt_xgpon_onu_cfg cfg;
+ bcmolt_xgpon_onu_alarm_state alarm_state;
+
+ PS_INFO(
+ "%s: updating LOOCi <%d:%d> ONU %d -> %d\n",
+ __FUNCTION__,
+ partner->device_id,
+ key.pon_ni,
+ key.onu_id,
+ ind->data.alarm_status);
+
+ ps_alarm_state_init(&alarm_state);
+ alarm_state.looci = ind->data.alarm_status;
+
+ BCMOLT_CFG_INIT(&cfg, xgpon_onu, key);
+ BCMOLT_CFG_PROP_SET(&cfg, xgpon_onu, alarm_state, alarm_state);
+ return bcmolt_cfg_set(partner->device_id, &cfg.hdr);
+}
+
+static bcmos_errno ps_handle_alloc_id_configuration_completed(
+ const bcmolt_ps_pon *partner,
+ const bcmolt_xgpon_alloc_configuration_completed *ind)
+{
+ bcmolt_xgpon_alloc_key key = { partner->pon_id, ind->key.alloc_id };
+ bcmolt_xgpon_alloc_set_state oper;
+
+ BCMOLT_OPER_INIT(&oper, xgpon_alloc, set_state, key);
+
+ if (ind->data.new_state == BCMOLT_ALLOC_STATE_ACTIVE)
+ {
+ /* only react to success indications */
+ if (ind->data.status != BCMOLT_RESULT_SUCCESS)
+ {
+ return BCM_ERR_OK;
+ }
+
+ PS_INFO("%s: activating <%d:%d> alloc %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.alloc_id);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_alloc, set_state, state, BCMOLT_ALLOC_OPERATION_ACTIVATE);
+ }
+ else if (ind->data.new_state == BCMOLT_ALLOC_STATE_INACTIVE)
+ {
+ PS_INFO("%s: deactivating <%d:%d> alloc %d\n", __FUNCTION__, partner->device_id, key.pon_ni, key.alloc_id);
+ BCMOLT_OPER_PROP_SET(&oper, xgpon_alloc, set_state, state, BCMOLT_ALLOC_OPERATION_DEACTIVATE);
+ }
+ else /* ind->data.new_state is something else (e.g. unprovisioned, if the alloc was cleared) */
+ {
+ return BCM_ERR_OK;
+ }
+
+ return bcmolt_oper_submit(partner->device_id, &oper.hdr);
+}
+
+bcmos_errno ps_process_ind_xgpon(const bcmolt_ps_pon *pon, const bcmolt_auto *ind)
+{
+ bcmos_errno err;
+ bcmolt_ps_pon_state state;
+ bcmolt_ps_pon partner;
+ bcmolt_ps_global_cfg global_cfg;
+
+ err = bcmolt_ps_global_cfg_get(&global_cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ if (global_cfg.mirror_mode != BCMOLT_PS_MIRROR_MODE_AUTO &&
+ ind->hdr.subgroup != BCMOLT_XGPON_NI_AUTO_ID_LOS)
+ {
+ /* only handle most indications while in mirror mode 'auto', but perform a switchover on LoS in any case */
+ return BCM_ERR_OK;
+ }
+
+ err = bcmolt_ps_pon_state_get(pon, &state, &partner);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ if (state != BCMOLT_PS_PON_STATE_WORKING)
+ {
+ return BCM_ERR_OK; /* ignore indications from PONs that aren't protected working */
+ }
+
+ switch (ind->hdr.obj_type)
+ {
+ case BCMOLT_OBJ_ID_XGPON_NI:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_XGPON_NI_AUTO_ID_LOS:
+ return ps_handle_ni_los(&global_cfg, pon, ((const bcmolt_xgpon_ni_los *)ind));
+ case BCMOLT_XGPON_NI_AUTO_ID_STATE_CHANGE_COMPLETED:
+ return ps_handle_ni_state_changed(&partner, ((const bcmolt_xgpon_ni_state_change_completed *)ind));
+ case BCMOLT_XGPON_NI_AUTO_ID_PROTECTION_SWITCHING_TRAFFIC_RESUME:
+ return ps_handle_ni_traffic_resume(pon, ((const bcmolt_xgpon_ni_protection_switching_traffic_resume *)ind));
+ case BCMOLT_XGPON_NI_AUTO_ID_PROTECTION_SWITCHING_ONUS_RANGED:
+ return ps_handle_ni_onus_ranged(&partner, ((const bcmolt_xgpon_ni_protection_switching_onus_ranged *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other NI indications */
+ }
+
+ case BCMOLT_OBJ_ID_XGPON_ONU:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_XGPON_ONU_AUTO_ID_RANGING_COMPLETED:
+ return ps_handle_onu_ranging_completed(&partner, ((const bcmolt_xgpon_onu_ranging_completed *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_KEY_EXCHANGE_COMPLETED:
+ return ps_handle_onu_key_exchange_completed(
+ pon,
+ &partner,
+ ((const bcmolt_xgpon_onu_key_exchange_completed *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_ACTIVATION_COMPLETED:
+ return ps_handle_onu_activation_completed(
+ pon,
+ &partner,
+ ((const bcmolt_xgpon_onu_onu_activation_completed *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_DEACTIVATION_COMPLETED:
+ return ps_handle_onu_deactivation_completed(
+ &partner,
+ ((const bcmolt_xgpon_onu_onu_deactivation_completed *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_ENABLE_COMPLETED:
+ return ps_handle_onu_enable_completed(&partner, ((const bcmolt_xgpon_onu_onu_enable_completed *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_DISABLE_COMPLETED:
+ return ps_handle_onu_disable_completed(&partner, ((const bcmolt_xgpon_onu_onu_disable_completed *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_ONU_ALARM:
+ return ps_handle_onu_alarm(&partner, ((const bcmolt_xgpon_onu_onu_alarm *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_DOWI:
+ return ps_handle_onu_dowi(&partner, ((const bcmolt_xgpon_onu_dowi *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_SFI:
+ return ps_handle_onu_sfi(&partner, ((const bcmolt_xgpon_onu_sfi *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_SDI:
+ return ps_handle_onu_sdi(&partner, ((const bcmolt_xgpon_onu_sdi *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_DFI:
+ return ps_handle_onu_dfi(&partner, ((const bcmolt_xgpon_onu_dfi *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_SUFI:
+ return ps_handle_onu_sufi(&partner, ((const bcmolt_xgpon_onu_sufi *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_TIWI:
+ return ps_handle_onu_tiwi(&partner, ((const bcmolt_xgpon_onu_tiwi *)ind));
+ case BCMOLT_XGPON_ONU_AUTO_ID_LOOCI:
+ return ps_handle_onu_looci(&partner, ((const bcmolt_xgpon_onu_looci *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other ONU indications */
+ }
+
+ case BCMOLT_OBJ_ID_XGPON_ALLOC:
+ switch (ind->hdr.subgroup)
+ {
+ case BCMOLT_XGPON_ALLOC_AUTO_ID_CONFIGURATION_COMPLETED:
+ return ps_handle_alloc_id_configuration_completed(
+ &partner,
+ ((const bcmolt_xgpon_alloc_configuration_completed *)ind));
+ default:
+ return BCM_ERR_OK; /* silently ignore all other alloc indications */
+ }
+
+ default:
+ return BCM_ERR_NOT_SUPPORTED; /* someone else needs to handle this indication */
+ }
+}
+
+bcmos_errno ps_move_to_standby_xgpon(const bcmolt_ps_pon *pon)
+{
+ bcmolt_xgpon_ni_set_pon_state set_pon_state;
+ bcmolt_xgpon_ni_key key = { (bcmolt_xgpon_ni)pon->pon_id };
+
+ BCMOLT_OPER_INIT(&set_pon_state, xgpon_ni, set_pon_state, key);
+ BCMOLT_OPER_PROP_SET(&set_pon_state, xgpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_STANDBY);
+ return bcmolt_oper_submit(pon->device_id, &set_pon_state.hdr);
+}
+
+bcmos_errno ps_move_to_working_xgpon(const bcmolt_ps_pon *pon)
+{
+ bcmolt_xgpon_ni_set_pon_state set_pon_state;
+ bcmolt_xgpon_ni_key key = { (bcmolt_xgpon_ni)pon->pon_id };
+
+ BCMOLT_OPER_INIT(&set_pon_state, xgpon_ni, set_pon_state, key);
+ BCMOLT_OPER_PROP_SET(&set_pon_state, xgpon_ni, set_pon_state, pon_state, BCMOLT_PON_OPERATION_ACTIVE_WORKING);
+ return bcmolt_oper_submit(pon->device_id, &set_pon_state.hdr);
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/remote_logger/Makefile b/bcm68620_release/release/host_reference/user_appl/remote_logger/Makefile
new file mode 100644
index 0000000..61e7df6
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/remote_logger/Makefile
@@ -0,0 +1,15 @@
+# User application to copy log prints from the device to a local file on the host
+
+ifeq ("$(ENABLE_LOG)", "y")
+ ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_user_appl_remote_logger
+ MOD_TYPE = lib
+ MOD_DEPS = host_api dev_log
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+ srcs = bcmolt_user_appl_remote_logger.c \
+ bcmolt_user_appl_remote_logger_cli.c
+ endif
+endif
+
diff --git a/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger.c b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger.c
new file mode 100644
index 0000000..8cbeddf
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger.c
@@ -0,0 +1,232 @@
+/*
+<: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 <bcmolt_model_types.h>
+#include "bcmolt_user_appl_remote_logger.h"
+
+static struct
+{
+ FILE *file_handle;
+ uint64_t file_size;
+ bcmolt_remote_logger_cfg cfg;
+ bcmos_task task;
+ bcmos_timer timer;
+ dev_log_id log_id;
+} remote_logger[BCMTR_MAX_OLTS] = {};
+
+static bcmos_timer_rc remote_logger_timer_handler(bcmos_timer *timer, long data)
+{
+ bcmolt_devid device = (bcmolt_devid)data;
+ int written;
+ bcmos_errno err;
+ bcmolt_logger_cfg cfg;
+ bcmolt_logger_key key = { .file_id = BCMOLT_LOG_FILE_ID_DDR };
+
+ BCMOLT_CFG_INIT(&cfg, logger, key);
+ BCMOLT_CFG_PROP_GET(&cfg, logger, buffer);
+ err = bcmolt_cfg_get(device, &cfg.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, remote_logger[device].log_id, "Logger get API returned '%s' (%d)\n", bcmos_strerror(err), err);
+ bcmolt_remote_logger_appl_stop(device);
+ return BCMOS_TIMER_OK;
+ }
+
+ written = fprintf(
+ remote_logger[device].file_handle,
+ "%.*s",
+ (int)sizeof(cfg.data.buffer.buff),
+ cfg.data.buffer.buff);
+ if (written < 0)
+ {
+ BCM_LOG(ERROR, remote_logger[device].log_id, "File write returned %d\n", written);
+ bcmolt_remote_logger_appl_stop(device);
+ return BCMOS_TIMER_OK;
+ }
+
+ fflush(remote_logger[device].file_handle);
+
+ /* Check to see if we will exceed the max file size next time we write a chunk. */
+ remote_logger[device].file_size += written;
+ if (remote_logger[device].file_size + sizeof(cfg.data.buffer.buff) > remote_logger[device].cfg.max_file_size)
+ {
+ switch (remote_logger[device].cfg.max_file_size_reached_behavior)
+ {
+ case BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_STOP:
+ BCM_LOG(INFO, remote_logger[device].log_id, "Max file size reached - remote logger stopped\n");
+ bcmolt_remote_logger_appl_stop(device);
+ return BCMOS_TIMER_OK;
+ case BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_CLEAR:
+ BCM_LOG(INFO, remote_logger[device].log_id, "Max file size reached - log file cleared\n");
+ /* the easiest way to clear the file is to close/re-open it */
+ fclose(remote_logger[device].file_handle);
+ remote_logger[device].file_handle = fopen(remote_logger[device].cfg.filename, "w");
+ BUG_ON(remote_logger[device].file_handle == NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cfg.data.buffer.msg_to_read == 0)
+ {
+ /* There are no more messages to read right now, wait for the entire polling interval. */
+ bcmos_timer_start(
+ &remote_logger[device].timer,
+ remote_logger[device].cfg.polling_period_ms * 1000); /* ms to us */
+ }
+ else
+ {
+ /* There are more messages to read - wait for only the 'subsequent delay' time. */
+ bcmos_timer_start(
+ &remote_logger[device].timer,
+ remote_logger[device].cfg.subsequent_delay_ms * 1000); /* ms to us */
+ }
+
+ return BCMOS_TIMER_OK;
+}
+
+void bcmolt_remote_logger_appl_init()
+{
+ static bcmos_task_parm task_params =
+ {
+ .priority = TASK_PRIORITY_USER_APPL_REMOTE_LOGGER,
+ .core = BCMOS_CPU_CORE_ANY, /* No CPU affinity */
+ .init_handler = NULL
+ };
+ static bcmos_module_parm module_params =
+ {
+ .qparm = { .size = 1 } /* just the timer message */
+ };
+ static bcmos_timer_parm timer_params =
+ {
+ .periodic = BCMOS_FALSE, /* we will re-enable the timer after each tick */
+ .handler = remote_logger_timer_handler
+ };
+
+ bcmos_errno err;
+ bcmolt_devid device;
+
+ for (device = 0; device < BCMTR_MAX_OLTS; device++)
+ {
+ bcmos_module_id module_id = BCMOS_MODULE_ID_USER_APPL_REMOTE_LOGGER_DEV0 + device;
+ snprintf(
+ remote_logger[device].task.name,
+ sizeof(remote_logger[device].task.name),
+ "user_appl_remote_logger%u",
+ device);
+
+ task_params.name = remote_logger[device].task.name;
+ module_params.qparm.name = remote_logger[device].task.name;
+ timer_params.owner = module_id;
+ timer_params.name = remote_logger[device].task.name;
+ timer_params.data = (long)device;
+
+ err = bcmos_task_create(&remote_logger[device].task, &task_params);
+ BUG_ON(err != BCM_ERR_OK);
+
+ err = bcmos_module_create(module_id, &remote_logger[device].task, &module_params);
+ BUG_ON(err != BCM_ERR_OK);
+
+ err = bcmos_timer_create(&remote_logger[device].timer, &timer_params);
+ BUG_ON(err != BCM_ERR_OK);
+
+ remote_logger[device].log_id =
+ bcm_dev_log_id_register(remote_logger[device].task.name, DEV_LOG_LEVEL_WARNING, DEV_LOG_ID_TYPE_BOTH);
+ }
+}
+
+bcmos_errno bcmolt_remote_logger_appl_start(bcmolt_devid device)
+{
+ bcmos_errno err;
+ bcmolt_logger_cfg cfg;
+ bcmolt_logger_key key = { .file_id = BCMOLT_LOG_FILE_ID_DDR };
+
+ if (bcmolt_remote_logger_appl_is_running(device))
+ {
+ BCM_LOG(ERROR, remote_logger[device].log_id, "Remote logger application is already running.\n");
+ return BCM_ERR_ALREADY;
+ }
+
+ /* configure DDR log file to clear on read so we can always stay up-to-date */
+ BCMOLT_CFG_INIT(&cfg, logger, key);
+ BCMOLT_CFG_PROP_SET(&cfg, logger, enable_log, BCMOS_TRUE);
+ BCMOLT_CFG_PROP_SET(&cfg, logger, clear_after_read, BCMOS_TRUE);
+ err = bcmolt_cfg_set(0, &cfg.hdr);
+ if (err != BCM_ERR_OK)
+ {
+ BCM_LOG(ERROR, remote_logger[device].log_id, "Logger set API returned '%s' (%d)\n", bcmos_strerror(err), err);
+ return err;
+ }
+
+ remote_logger[device].file_handle = fopen(remote_logger[device].cfg.filename, "w");
+ if (remote_logger[device].file_handle == NULL)
+ {
+ BCM_LOG(ERROR, remote_logger[device].log_id, "Could not open file '%s'\n", remote_logger[device].cfg.filename);
+ return BCM_ERR_IO;
+ }
+ remote_logger[device].file_size = 0;
+
+ bcmos_timer_start(&remote_logger[device].timer, remote_logger[device].cfg.polling_period_ms * 1000); /* ms to us */
+ return BCM_ERR_OK;
+}
+
+bcmos_errno bcmolt_remote_logger_appl_stop(bcmolt_devid device)
+{
+ if (!bcmolt_remote_logger_appl_is_running(device))
+ {
+ BCM_LOG(ERROR, remote_logger[device].log_id, "Remote logger application is not running.\n");
+ return BCM_ERR_ALREADY;
+ }
+
+ bcmos_timer_stop(&remote_logger[device].timer);
+ fclose(remote_logger[device].file_handle);
+ remote_logger[device].file_handle = NULL;
+ return BCM_ERR_OK;
+}
+
+bcmos_bool bcmolt_remote_logger_appl_is_running(bcmolt_devid device)
+{
+ return bcmos_timer_is_running(&remote_logger[device].timer);
+}
+
+bcmos_errno bcmolt_remote_logger_appl_cfg_get(bcmolt_devid device, bcmolt_remote_logger_cfg *cfg)
+{
+ *cfg = remote_logger[device].cfg;
+ return BCM_ERR_OK;
+}
+
+bcmos_errno bcmolt_remote_logger_appl_cfg_update(bcmolt_devid device, const bcmolt_remote_logger_cfg *cfg)
+{
+ remote_logger[device].cfg = *cfg;
+ return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger.h b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger.h
new file mode 100644
index 0000000..75a22fd
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger.h
@@ -0,0 +1,89 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_USER_APPL_REMOTE_LOGGER_H_
+#define _BCMOLT_USER_APPL_REMOTE_LOGGER_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_msg.h>
+
+#define BCMOLT_REMOTE_LOGGER_FILENAME_MAX_LEN 128
+
+/** Possible actions to take when the max log file size is reached. */
+typedef enum
+{
+ BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_STOP, /**< Stop the remote logger application. */
+ BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_CLEAR, /**< Clear all content from the file and continue. */
+} bcmolt_remote_logger_file_size_reached_behavior;
+
+/** Configuration for the remote logger application. */
+typedef struct
+{
+ uint32_t polling_period_ms; /**< Time to wait in between polling API calls. */
+ uint32_t subsequent_delay_ms; /**< Minumum required time to wait in between susequent API calls. */
+ uint32_t max_file_size; /**< Maximum size for the output file in bytes. */
+
+ /** Output file full path. */
+ char filename[BCMOLT_REMOTE_LOGGER_FILENAME_MAX_LEN];
+
+ /** Behavior when the max file size is reached. */
+ bcmolt_remote_logger_file_size_reached_behavior max_file_size_reached_behavior;
+} bcmolt_remote_logger_cfg;
+
+/** Initialize the remote logger application (this should be called as part of application startup). */
+void bcmolt_remote_logger_appl_init(void);
+
+/** Start the remote logger application.
+ * \return BCM_ERR_OK if the application was started successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_remote_logger_appl_start(bcmolt_devid device);
+
+/** Stop the remote logger application.
+ * \return BCM_ERR_OK if the application was stopped successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_remote_logger_appl_stop(bcmolt_devid device);
+
+/** Query whether the remote logger application is currently running.
+ * \return BCMOS_TRUE if the application is running, BCMOS_FALSE otherwise.
+ */
+bcmos_bool bcmolt_remote_logger_appl_is_running(bcmolt_devid device);
+
+/** Get the configuration of the remote logger application.
+ * \param[out] cfg Remote logger configuration.
+ * \return BCM_ERR_OK if the configuration was retrieved successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_remote_logger_appl_cfg_get(bcmolt_devid device, bcmolt_remote_logger_cfg *cfg);
+
+/** Change the configuration of the remote logger application.
+ * \param[in] cfg Remote logger configuration.
+ * \return BCM_ERR_OK if the configuration was updated successfully, <0 otherwise.
+ */
+bcmos_errno bcmolt_remote_logger_appl_cfg_update(bcmolt_devid device, const bcmolt_remote_logger_cfg *cfg);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger_cli.c b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger_cli.c
new file mode 100644
index 0000000..86c4fd5
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger_cli.c
@@ -0,0 +1,257 @@
+/*
+<: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 <bcmcli.h>
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include "bcmolt_user_appl_remote_logger_cli.h"
+#include "bcmolt_user_appl_remote_logger.h"
+#include "bcmolt_conv.h"
+
+#define BCMOLT_REMOTE_LOGGER_DEFAULT_POLLING_PERIOD_MS 5000
+#define BCMOLT_REMOTE_LOGGER_DEFAULT_SUBSEQUENT_DELAY_MS 5
+#define BCMOLT_REMOTE_LOGGER_DEFAULT_MAX_FILE_SIZE (200 * 1000000)
+#define BCMOLT_REMOTE_LOGGER_DEFAULT_FILE_REACHED_BEHAVIOR "stop"
+#define BCMOLT_REMOTE_LOGGER_DEFAULT_FILENAME "remote_log.txt"
+
+static bcmos_errno remote_logger_configure_log(
+ bcmolt_devid device,
+ const bcmolt_log_entry_key *key,
+ bcmolt_log_level log_level_save)
+{
+ bcmolt_log_entry_cfg cfg;
+ BCMOLT_CFG_INIT(&cfg, log_entry, *key);
+ BCMOLT_CFG_PROP_SET(&cfg, log_entry, log_level_save, log_level_save);
+ BCMOLT_CFG_PROP_SET(&cfg, log_entry, log_level_print, BCMOLT_LOG_LEVEL_WARNING);
+ return bcmolt_cfg_set(device, &cfg.hdr);
+}
+
+static bcmos_errno remote_logger_configure_logs(bcmolt_devid device)
+{
+ bcmos_errno err;
+ bcmolt_msg_set *msg_set;
+ bcmolt_log_entry_cfg multi_cfg;
+ bcmolt_log_entry_key multi_key = { .log_id = 0 }; /* start from the first log ID */
+ uint16_t i;
+
+ /* allocate space for multi-get return */
+ err = bcmolt_msg_set_alloc(BCMOLT_OBJ_ID_LOG_ENTRY, BCMOLT_MGT_GROUP_CFG, 50, &msg_set);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ /* initialize the multi-get config structure */
+ BCMOLT_CFG_INIT(&multi_cfg, log_entry, multi_key);
+ BCMOLT_MSGSET_CFG_PROP_GET(msg_set, log_entry, log_level_save);
+
+ do
+ {
+ /* call multi-get API */
+ err = bcmolt_cfg_get_multi(device, &multi_cfg.hdr, BCMOLT_FILTER_FLAGS_NONE, msg_set);
+ if (err != BCM_ERR_OK)
+ {
+ break;
+ }
+
+ /* for each log entry, reconfigure it to print only warning level and above to the screen (keeping current
+ * behavior for printing to RAM files) */
+ for (i = 0; i < msg_set->num_instances; i++)
+ {
+ bcmolt_log_entry_cfg *cfg = (bcmolt_log_entry_cfg *)msg_set->msg[i];
+ err = remote_logger_configure_log(device, &cfg->key, cfg->data.log_level_save);
+ if (err != BCM_ERR_OK)
+ {
+ break;
+ }
+ }
+
+ /* update the key for next call */
+ multi_cfg.key = *((bcmolt_log_entry_key *)msg_set->next_key);
+
+ /* keep calling the function until we have retrieved all entries */
+ } while (msg_set->more);
+
+ bcmolt_msg_set_free(msg_set);
+ return err;
+}
+
+static bcmos_errno remote_logger_cmd_start(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmos_errno err;
+
+ /* these parameters will always be non-NULL since they have default values */
+ bcmolt_remote_logger_cfg cfg =
+ {
+ .max_file_size = bcmcli_find_named_parm(session, "max_file_size")->value.unumber,
+ .max_file_size_reached_behavior = (bcmolt_remote_logger_file_size_reached_behavior)
+ bcmcli_find_named_parm(session, "file_size_reached")->value.number,
+ .polling_period_ms = bcmcli_find_named_parm(session, "polling_period")->value.unumber,
+ .subsequent_delay_ms = bcmcli_find_named_parm(session, "subsequent_delay")->value.unumber,
+ };
+ strncpy(
+ cfg.filename,
+ bcmcli_find_named_parm(session, "filename")->value.string,
+ BCMOLT_REMOTE_LOGGER_FILENAME_MAX_LEN - 1); /* leave room for terminator */
+
+ /* if the user requested, set all logs on the device to print warnings/errors only */
+ if ((bcmos_bool)bcmcli_find_named_parm(session, "configure_logs")->value.number)
+ {
+ err = remote_logger_configure_logs(current_device);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+ bcmcli_session_print(session, "Reconfigured all log entries to print warning/error messages only\n");
+ }
+
+ err = bcmolt_remote_logger_appl_cfg_update(current_device, &cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ err = bcmolt_remote_logger_appl_start(current_device);
+ if (err == BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "Remote logger application started\n");
+ }
+ return err;
+}
+
+static bcmos_errno remote_logger_cmd_stop(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmos_errno err = bcmolt_remote_logger_appl_stop(current_device);
+ if (err == BCM_ERR_OK)
+ {
+ bcmcli_session_print(session, "Remote logger application stopped\n");
+ }
+ return err;
+}
+
+static bcmos_errno remote_logger_cmd_status(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ static int2str_t file_size_reached2str[] =
+ {
+ { BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_STOP, "stop" },
+ { BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_CLEAR, "clear" },
+ { -1 }
+ };
+
+ bcmos_errno err;
+ bcmolt_remote_logger_cfg cfg;
+
+ if (bcmolt_remote_logger_appl_is_running(current_device))
+ {
+ bcmcli_session_print(session, "Remote logger application is running\n");
+ err = bcmolt_remote_logger_appl_cfg_get(current_device, &cfg);
+ if (err != BCM_ERR_OK)
+ {
+ return err;
+ }
+
+ bcmcli_session_print(session, "filename=%s\n", cfg.filename);
+ bcmcli_session_print(session, "max_file_size=%u\n", cfg.max_file_size);
+ bcmcli_session_print(
+ session,
+ "file_size_reached=%s\n",
+ int2str(file_size_reached2str, cfg.max_file_size_reached_behavior));
+ bcmcli_session_print(session, "polling_period=%u\n", cfg.polling_period_ms);
+ bcmcli_session_print(session, "subsequent_delay=%u\n", cfg.subsequent_delay_ms);
+ }
+ else
+ {
+ bcmcli_session_print(session, "Remote logger application is not running\n");
+ }
+
+ return BCM_ERR_OK;
+}
+
+bcmos_errno bcmolt_remote_logger_appl_cli_init(bcmcli_entry *top_dir)
+{
+ static bcmcli_enum_val file_size_reached_behavior_table[] =
+ {
+ { "stop", BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_STOP },
+ { "clear", BCMOLT_REMOTE_LOGGER_FILE_SIZE_REACHED_BEHAVIOR_CLEAR },
+ BCMCLI_ENUM_LAST
+ };
+
+ bcmcli_entry *dir = bcmcli_dir_add(
+ top_dir,
+ "remote_logger",
+ "Periodically copy device log to local file",
+ BCMCLI_ACCESS_ADMIN,
+ NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+
+ BCMCLI_MAKE_CMD(dir, "start", "Start periodically copying device log to host", remote_logger_cmd_start,
+ BCMCLI_MAKE_PARM_DEFVAL(
+ "filename",
+ "Output file path",
+ BCMCLI_PARM_STRING,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ (long)BCMOLT_REMOTE_LOGGER_DEFAULT_FILENAME),
+ BCMCLI_MAKE_PARM_DEFVAL(
+ "max_file_size",
+ "Maximum output file size in bytes",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ BCMOLT_REMOTE_LOGGER_DEFAULT_MAX_FILE_SIZE),
+ BCMCLI_MAKE_PARM_ENUM_DEFVAL(
+ "file_size_reached",
+ "Behavior when max file size is reached",
+ file_size_reached_behavior_table,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ BCMOLT_REMOTE_LOGGER_DEFAULT_FILE_REACHED_BEHAVIOR),
+ BCMCLI_MAKE_PARM_DEFVAL(
+ "polling_period",
+ "Polling period in ms",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ BCMOLT_REMOTE_LOGGER_DEFAULT_POLLING_PERIOD_MS),
+ BCMCLI_MAKE_PARM_DEFVAL(
+ "subsequent_delay",
+ "Delay between repeated API calls in ms",
+ BCMCLI_PARM_UNUMBER,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ BCMOLT_REMOTE_LOGGER_DEFAULT_SUBSEQUENT_DELAY_MS),
+ BCMCLI_MAKE_PARM_ENUM_DEFVAL(
+ "configure_logs",
+ "Configure all device logs to print error/warning only",
+ bcmcli_enum_bool_table,
+ BCMCLI_PARM_FLAG_OPTIONAL,
+ "yes"));
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "stop", "Stop periodically copying device log to host", remote_logger_cmd_stop);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "status", "Show status / configuration parameters", remote_logger_cmd_status);
+
+ return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger_cli.h b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger_cli.h
new file mode 100644
index 0000000..191ef8b
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/remote_logger/bcmolt_user_appl_remote_logger_cli.h
@@ -0,0 +1,38 @@
+/*
+<: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.
+
+:>
+*/
+
+#ifndef _BCMOLT_USER_APPL_REMOTE_LEARNING_CLI_H_
+#define _BCMOLT_USER_APPL_REMOTE_LEARNING_CLI_H_
+
+#include <bcmos_system.h>
+#include <bcmcli.h>
+
+bcmos_errno bcmolt_remote_logger_appl_cli_init(bcmcli_entry *top_dir);
+
+#endif
diff --git a/bcm68620_release/release/host_reference/user_appl/sw_upgrade/Makefile b/bcm68620_release/release/host_reference/user_appl/sw_upgrade/Makefile
new file mode 100644
index 0000000..1641a6b
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/sw_upgrade/Makefile
@@ -0,0 +1,12 @@
+# SW upgrade application
+
+ifeq ("$(ENABLE_CLI)", "y")
+ MOD_NAME = bcm_sw_upgrade
+ MOD_TYPE = lib
+ MOD_DEPS = host_api
+ srcs = bcmolt_sw_upgrade_cli.c
+
+ ifeq ("$(OS_KERNEL)", "linux")
+ MOD_DEPS += dev_log_linux
+ endif
+endif
diff --git a/bcm68620_release/release/host_reference/user_appl/sw_upgrade/bcmolt_sw_upgrade_cli.c b/bcm68620_release/release/host_reference/user_appl/sw_upgrade/bcmolt_sw_upgrade_cli.c
new file mode 100644
index 0000000..c9b03b4
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/sw_upgrade/bcmolt_sw_upgrade_cli.c
@@ -0,0 +1,70 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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 <bcmcli.h>
+#include <bcmolt_model_types.h>
+#include <bcmolt_msg.h>
+#include <bcmolt_api.h>
+#include <bcm_api_cli_helpers.h>
+#include "bcmolt_sw_upgrade_cli.h"
+
+static bcmos_errno bcmolt_user_appl_cli_sw_upgrade_activate(bcmcli_session *session, const bcmcli_cmd_parm parms[], uint16_t n_parms)
+{
+ bcmolt_device_key key = {};
+ bcmolt_device_sw_upgrade_activate oper_sw_upgrade_activate;
+ bcmolt_device_disconnect oper_disconnect;
+ bcmolt_device_connect oper_connect;
+ bcmos_errno err;
+
+ BCMOLT_OPER_INIT(&oper_sw_upgrade_activate, device, sw_upgrade_activate, key);
+ err = bcmolt_oper_submit(0, &oper_sw_upgrade_activate.hdr);
+ if (err)
+ return err;
+
+ BCMOLT_OPER_INIT(&oper_disconnect, device, disconnect, key);
+ err = bcmolt_oper_submit(0, &oper_disconnect.hdr);
+ if (err)
+ return err;
+
+ BCMOLT_OPER_INIT(&oper_connect, device, connect, key);
+ return bcmolt_oper_submit(0, &oper_connect.hdr);
+}
+
+bcmos_errno bcmolt_user_appl_cli_sw_upgrade_init(bcmcli_entry *top_dir)
+{
+ bcmcli_entry *dir = bcmcli_dir_add(top_dir, "sw_upgrade",
+ "Software upgrade user application", BCMCLI_ACCESS_ADMIN, NULL);
+ BCMOS_CHECK_RETURN_ERROR(!dir, BCM_ERR_NOMEM);
+
+ BCMCLI_MAKE_CMD_NOPARM(dir, "activate", "SW upgrade image activation", bcmolt_user_appl_cli_sw_upgrade_activate);
+
+ return BCM_ERR_OK;
+}
+
diff --git a/bcm68620_release/release/host_reference/user_appl/sw_upgrade/bcmolt_sw_upgrade_cli.h b/bcm68620_release/release/host_reference/user_appl/sw_upgrade/bcmolt_sw_upgrade_cli.h
new file mode 100644
index 0000000..9915a95
--- /dev/null
+++ b/bcm68620_release/release/host_reference/user_appl/sw_upgrade/bcmolt_sw_upgrade_cli.h
@@ -0,0 +1,38 @@
+/*
+<:copyright-BRCM:2014:DUAL/GPL:standard
+
+ Copyright (c) 2014 Broadcom Corporation
+ 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.
+
+:>
+*/
+
+#ifndef _BCMOLT_SW_UPGRADE_CLI_H_
+#define _BCMOLT_SW_UPGRADE_CLI_H_
+
+#include <bcmos_system.h>
+#include <bcmcli.h>
+
+bcmos_errno bcmolt_user_appl_cli_sw_upgrade_init(bcmcli_entry *top_dir);
+
+#endif