| /****************************************************************************** |
| * |
| * <:copyright-BRCM:2016:DUAL/GPL:standard |
| * |
| * Copyright (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 bal_oam_util.c |
| * |
| * @brief OAM util interfaces definition used by Bal Core |
| * |
| * This file expose the APIs to the core to configure ONU via EPON OAM |
| * |
| * @addtogroup oam_util |
| */ |
| |
| /*@{*/ |
| |
| #include "bal_oam_util.h" |
| #include <bcmolt_user_appl_epon_oam.h> |
| #include <bcmolt_eon.h> |
| #include <bcmolt_eon_log.h> |
| #include <bcmolt_epon_oam_types.h> |
| |
| #define BAL_OAM_TIMEOUT_US 1000000 /* 1 second */ |
| |
| typedef enum |
| { |
| BAL_OAM_STATE_IDLE, |
| BAL_OAM_STATE_SET_OAM_RATE, |
| BAL_OAM_STATE_CLEAR_INGRESS_RULES_NETWORK_PON, |
| BAL_OAM_STATE_CLEAR_INGRESS_RULES_USER_PORT, |
| BAL_OAM_STATE_SET_BASIC_QUEUE_CONFIG, |
| BAL_OAM_STATE_ADD_INGRESS_RULES_NETWORK_PON, |
| BAL_OAM_STATE_ADD_INGRESS_RULES_USER_PORT, |
| BAL_OAM_STATE_SET_REPORT_THRESHOLDS, |
| BAL_OAM_STATE_ENABLE_USER_TRAFFIC, |
| BAL_OAM_STATE_DISABLE_USER_TRAFFIC, |
| BAL_OAM_STATE__NUM_OF, |
| } bal_oam_state; |
| |
| typedef enum |
| { |
| BAL_OAM_EVENT_ACK, |
| BAL_OAM_EVENT_TIMEOUT, |
| BAL_OAM_EVENT__NUM_OF, |
| } bal_oam_event; |
| |
| typedef struct bal_oam_context |
| { |
| TAILQ_ENTRY(bal_oam_context) next; |
| bal_oam_cb cb; |
| void *context; |
| bcmolt_devid device_id; |
| bcmolt_epon_ni epon_ni; |
| bcmos_mac_address mac; |
| bcmos_timer timer; |
| bal_oam_state state; |
| } bal_oam_context; |
| |
| typedef struct |
| { |
| bcmos_msg m; |
| bcmolt_devid device_id; |
| bcmolt_epon_ni epon_ni; |
| bcmos_mac_address mac; |
| } bal_oam_timeout_msg; |
| |
| typedef bcmos_errno (*bal_oam_sm_cb)(bal_oam_context *context); |
| |
| static TAILQ_HEAD(, bal_oam_context) bal_oam_contexts = TAILQ_HEAD_INITIALIZER(bal_oam_contexts); |
| |
| static bal_oam_context *bal_oam_get_context(bcmolt_devid device_id, bcmolt_epon_ni epon_ni, const bcmos_mac_address *mac) |
| { |
| bal_oam_context *iter; |
| |
| TAILQ_FOREACH(iter, &bal_oam_contexts, next) |
| { |
| if (device_id == iter->device_id && epon_ni == iter->epon_ni && !memcmp(mac, &iter->mac, sizeof(*mac))) |
| break; |
| } |
| return iter; |
| } |
| |
| static void bal_oam_negotiation_result_cb(void *context, const eon_link_key_t *link_key, bcmos_errno result) |
| { |
| bal_oam_context *_context = context; |
| |
| _context->cb(_context->context, _context->device_id, _context->epon_ni, &_context->mac, result); |
| |
| bcmos_free(_context); |
| } |
| |
| bcmos_errno bal_oam_start_oam_negotiation(bcmolt_devid device_id, bcmolt_epon_ni epon_ni, bcmos_mac_address *mac, bal_oam_cb cb, void *context) |
| { |
| eon_link_key_t link_key = {}; |
| bal_oam_context *_context; |
| |
| link_key.device_id = device_id; |
| link_key.epon_ni = epon_ni; |
| link_key.mac_address = *mac; |
| |
| _context = bcmos_calloc(sizeof(*_context)); |
| _context->cb = cb; |
| _context->context = context; |
| _context->device_id = device_id; |
| _context->epon_ni = epon_ni; |
| _context->mac = *mac; |
| |
| return bcmolt_user_appl_eon_start(&link_key, eon_oam_set_id_dpoe, bal_oam_negotiation_result_cb, _context, BCMOS_TRUE); |
| } |
| |
| static void bal_oam_configure_traffic_complete(bal_oam_context *context, bcmos_errno result) |
| { |
| context->cb(context->context, context->device_id, context->epon_ni, &context->mac, result); |
| |
| bcmos_timer_destroy(&context->timer); |
| |
| TAILQ_REMOVE(&bal_oam_contexts, context, next); |
| |
| /* No need to set context->state to BAL_OAM_STATE_IDLE - it is going to be destroyed here anyway. */ |
| bcmos_free(context); |
| } |
| |
| static void bal_oam_timeout_msg_cb(bcmos_module_id module_id, bcmos_msg *msg) |
| { |
| bal_oam_timeout_msg *timeout_msg = (bal_oam_timeout_msg *)msg; |
| bal_oam_context *context = bal_oam_get_context(timeout_msg->device_id, timeout_msg->epon_ni, &timeout_msg->mac); |
| |
| bal_oam_configure_traffic_complete(context, BCM_ERR_TIMEOUT); |
| |
| bcmos_msg_free(msg); |
| } |
| |
| static bcmos_errno bal_oam_state_any_event_timeout(bal_oam_context *context) |
| { |
| bal_oam_timeout_msg *msg; |
| |
| /* We should not directly call bal_oam_configure_traffic_complete() from within timer context, because in bal_oam_configure_traffic_complete() we free the memory in which the timer |
| * resides. To overcome this, we use a message - the timer will finish executing and only then the message handler will execute. */ |
| msg = bcmos_calloc(sizeof(*msg)); |
| msg->device_id = context->device_id; |
| msg->epon_ni = context->epon_ni; |
| msg->mac = context->mac; |
| msg->m.handler = bal_oam_timeout_msg_cb; |
| bcmos_msg_send_to_module(BCMOS_MODULE_ID_WORKER_MGMT, &msg->m, 0); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_set_oam_rate_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| |
| /* Clear EPON port ingress. */ |
| rc = epon_oam_dpoe_clear_ingress_rules_network_pon(context->device_id, context->epon_ni, &context->mac); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_CLEAR_INGRESS_RULES_NETWORK_PON; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_clear_ingress_rules_network_pon_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| |
| /* Clear UNI port ingress rules. */ |
| rc = epon_oam_dpoe_clear_ingress_rules_user_port(context->device_id, context->epon_ni, &context->mac); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_CLEAR_INGRESS_RULES_USER_PORT; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_clear_ingress_rules_user_port_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| |
| /* Set ONU queue configuration. */ |
| rc = epon_oam_dpoe_set_basic_queue_config(context->device_id, context->epon_ni, &context->mac, 255, 255); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_SET_BASIC_QUEUE_CONFIG; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_set_basic_queue_config_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| |
| /* Add EPON port ingress rules */ |
| rc = epon_oam_dpoe_add_ingress_rules_network_pon(context->device_id, context->epon_ni, &context->mac, DPOE_RULE_VLAN_MODE_NONE, NULL); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_ADD_INGRESS_RULES_NETWORK_PON; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_add_ingress_rules_network_pon_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| |
| /* Add UNI port ingress rules. */ |
| rc = epon_oam_dpoe_add_ingress_rules_user_port(context->device_id, context->epon_ni, &context->mac, DPOE_RULE_VLAN_MODE_NONE, NULL); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_ADD_INGRESS_RULES_USER_PORT; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_add_ingress_rules_user_port_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| bcmolt_epon_oam_queue_sets queue_sets = |
| { |
| [0] = {16256}, |
| [1] = {32512}, |
| [2] = {48768}, |
| [3] = {65024}, |
| }; |
| |
| /* Set ONU report thresholds. */ |
| rc = epon_oam_dpoe_set_report_thresholds(context->device_id, context->epon_ni, &context->mac, 1, queue_sets, 4); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_SET_REPORT_THRESHOLDS; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_set_report_thresholds_event_ack(bal_oam_context *context) |
| { |
| bcmos_errno rc; |
| |
| /* Enable user traffic. */ |
| rc = epon_oam_dpoe_enable_user_traffic(context->device_id, context->epon_ni, &context->mac); |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(context, rc); |
| return rc; |
| } |
| |
| bcmos_timer_start(&context->timer, BAL_OAM_TIMEOUT_US); |
| |
| context->state = BAL_OAM_STATE_ENABLE_USER_TRAFFIC; |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_enable_user_traffic_event_ack(bal_oam_context *context) |
| { |
| bal_oam_configure_traffic_complete(context, BCM_ERR_OK); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bal_oam_state_disable_user_traffic_event_ack(bal_oam_context *context) |
| { |
| bal_oam_configure_traffic_complete(context, BCM_ERR_OK); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bal_oam_sm_cb bal_oam_state_machine[BAL_OAM_STATE__NUM_OF][BAL_OAM_EVENT__NUM_OF] = |
| { |
| [BAL_OAM_STATE_SET_OAM_RATE] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_set_oam_rate_event_ack, |
| }, |
| [BAL_OAM_STATE_CLEAR_INGRESS_RULES_NETWORK_PON] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_clear_ingress_rules_network_pon_event_ack, |
| }, |
| [BAL_OAM_STATE_CLEAR_INGRESS_RULES_USER_PORT] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_clear_ingress_rules_user_port_event_ack, |
| }, |
| [BAL_OAM_STATE_SET_BASIC_QUEUE_CONFIG] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_set_basic_queue_config_event_ack, |
| }, |
| [BAL_OAM_STATE_ADD_INGRESS_RULES_NETWORK_PON] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_add_ingress_rules_network_pon_event_ack, |
| }, |
| [BAL_OAM_STATE_ADD_INGRESS_RULES_USER_PORT] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_add_ingress_rules_user_port_event_ack, |
| }, |
| [BAL_OAM_STATE_SET_REPORT_THRESHOLDS] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_set_report_thresholds_event_ack, |
| }, |
| [BAL_OAM_STATE_ENABLE_USER_TRAFFIC] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_enable_user_traffic_event_ack, |
| }, |
| [BAL_OAM_STATE_DISABLE_USER_TRAFFIC] = |
| { |
| [BAL_OAM_EVENT_TIMEOUT] = bal_oam_state_any_event_timeout, |
| [BAL_OAM_EVENT_ACK] = bal_oam_state_disable_user_traffic_event_ack, |
| }, |
| }; |
| |
| static void bal_oam_sm_run(bal_oam_context *context, bal_oam_event event) |
| { |
| bal_oam_sm_cb cb = bal_oam_state_machine[context->state][event]; |
| |
| cb(context); |
| } |
| |
| static bcmos_timer_rc bal_oam_timeout_cb(bcmos_timer *timer, long data) |
| { |
| bal_oam_context *context = (bal_oam_context *)data; |
| |
| bal_oam_sm_run(context, BAL_OAM_EVENT_TIMEOUT); |
| |
| return BCMOS_TIMER_OK; |
| } |
| |
| bcmos_errno bal_oam_configure_traffic(bcmolt_devid device_id, bcmolt_epon_ni epon_ni, bcmos_mac_address *mac, bcmos_bool is_enabled, bal_oam_cb cb, void *context) |
| { |
| bcmos_errno rc; |
| bal_oam_context *_context; |
| bcmos_timer_parm timer_params = |
| { |
| .name = "bal_oam_timer", |
| .owner = BCMOS_MODULE_ID_WORKER_MGMT, |
| .handler = bal_oam_timeout_cb, |
| }; |
| |
| _context = bcmos_calloc(sizeof(*_context)); |
| _context->cb = cb; |
| _context->context = context; |
| _context->device_id = device_id; |
| _context->epon_ni = epon_ni; |
| _context->mac = *mac; |
| TAILQ_INSERT_TAIL(&bal_oam_contexts, _context, next); |
| |
| if (is_enabled) |
| { |
| /* Set OAM rate. */ |
| rc = epon_oam_dpoe_set_oam_rate(device_id, epon_ni, mac, 10, 3); |
| } |
| else |
| { |
| /* Disable user traffic. */ |
| rc = epon_oam_dpoe_disable_user_traffic(device_id, epon_ni, mac); |
| } |
| if (rc != BCM_ERR_OK) |
| { |
| bal_oam_configure_traffic_complete(_context, rc); |
| return rc; |
| } |
| |
| timer_params.data = (long)_context; |
| bcmos_timer_create(&_context->timer, &timer_params); |
| bcmos_timer_start(&_context->timer, BAL_OAM_TIMEOUT_US); |
| |
| if (is_enabled) |
| _context->state = BAL_OAM_STATE_SET_OAM_RATE; |
| else |
| _context->state = BAL_OAM_STATE_DISABLE_USER_TRAFFIC; |
| |
| return BCM_ERR_OK; |
| } |
| |
| /* TODO: Currently we ignore rc. But we may want to check it. */ |
| void bal_oam_proxy_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) |
| { |
| bal_oam_context *context; |
| |
| if (id != BCMOLT_USER_APPL_EPON_OAM_RX_ID_DPOE_SET_RESPONSE) |
| return; |
| |
| /* If applying set command not from within bal_oam_configure_traffic() (for example, from BAL CLI), context might be NULL. */ |
| context = bal_oam_get_context(device_id, epon_ni, mac); |
| if (!context) |
| return; |
| |
| bal_oam_sm_run(context, BAL_OAM_EVENT_ACK); |
| } |
| |
| /*@}*/ |
| |