| /* |
| <: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; |
| } |
| |