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