blob: e193148a49d6aa7e21c18e7230de4dd757858974 [file] [log] [blame]
/*
<: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;
}