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