/*
 * Copyright (c) 2013-2019, Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 *
 * \file mac_mlme.c
 * \brief MLME API for MAC control
 *
 *  MLME API for MAC certification.
 *
 */

#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "eventOS_event.h"
#include "eventOS_scheduler.h"
#include "eventOS_callback_timer.h"
#include "ns_trace.h"
#include "randLIB.h"
#include "nsdynmemLIB.h"
#include "platform/arm_hal_interrupt.h"
#include "common_functions.h"
#include "sw_mac.h"
#include "mlme.h"
#include "mac_api.h"
#include "fhss_api.h"

#include "MAC/IEEE802_15_4/sw_mac_internal.h"
#include "MAC/IEEE802_15_4/mac_defines.h"
#include "MAC/IEEE802_15_4/mac_header_helper_functions.h"
#include "MAC/IEEE802_15_4/mac_indirect_data.h"
#include "MAC/IEEE802_15_4/mac_security_mib.h"
#include "MAC/IEEE802_15_4/mac_mlme.h"
#include "MAC/IEEE802_15_4/mac_timer.h"
#include "MAC/IEEE802_15_4/mac_pd_sap.h"
#include "MAC/IEEE802_15_4/mac_mcps_sap.h"
#include "MAC/virtual_rf/virtual_rf_defines.h"
#include "MAC/rf_driver_storage.h"

#define TRACE_GROUP "mlme"

#define MAC_ACK_WAIT_DURATION   90

static int8_t mac_mlme_rf_disable(struct protocol_interface_rf_mac_setup *rf_mac_setup);
static int8_t mac_mlme_rf_receiver_enable(struct protocol_interface_rf_mac_setup *rf_mac_setup);

static void mac_mlme_write_mac16(protocol_interface_rf_mac_setup_s *rf_setup, uint8_t *addrPtr);
static void mac_mlme_write_mac64(struct protocol_interface_rf_mac_setup *rf_setup, uint8_t *addrPtr);
static void mac_mlme_timers_disable(protocol_interface_rf_mac_setup_s *rf_ptr);
static void mac_mlme_free_scan_temporary_data(protocol_interface_rf_mac_setup_s *rf_mac_setup);
static int8_t mac_mlme_set_panid(struct protocol_interface_rf_mac_setup *rf_setup, uint16_t pan_id);
static int8_t mac_mlme_set_mac16(struct protocol_interface_rf_mac_setup *rf_setup, uint16_t mac16);
static int8_t mac_mlme_rf_channel_set(struct protocol_interface_rf_mac_setup *rf_setup, uint8_t new_channel);
static void mac_mlme_timer_cb(int8_t timer_id, uint16_t slots);
static void mac_mlme_start_confirm_handler(protocol_interface_rf_mac_setup_s *rf_ptr, const mlme_start_conf_t *conf);
static void mac_mlme_scan_confirm_handler(protocol_interface_rf_mac_setup_s *rf_ptr, const mlme_scan_conf_t *conf);
static int mac_mlme_set_symbol_rate(protocol_interface_rf_mac_setup_s *rf_mac_setup);

static void mac_mlme_energy_scan_start(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint8_t channel)
{
    phy_device_driver_s *dev_driver = rf_mac_setup->dev_driver->phy_driver;
    rf_mac_setup->mac_channel = channel;
    if (rf_mac_setup->macRfRadioOn) {
        if (dev_driver->state_control) {
            dev_driver->state_control(PHY_INTERFACE_DOWN, 0);
        }
    }
    if (dev_driver->state_control) {
        dev_driver->state_control(PHY_INTERFACE_RX_ENERGY_STATE, channel);
    }
    rf_mac_setup->macRfRadioOn = true;
    rf_mac_setup->macRfRadioTxActive = false;
}

uint16_t mlme_scan_analyze_next_channel(channel_list_s *mac_channel_list, bool clear_channel)
{
    uint8_t i, j = 0, k = 1;
    uint32_t mask = 1;
    uint32_t *channel_mask = mac_channel_list->channel_mask;

    if (mac_channel_list->channel_page == CHANNEL_PAGE_9 ||
            mac_channel_list->channel_page == CHANNEL_PAGE_10) {
        k = 8;
    }
    for (j = 0; j < k; j++) {
        for (i = 0; i < 32; i++) {
            if (*channel_mask & mask) {
                if (clear_channel) {
                    *channel_mask &= ~mask;
                }
                return (i + j * 32);
            }
            mask <<= 1;
        }
        mask = 1;
        channel_mask++;
    }
    return 0xffff;
}

static uint32_t mac_mlme_channel_symbol_rate(uint8_t channel_page, uint8_t channel)
{

    uint32_t symbol_rate;

    // Gets symbol rate for channel
    switch (channel_page) {
        case CHANNEL_PAGE_0:
            // 868 Mhz BPSK
            if (channel == 0) {
                symbol_rate = 20000;
                // 915 Mhz BPSK
            } else if (channel >= 1 && channel <= 10) {
                symbol_rate = 40000;
                // 2450 Mhz O-QPSK
            } else {
                symbol_rate = 62500;
            }
            break;
        case CHANNEL_PAGE_1:
            // 868 MHz ASK
            if (channel == 0) {
                symbol_rate = 12500;
                // 915 MHz ASK
            } else {
                symbol_rate = 50000;
            }
            break;
        case CHANNEL_PAGE_2:
            // 868 MHz O-QPSK
            if (channel == 0) {
                symbol_rate = 25000;
                // 915 MHz O-QPSK
            } else {
                symbol_rate = 62500;
            }
            break;
        case CHANNEL_PAGE_3:
            // 2450 CSS
            symbol_rate = 167000;
            break;
        case CHANNEL_PAGE_6:
            // 950 MHz BPSK
            if (channel <= 9) {
                symbol_rate = 20000;
                // 950 MHz GFSK
            } else {
                symbol_rate = 100000;
            }
            break;
        default:
            symbol_rate = 62500;
            break;
    }

    return symbol_rate;
}

static void mac_mlme_calc_scan_duration(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint8_t channel)
{
    rf_mac_setup->mlme_ED_counter = 1;

    if (rf_mac_setup->scan_duration) {
        rf_mac_setup->mlme_ED_counter <<= rf_mac_setup->scan_duration;
    }

    rf_mac_setup->mlme_ED_counter++;

    if (rf_mac_setup->scan_type == MAC_ED_SCAN_TYPE) {
        // Gets symbol rate for channel that is scanned
        uint32_t symbol_rate = mac_mlme_channel_symbol_rate(
                                   rf_mac_setup->mac_channel_list.channel_page, channel);

        // Energy scan duration is aBaseSuperframeDuration * (2^n + 1)
        // aBaseSuperframeDuration is 960 symbols e.g for 2.4Ghz O-QPSK with 62.5 ksymbols/s -> 15,36ms.
        // ED scan timer timeout is 4.8ms
        uint16_t frame_duration = (uint32_t) 960 * 100000 / symbol_rate;
        rf_mac_setup->mlme_ED_counter = (uint32_t) rf_mac_setup->mlme_ED_counter * frame_duration / 480;
    }
}

static void mac_mlme_start_request(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    mac_pre_build_frame_t *buf;
    platform_enter_critical();

    mac_mlme_rf_disable(rf_mac_setup);
    buf = rf_mac_setup->active_pd_data_request;
    rf_mac_setup->active_pd_data_request = NULL;
    mac_mlme_mac_radio_enable(rf_mac_setup);
    rf_mac_setup->macUpState = true;
    if (buf) {
        // Active packet is pushed back to queue and statistics will be cleared. They need to be updated here.
        sw_mac_stats_update(rf_mac_setup, STAT_MAC_TX_CCA_ATT, rf_mac_setup->mac_tx_status.cca_cnt);
        sw_mac_stats_update(rf_mac_setup, STAT_MAC_TX_RETRY, rf_mac_setup->mac_tx_status.retry);
        mcps_sap_pd_req_queue_write(rf_mac_setup, buf);
    }
    platform_exit_critical();
}

uint8_t mac_mlme_beacon_req_tx(protocol_interface_rf_mac_setup_s *rf_ptr)
{

    mac_pre_build_frame_t *buf = mcps_sap_prebuild_frame_buffer_get(0);

    if (buf) {
        buf->fcf_dsn.DstAddrMode = MAC_ADDR_MODE_16_BIT;
        buf->fcf_dsn.SrcAddrMode = MAC_ADDR_MODE_NONE;
        buf->fcf_dsn.frametype = FC_CMD_FRAME;
        buf->DstPANId = 0xffff;
        buf->DstAddr[0] = 0xff;
        buf->DstAddr[1] = 0xff;
        buf->mac_command_id = MAC_BEACON_REQ;
        buf->mac_header_length_with_security = 7;
        buf->mac_payload = &buf->mac_command_id;
        buf->mac_payload_length = 1;
        buf->priority = MAC_PD_DATA_MEDIUM_PRIORITY;
        buf->fcf_dsn.DstPanPresents = true;
        buf->fcf_dsn.SrcPanPresents = false;

        mcps_sap_pd_req_queue_write(rf_ptr, buf);
        return 1;
    }
    return 0;
}

static void mac_mlme_scan_init(uint8_t channel, protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    mac_mlme_calc_scan_duration(rf_mac_setup, channel);

    switch (rf_mac_setup->scan_type) {
        /* ED Scan */
        case MAC_ED_SCAN_TYPE:
            //tr_debug("Energy Scan");
            rf_mac_setup->max_ED = 0;
            mac_mlme_energy_scan_start(rf_mac_setup, channel);
            rf_mac_setup->mac_mlme_event = ARM_NWK_MAC_MLME_ED_ANALYZE;
            eventOS_callback_timer_start(rf_mac_setup->mlme_timer_id, 96);
            break;
        case MAC_PASSIVE_SCAN:
        case MAC_ACTIVE_SCAN:   /*Active*/
            tr_debug("Scan channel %u", channel);
            rf_mac_setup->mac_channel = channel;
            rf_mac_setup->macCapRxOnIdle = true;
            mac_mlme_start_request(rf_mac_setup);
            if (rf_mac_setup->scan_type == MAC_ACTIVE_SCAN) {
                mac_pre_build_frame_t *active_buf = rf_mac_setup->active_pd_data_request;
                /* If there is active data request, it must be Beacon request which were failed to send on previous channel.
                 * Do not push new Beacon request to queue as the MAC will try to send the current active data request anyway.
                 */
                if (!active_buf || (active_buf && active_buf->fcf_dsn.frametype != FC_CMD_FRAME)) {
                    mac_mlme_beacon_req_tx(rf_mac_setup);
                }
            }
            rf_mac_setup->mac_mlme_event = ARM_NWK_MAC_MLME_SCAN;
            rf_mac_setup->mlme_tick_count = rf_mac_setup->mlme_ED_counter;
            //tr_debug("mlme_tick_count: %x", rf_mac_setup->mlme_tick_count);
            eventOS_callback_timer_start(rf_mac_setup->mlme_timer_id, 300);
            break;
        case MAC_ORPHAN_SCAN:   /*Orphan*/
            break;
    }
}

static void mac_mlme_scan_start(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    uint8_t channel;

    channel = (uint8_t) mlme_scan_analyze_next_channel(&rf_mac_setup->mac_channel_list, true);
    mac_mlme_scan_init(channel, rf_mac_setup);
}

static bool mac_channel_list_validate(const channel_list_s *list, protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    if (rf_mac_setup->dev_driver->phy_driver->link_type == PHY_LINK_15_4_2_4GHZ_TYPE &&
            list->channel_page == 0) {
        //Accept only Channels channels 11-26
        if (list->channel_mask[0] & 0x7fff800) {
            return true;
        }
        return false;
    } else if (rf_mac_setup->dev_driver->phy_driver->link_type == PHY_LINK_15_4_SUBGHZ_TYPE &&
               (list->channel_page == 0 || list->channel_page == 1 || list->channel_page == 2)) {
        if (list->channel_mask[0] & 0x000007ff) {
            return true;
        }

        return false;
    } else {
        for (int i = 0; i < 8; i++) {
            if (list->channel_mask[i]) {
                return true;
            }
        }
    }

    return false;
}

static int mac_mlme_generate_scan_confirmation(mlme_scan_conf_t *conf, const mlme_scan_t *msg, uint8_t status)
{
    memset(conf, 0, sizeof(mlme_scan_conf_t));
    conf->status = status;
    if (!msg) {
        return -1;
    }
    conf->ScanType = msg->ScanType;
    conf->ChannelPage = msg->ChannelPage;
    if (msg->ScanType == MAC_ACTIVE_SCAN) {
        /*If the scan is successful, this value will be modified properly in mlme_analyze_next_channel()
        and assigned in mlme_scan_operation()*/
        conf->UnscannedChannels.channel_mask[0] = msg->ScanChannels.channel_mask[0];
    } else if (msg->ScanType == MAC_ED_SCAN_TYPE) {
        conf->ED_values = ns_dyn_mem_temporary_alloc(MLME_MAC_RES_SIZE_MAX);
        if (!conf->ED_values) {
            tr_debug("Could not allocate memory for ED values");
            conf->status = MLME_SUCCESS;
            return -2;
        }
        memset(conf->ED_values, 0, MLME_MAC_RES_SIZE_MAX);
    }
    return 0;
}

void mac_mlme_scan_request(const mlme_scan_t *msg, protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    if (rf_mac_setup->mac_mlme_scan_resp) {
        mlme_scan_conf_t conf;
        mac_mlme_generate_scan_confirmation(&conf, msg, MLME_SCAN_IN_PROGRESS);
        mac_mlme_scan_confirm_handler(rf_mac_setup, &conf);
        ns_dyn_mem_free(conf.ED_values);
        return;
    }

    mlme_scan_conf_t *resp = ns_dyn_mem_temporary_alloc(sizeof(mlme_scan_conf_t));
    if (!resp) {
        mlme_scan_conf_t conf;
        tr_error("Mac Scan request fail, no memory");
        mac_mlme_generate_scan_confirmation(&conf, NULL, MLME_SUCCESS);
        mac_mlme_scan_confirm_handler(rf_mac_setup, &conf);
        ns_dyn_mem_free(conf.ED_values);
        return;
    }

    rf_mac_setup->mac_mlme_scan_resp = resp;
    //Validate channel list
    tr_debug("chan page %u, mask %"PRIx32, msg->ScanChannels.channel_page, msg->ScanChannels.channel_mask[0]);
    if (!mac_channel_list_validate(&msg->ScanChannels, rf_mac_setup)) {
        tr_debug("Unsupported channel list");
        mac_mlme_generate_scan_confirmation(resp, msg, MLME_INVALID_PARAMETER);
        mac_generic_event_trig(MAC_MLME_SCAN_CONFIRM_HANDLER, rf_mac_setup, false);
        return;
    }

    if (mac_mlme_generate_scan_confirmation(resp, msg, MLME_SUCCESS) != 0) {
        return;
    }

    tr_debug("MAC: Start MLME scan");

    if (mac_mlme_rf_disable(rf_mac_setup) == 0) {
        rf_mac_setup->macCapRxOnIdle = false;
        if (msg->ScanType == MAC_ACTIVE_SCAN) {
            rf_mac_setup->macUpState = true;
        }
    } else {
        tr_error("Failed to disable RF");
        mac_generic_event_trig(MAC_MLME_SCAN_CONFIRM_HANDLER, rf_mac_setup, false);
        return;
    }

    rf_mac_setup->scan_type = msg->ScanType;
    rf_mac_setup->scan_duration = msg->ScanDuration;
    rf_mac_setup->mac_channel_list = msg->ScanChannels;
    rf_mac_setup->mac_mlme_event = ARM_NWK_MAC_MLME_SCAN;
    rf_mac_setup->scan_active = true;
    mac_mlme_scan_start(rf_mac_setup);
}

int8_t mac_mlme_start_req(const mlme_start_t *s, struct protocol_interface_rf_mac_setup *rf_mac_setup)
{
    if (!s || !rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return -1;
    }

    tr_debug("MAC: Start network %u channel %x panid", s->LogicalChannel, s->PANId);
    mac_mlme_set_panid(rf_mac_setup, s->PANId);

    // Synchronize FHSS
    if (rf_mac_setup->fhss_api) {
        rf_mac_setup->mac_channel = rf_mac_setup->fhss_api->synch_state_set(rf_mac_setup->fhss_api, FHSS_SYNCHRONIZED, s->PANId);
    } else {
        rf_mac_setup->mac_channel = s->LogicalChannel;
    }

    mac_mlme_start_request(rf_mac_setup);
    if (s->PANCoordinator) {
        //tr_debug("Cordinator");
        rf_mac_setup->macCapCordinator = true;
        rf_mac_setup->macCapRxOnIdle = true;
    } else {
        rf_mac_setup->macCapCordinator = false;
    }

    if (s->BatteryLifeExtension) {
        rf_mac_setup->macCapBatteryPowered = true;
    } else {
        rf_mac_setup->macCapBatteryPowered = false;
    }
    mlme_start_conf_t conf;
    conf.status = MLME_SUCCESS;
    mac_mlme_start_confirm_handler(rf_mac_setup, &conf);
    return 0;
}

int8_t mac_mlme_reset(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_reset_t *reset)
{
    if (!reset || !rf_mac_setup) {
        return -1;
    }

    // Stop FHSS
    if (rf_mac_setup->fhss_api) {
        rf_mac_setup->fhss_api->synch_state_set(rf_mac_setup->fhss_api, FHSS_UNSYNCHRONIZED, 0);
    }

    mac_mlme_timers_disable(rf_mac_setup);
    mac_mlme_mac_radio_disabled(rf_mac_setup);
    mac_mlme_set_active_state(rf_mac_setup, false);
    mac_mlme_free_scan_temporary_data(rf_mac_setup);
    mac_mcps_buffer_queue_free(rf_mac_setup);
    rf_mac_setup->macProminousMode = false;
    rf_mac_setup->macWaitingData = false;
    rf_mac_setup->macDataPollReq = false;
    rf_mac_setup->macRxDataAtPoll = false;
    //Clean MAC
    if (reset->SetDefaultPIB) {
        tr_debug("RESET MAC PIB");
        rf_mac_setup->mac_short_address = 0xffff;
        rf_mac_setup->pan_id = 0xffff;
        rf_mac_setup->macCapRxOnIdle = true;
        rf_mac_setup->mac_security_enabled = false;
        rf_mac_setup->macCapCordinator = false;
        rf_mac_setup->mac_mlme_retry_max = MAC_DEFAULT_MAX_FRAME_RETRIES;
    }

    return 0;
}


static int8_t mac_mlme_boolean_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, mlme_attr_t attribute, bool value)
{
    switch (attribute) {
        case macSecurityEnabled:
            rf_mac_setup->mac_security_enabled = value;
            break;

        case macRxOnWhenIdle:
            rf_mac_setup->macCapRxOnIdle = value;
            break;

        case macPromiscuousMode:
            rf_mac_setup->macProminousMode = value;
            break;

        case macGTSPermit:
            rf_mac_setup->macGTSPermit = value;
            break;

        case macAssociationPermit:
            rf_mac_setup->macCapAssocationPermit = value;
            break;
        case macThreadForceLongAddressForBeacon:
            rf_mac_setup->beaconSrcAddressModeLong = value;
            break;

        case macAssociatedPANCoord:

            break;

        case macTimestampSupported:

            break;

        case macBattLifeExt:

            break;

        case macAutoRequest:

            break;

        case macLoadBalancingAcceptAnyBeacon:
            rf_mac_setup->macAcceptAnyBeacon = value;
            if (rf_mac_setup->dev_driver->phy_driver->extension) {
                rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_ACCEPT_ANY_BEACON, (uint8_t *)&value);
            }
            break;
        case macAcceptByPassUnknowDevice:
            rf_mac_setup->mac_security_bypass_unknow_device = value;
            break;

        default:
            return -1;
    }
    return 0;
}

static int8_t mac_mlme_16bit_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, mlme_attr_t attribute, uint16_t value)
{
    switch (attribute) {
        case macCoordShortAddress:
            rf_mac_setup->coord_short_address = value;
            break;
        case macPANId:
            mac_mlme_set_panid(rf_mac_setup, value);
            break;

        case macShortAddress:
            mac_mlme_set_mac16(rf_mac_setup, value);
            break;

        case macSyncSymbolOffset:
            //TODO: change this to return real error
            //This is read only
            break;

        case macTransactionPersistenceTime:
            //TODO: check this also
            break;
        case macDeviceDescriptionPanIDUpdate:
            mac_sec_mib_device_description_pan_update(rf_mac_setup, value);
            break;

        default:
            return -1;
    }
    return 0;
}

static int8_t mac_mlme_8bit_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, mlme_attr_t attribute, uint8_t value)
{
    switch (attribute) {
        case phyCurrentChannel:
            mac_mlme_rf_channel_set(rf_mac_setup, value);
            break;
        case macBeaconPayloadLength:
            if (rf_mac_setup->max_beacon_payload_length < value) {
                return -1;
            }

            rf_mac_setup->mac_beacon_payload_size = value;
            break;
        case macAutoRequestKeyIndex:
            rf_mac_setup->mac_auto_request.KeyIndex = value;
            break;

        case macAutoRequestKeyIdMode:
            rf_mac_setup->mac_auto_request.KeyIdMode = value;
            break;

        case macAutoRequestSecurityLevel:
            rf_mac_setup->mac_auto_request.SecurityLevel = value;
            break;
        case macMaxFrameRetries:
            if (value > 7) {
                return -1;
            }
            rf_mac_setup->mac_mlme_retry_max = value;
            break;

        case macMinBE:
            if (value < rf_mac_setup->macMaxBE) {
                rf_mac_setup->macMinBE = value;
            }
            break;

        case macMaxBE:
            if (value > 8 || value < 3) {
                return -1;
            }
            rf_mac_setup->macMaxBE = value;
            break;

        case macMaxCSMABackoffs:
            if (value > 8) {
                return -1;
            }
            rf_mac_setup->macMaxCSMABackoffs = value;
            break;

        default:
            return -1;
    }
    return 0;
}

static int8_t mac_mlme_32bit_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, mlme_attr_t attribute, uint8_t index, uint32_t value)
{

    switch (attribute) {
        case macFrameCounter:
            if (rf_mac_setup->secFrameCounterPerKey) {
                mlme_key_descriptor_t *key_desc = mac_sec_key_description_get_by_attribute(rf_mac_setup, index);
                if (!key_desc) {
                    return -1;
                }
                mac_sec_mib_key_outgoing_frame_counter_set(rf_mac_setup, key_desc, value);
            } else {
                mac_sec_mib_key_outgoing_frame_counter_set(rf_mac_setup, NULL, value);
            }

            break;

        default:
            return -1;
    }
    return 0;
}

void mac_extended_mac_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const uint8_t *mac64)
{
    if (!mac64 || !rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return;
    }
    phy_device_driver_s *dev_driver = rf_mac_setup->dev_driver->phy_driver;
    memcpy(rf_mac_setup->mac64, mac64, 8); //This should be random
    if (dev_driver->address_write) {
        dev_driver->address_write(PHY_MAC_64BIT, rf_mac_setup->mac64);
    }
}

static uint32_t mac_calc_ack_wait_duration(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint16_t symbols)
{
    uint32_t AckWaitDuration = 0;
    if (rf_mac_setup->rf_csma_extension_supported) {
        AckWaitDuration = symbols * rf_mac_setup->symbol_time_us;
    }
    return AckWaitDuration;
}

static int8_t mac_mlme_set_ack_wait_duration(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    uint16_t symbols = common_read_16_bit_inverse((uint8_t *)set_req->value_pointer);
    uint32_t ack_wait_time_us = mac_calc_ack_wait_duration(rf_mac_setup, symbols);
    if (ack_wait_time_us < 50) {
        return -1;
    }
    // MAC timer uses 50us resolution
    rf_mac_setup->mac_ack_wait_duration = ack_wait_time_us / 50;
    tr_debug("Set macAckWaitDuration: %uus", rf_mac_setup->mac_ack_wait_duration * 50);

    return 0;
}

static int8_t mac_mlme_device_description_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{

    if (set_req->value_size != sizeof(mlme_device_descriptor_t)) {
        return -1;
    }

    return mac_sec_mib_device_description_set(set_req->attr_index, (mlme_device_descriptor_t *) set_req->value_pointer, rf_mac_setup);
}

static int8_t mac_mlme_key_description_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    if (set_req->value_size != sizeof(mlme_key_descriptor_entry_t)) {
        return -1;
    }

    return mac_sec_mib_key_description_set(set_req->attr_index, (mlme_key_descriptor_entry_t *) set_req->value_pointer, rf_mac_setup);
}

static int8_t mac_mlme_default_key_source_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    if (set_req->value_size != 8) {
        return -1;
    }
    if (set_req->attr == macDefaultKeySource) {
        memcpy(rf_mac_setup->mac_default_key_source, (uint8_t *)set_req->value_pointer, 8);
    } else {
        memcpy(rf_mac_setup->mac_auto_request.Keysource, (uint8_t *)set_req->value_pointer, 8);
    }
    return 0;
}

static int8_t mac_mlme_beacon_payload_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    if (set_req->value_size > rf_mac_setup->max_beacon_payload_length) {
        return -1;
    }

    memcpy(rf_mac_setup->mac_beacon_payload, set_req->value_pointer, set_req->value_size);
    memset(rf_mac_setup->mac_beacon_payload + set_req->value_size, 0, rf_mac_setup->max_beacon_payload_length - set_req->value_size);

    return 0;
}

static int8_t mac_mlme_handle_set_values(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    if (set_req->value_size == 1) {
        const bool *pbool = set_req->value_pointer;
        //Check first boolean
        if (mac_mlme_boolean_set(rf_mac_setup, set_req->attr, *pbool) == 0) {
            return 0;
        }
        const uint8_t *pu8 = set_req->value_pointer;
        return mac_mlme_8bit_set(rf_mac_setup, set_req->attr, *pu8);

    } else if (set_req->value_size == 2) {
        const uint16_t *pu16 = set_req->value_pointer;
        return mac_mlme_16bit_set(rf_mac_setup, set_req->attr, *pu16);
    } else if (set_req->value_size == 4) {
        const uint32_t *pu32 = set_req->value_pointer;
        return mac_mlme_32bit_set(rf_mac_setup, set_req->attr, set_req->attr_index, *pu32);
    }
    return -1;
}

static int8_t mac_mlme_set_multi_csma_parameters(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    mlme_multi_csma_ca_param_t multi_csma_params;
    memcpy(&multi_csma_params, set_req->value_pointer, sizeof(mlme_multi_csma_ca_param_t));
    rf_mac_setup->multi_cca_interval = multi_csma_params.multi_cca_interval;
    rf_mac_setup->number_of_csma_ca_periods = multi_csma_params.number_of_csma_ca_periods;
    tr_debug("Multi CSMA-CA, interval: %u, periods %u", rf_mac_setup->multi_cca_interval, rf_mac_setup->number_of_csma_ca_periods);
    return 0;
}

int8_t mac_mlme_set_req(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req)
{
    if (!set_req || !rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return -1;
    }
    uint8_t *pu8 = NULL;
    switch (set_req->attr) {
        case macAckWaitDuration:
            return mac_mlme_set_ack_wait_duration(rf_mac_setup, set_req);
        case macDeviceTable:
            return mac_mlme_device_description_set(rf_mac_setup, set_req);
        case macKeyTable:
            return mac_mlme_key_description_set(rf_mac_setup, set_req);
        case macDefaultKeySource:
        case macAutoRequestKeySource:
            return mac_mlme_default_key_source_set(rf_mac_setup, set_req);
        case macBeaconPayload:
            return mac_mlme_beacon_payload_set(rf_mac_setup, set_req);
        case macLoadBalancingBeaconTx:
            return mac_mlme_beacon_tx(rf_mac_setup);
        case macCoordExtendedAddress:
            if (set_req->value_size == 8) {
                memcpy(rf_mac_setup->coord_long_address, set_req->value_pointer, 8);
            }
            return 0;
        case macTXPower:
            pu8 = (uint8_t *) set_req->value_pointer;
            rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_TX_POWER, pu8);
            tr_debug("Set TX output power to %u%%", *pu8);
            return 0;
        case macCCAThreshold:
            pu8 = (uint8_t *) set_req->value_pointer;
            rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_CCA_THRESHOLD, pu8);
            tr_debug("Set CCA threshold to %u%%", *pu8);
            return 0;
        case macMultiCSMAParameters:
            return mac_mlme_set_multi_csma_parameters(rf_mac_setup, set_req);
        case macRfConfiguration:
            rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_RF_CONFIGURATION, (uint8_t *) set_req->value_pointer);
            mac_mlme_set_symbol_rate(rf_mac_setup);
            phy_rf_channel_configuration_s *config_params = (phy_rf_channel_configuration_s *)set_req->value_pointer;
            tr_info("RF config update:");
            tr_info("Frequency(ch0): %"PRIu32"Hz", config_params->channel_0_center_frequency);
            tr_info("Channel spacing: %"PRIu32"Hz", config_params->channel_spacing);
            tr_info("Datarate: %"PRIu32"bps", config_params->datarate);
            tr_info("Number of channels: %u", config_params->number_of_channels);
            tr_info("Modulation: %u", config_params->modulation);
            tr_info("Modulation index: %u", config_params->modulation_index);
            return 0;
        default:
            return mac_mlme_handle_set_values(rf_mac_setup, set_req);
    }
}

int8_t mac_mlme_get_req(struct protocol_interface_rf_mac_setup *rf_mac_setup, mlme_get_conf_t *get_req)
{
    if (!get_req || !rf_mac_setup) {
        return -1;
    }

    switch (get_req->attr) {
        case macDeviceTable:
            get_req->value_pointer = mac_sec_mib_device_description_get_attribute_index(rf_mac_setup, get_req->attr_index);
            if (get_req->value_pointer) {
                get_req->value_size = sizeof(mlme_device_descriptor_t);
            } else {
                get_req->status = MLME_INVALID_INDEX;
            }
            break;

        case macMaxFrameRetries:
            get_req->value_pointer = &rf_mac_setup->mac_mlme_retry_max;
            get_req->value_size = 1;
            break;

        case macFrameCounter:
            if (rf_mac_setup->secFrameCounterPerKey) {
                mlme_key_descriptor_t *key_desc = mac_sec_key_description_get_by_attribute(rf_mac_setup, get_req->attr_index);
                if (!key_desc) {
                    return -1;
                }
                get_req->value_pointer = &key_desc->KeyFrameCounter;
            } else {
                get_req->value_pointer = &rf_mac_setup->security_frame_counter;
            }

            get_req->value_size = 4;
            break;

        default:
            get_req->status = MLME_UNSUPPORTED_ATTRIBUTE;
            break;

    }
    return 0;
}

static void mlme_scan_operation(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    uint16_t channel;
    mlme_scan_conf_t *resp = rf_mac_setup->mac_mlme_scan_resp;

    if (rf_mac_setup->scan_type == MAC_ED_SCAN_TYPE) {
        resp->ED_values[resp->ResultListSize] = rf_mac_setup->max_ED;
        resp->ResultListSize++;
    }

    channel = mlme_scan_analyze_next_channel(&rf_mac_setup->mac_channel_list, true);
    if (channel > 0xff || rf_mac_setup->mac_mlme_scan_resp->ResultListSize == MLME_MAC_RES_SIZE_MAX) {
        resp->status = MLME_SUCCESS;
        platform_enter_critical();
        mac_mlme_mac_radio_disabled(rf_mac_setup);
        if (resp->ResultListSize == 0 && rf_mac_setup->scan_type == MAC_ACTIVE_SCAN) {
            resp->status = MLME_NO_BEACON;
        } else if (resp->ResultListSize == MLME_MAC_RES_SIZE_MAX) {
            resp->status = MLME_LIMIT_REACHED;
        }
        resp->UnscannedChannels.channel_mask[0] = rf_mac_setup->mac_channel_list.channel_mask[0];
        platform_exit_critical();
        //Scan Confirmation
        mac_generic_event_trig(MAC_MLME_SCAN_CONFIRM_HANDLER, rf_mac_setup, false);
        rf_mac_setup->scan_active = false;
    } else {
        mac_mlme_scan_init((uint8_t) channel, rf_mac_setup);
    }
}

void mac_frame_src_address_set_from_interface(uint8_t SrcAddrMode, protocol_interface_rf_mac_setup_s *rf_ptr, uint8_t *addressPtr)
{
    if (!rf_ptr) {
        return;
    }
    if (SrcAddrMode == MAC_ADDR_MODE_16_BIT) {
        mac_mlme_write_mac16(rf_ptr, addressPtr);
    } else if (SrcAddrMode == MAC_ADDR_MODE_64_BIT) {
        mac_mlme_write_mac64(rf_ptr, addressPtr);
    }
}

void mac_mlme_active_scan_response_timer_start(void *interface)
{
    if (interface) {
        protocol_interface_rf_mac_setup_s *rf_mac_setup = (protocol_interface_rf_mac_setup_s *)interface;
        if (rf_mac_setup) {
            if (rf_mac_setup->mac_mlme_event == ARM_NWK_MAC_MLME_SCAN) {
                eventOS_callback_timer_stop(rf_mac_setup->mlme_timer_id);
                rf_mac_setup->mac_mlme_event = ARM_NWK_MAC_MLME_SCAN;
                rf_mac_setup->mlme_tick_count = rf_mac_setup->mlme_ED_counter;

                eventOS_callback_timer_start(rf_mac_setup->mlme_timer_id, 300);
            }
        }
    }
}

static void mac_mlme_timers_disable(protocol_interface_rf_mac_setup_s *rf_ptr)
{
    platform_enter_critical();
    if (rf_ptr->mac_mlme_event != ARM_NWK_MAC_MLME_IDLE) {
        eventOS_callback_timer_stop(rf_ptr->mlme_timer_id);
        rf_ptr->mac_mlme_event = ARM_NWK_MAC_MLME_IDLE;
    }
    timer_mac_stop(rf_ptr);
    platform_exit_critical();
}

void mac_mlme_event_cb(void *mac_ptr)
{
    protocol_interface_rf_mac_setup_s *rf_mac_setup = (protocol_interface_rf_mac_setup_s *) mac_ptr;
    if (!rf_mac_setup) {
        return;
    }
    arm_nwk_mlme_event_type_e event_type;
    event_type = rf_mac_setup->mac_mlme_event;
    rf_mac_setup->mac_mlme_event = ARM_NWK_MAC_MLME_IDLE;
    switch (event_type) {
        case ARM_NWK_MAC_MLME_SCAN:
            mlme_scan_operation(rf_mac_setup);
            break;

        case ARM_NWK_MAC_MLME_INDIRECT_DATA_POLL:
            tr_debug("Data poll data wait TO");
            mac_mlme_poll_process_confirm(rf_mac_setup, MLME_NO_DATA);
            break;

        case ARM_NWK_MAC_MLME_INDIRECT_DATA_POLL_AFTER_DATA:
            mac_mlme_poll_process_confirm(rf_mac_setup, MLME_SUCCESS);
            break;

        default:
            break;
    }
}

static void mac_mcps_timer_cb(int8_t timer_id, uint16_t slots)
{

    protocol_interface_rf_mac_setup_s *rf_ptr = get_sw_mac_ptr_by_timer(timer_id, ARM_MCPS_TIMER);
    if (!rf_ptr || !rf_ptr->dev_driver || !rf_ptr->dev_driver->phy_driver) {
        return;
    }
    rf_ptr->mac_mcps_timer_event.event_data = slots;
    eventOS_event_send(&rf_ptr->mac_mcps_timer_event);

}


static void mac_mlme_timer_cb(int8_t timer_id, uint16_t slots)
{
    protocol_interface_rf_mac_setup_s *rf_ptr = get_sw_mac_ptr_by_timer(timer_id, ARM_NWK_MLME_TIMER);
    if (!rf_ptr || !rf_ptr->dev_driver || !rf_ptr->dev_driver->phy_driver) {
        return;
    }

    if (rf_ptr->mlme_tick_count == 0) {
        if (rf_ptr->mac_mlme_event != ARM_NWK_MAC_MLME_IDLE) {
            if (rf_ptr->mac_mlme_event == ARM_NWK_MAC_MLME_ED_ANALYZE) {
                if (rf_ptr->mlme_ED_counter > 1) {
                    uint8_t ed = 0;
                    phy_device_driver_s *dev_driver = rf_ptr->dev_driver->phy_driver;
                    rf_ptr->mlme_ED_counter--;
                    if (dev_driver->extension) {
                        dev_driver->extension(PHY_EXTENSION_READ_CHANNEL_ENERGY, &ed);
                    }
                    if (ed > rf_ptr->max_ED) {
                        rf_ptr->max_ED = ed;
                    }

                    /*96 * 50us = 4.8ms*/
                    rf_ptr->mlme_tick_count = 0;
                    rf_ptr->mac_mlme_event = ARM_NWK_MAC_MLME_ED_ANALYZE;
                    eventOS_callback_timer_start(timer_id, slots);
                } else {
                    mac_mlme_rf_disable(rf_ptr);
                    rf_ptr->mac_mlme_event = ARM_NWK_MAC_MLME_SCAN;
                    mac_generic_event_trig(MAC_MLME_EVENT_HANDLER, rf_ptr, false);
                }
            } else {
                mac_generic_event_trig(MAC_MLME_EVENT_HANDLER, rf_ptr, true);
            }
        }
    } else {
        rf_ptr->mlme_tick_count--;
        eventOS_callback_timer_start(timer_id, slots);
    }
}

static void mac_mlme_free_scan_temporary_data(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    rf_mac_setup->scan_active = false;
    if (rf_mac_setup->mac_mlme_scan_resp) {
        mlme_scan_conf_t *r = rf_mac_setup->mac_mlme_scan_resp;
        if (r->ED_values) {
            ns_dyn_mem_free(r->ED_values);
            r->ED_values = NULL;
        }
        uint8_t i = 0;
        while (i < r->ResultListSize) {
            if (r->PAN_values[i]) {
                ns_dyn_mem_free(r->PAN_values[i]);
            }
            i++;
        }
        ns_dyn_mem_free(rf_mac_setup->mac_mlme_scan_resp);
        rf_mac_setup->mac_mlme_scan_resp = NULL;
    }
}

void mac_mlme_set_active_state(protocol_interface_rf_mac_setup_s *entry, bool new_state)
{
    if (entry) {
        entry->macUpState = new_state;
    }
}

void mac_mlme_data_base_deallocate(struct protocol_interface_rf_mac_setup *rf_mac)
{
    if (rf_mac) {
        if (rf_mac->dev_driver) {
            rf_mac->dev_driver->phy_sap_identifier = NULL;
            rf_mac->dev_driver->phy_sap_upper_cb = NULL;
        }

        eventOS_callback_timer_unregister(rf_mac->mlme_timer_id);
        eventOS_callback_timer_unregister(rf_mac->mac_timer_id);
        eventOS_callback_timer_unregister(rf_mac->mac_mcps_timer);

        ns_dyn_mem_free(rf_mac->dev_driver_tx_buffer.buf);
        ns_dyn_mem_free(rf_mac->dev_driver_tx_buffer.enhanced_ack_buf);
        ns_dyn_mem_free(rf_mac->mac_beacon_payload);

        mac_sec_mib_deinit(rf_mac);
        ns_dyn_mem_free(rf_mac);
    }
}

static uint8_t mac_backoff_ticks_calc(phy_device_driver_s *phy_driver)
{
    //Calculate 20 symbol time which is typically 10 bytes
    const phy_device_channel_page_s *phy_channel_pages = phy_driver->phy_channel_pages;
    uint32_t datarate = dev_get_phy_datarate(phy_driver, phy_channel_pages->channel_page);
    if (datarate == 0) {
        datarate = 250000;
    }
    //How many 10us ticks backoff period is, assuming 4 bits per symbol (O-QPSK)
    unsigned int ticks = (2000000 / (datarate / 4));
    if (ticks > 255) {
        ticks = 255;
        tr_warn("Backoff period too slow");
    }
    return (uint8_t) ticks;
}

static int mac_mlme_set_symbol_rate(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    if (rf_mac_setup->rf_csma_extension_supported) {
        rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_GET_SYMBOLS_PER_SECOND, (uint8_t *) &rf_mac_setup->symbol_rate);
        rf_mac_setup->symbol_time_us = 1000000 / rf_mac_setup->symbol_rate;
        tr_debug("SW-MAC driver support rf extension %"PRIu32" symbol/seconds  %"PRIu32" us symbol time length", rf_mac_setup->symbol_rate, rf_mac_setup->symbol_time_us);
        return 0;
    }
    return -1;
}

protocol_interface_rf_mac_setup_s *mac_mlme_data_base_allocate(uint8_t *mac64, arm_device_driver_list_s *dev_driver, mac_description_storage_size_t *storage_sizes)
{
    uint16_t total_length = 0;
    //allocate security
    if (!dev_driver || !mac64 || !dev_driver->phy_driver || !storage_sizes) {
        return NULL;
    }

    protocol_interface_rf_mac_setup_s *entry = ns_dyn_mem_alloc(sizeof(protocol_interface_rf_mac_setup_s));
    if (!entry) {
        return NULL;
    }
    //Init everything for 0, NULL or false
    memset(entry, 0, sizeof(protocol_interface_rf_mac_setup_s));
    entry->ifs_timer_id = -1;
    entry->cca_timer_id = -1;
    entry->mlme_timer_id = -1;
    entry->mac_timer_id = -1;
    entry->bc_timer_id = -1;
    entry->mac_interface_id = -1;
    entry->dev_driver = dev_driver;
    entry->aUnitBackoffPeriod = 20; //This can be different in some Platform 20 comes from 12-symbol turnaround and 8 symbol CCA read
    entry->number_of_csma_ca_periods = MAC_DEFAULT_NUMBER_OF_CSMA_PERIODS;
    entry->multi_cca_interval = MAC_DEFAULT_CSMA_MULTI_CCA_INTERVAL;

    if (mac_sec_mib_init(entry, storage_sizes) != 0) {
        mac_mlme_data_base_deallocate(entry);
        return NULL;
    }

    if (!dev_driver->phy_driver->phy_MTU) {
        mac_mlme_data_base_deallocate(entry);
        return NULL;
    }

    //Allocate tx buffer by given MTU + header + tail
    total_length = dev_driver->phy_driver->phy_MTU;
    total_length += (dev_driver->phy_driver->phy_header_length + dev_driver->phy_driver->phy_tail_length);
    entry->dev_driver_tx_buffer.buf = ns_dyn_mem_alloc(total_length);
    if (!entry->dev_driver_tx_buffer.buf) {
        mac_mlme_data_base_deallocate(entry);
        return NULL;
    }

    //allocate Beacon Payload buffer
    entry->max_beacon_payload_length = dev_driver->phy_driver->phy_MTU - MAC_IEEE_802_15_4_MAX_BEACON_OVERHEAD;
    entry->mac_beacon_payload = ns_dyn_mem_alloc(entry->max_beacon_payload_length);
    if (!entry->mac_beacon_payload) {
        mac_mlme_data_base_deallocate(entry);
        return NULL;
    }

    entry->mac_tasklet_id = mac_mcps_sap_tasklet_init();
    if (entry->mac_tasklet_id < 0) {
        mac_mlme_data_base_deallocate(entry);
        return NULL;
    }

    entry->mlme_timer_id = eventOS_callback_timer_register(mac_mlme_timer_cb);
    entry->mac_timer_id = eventOS_callback_timer_register(timer_mac_interrupt);
    entry->mac_mcps_timer = eventOS_callback_timer_register(mac_mcps_timer_cb);
    if (entry->mlme_timer_id == -1 || entry->mac_timer_id == -1 || entry->mac_mcps_timer == -1) {
        mac_mlme_data_base_deallocate(entry);
        return NULL;
    }
    entry->macCapRxOnIdle = true;
    entry->macCapSecrutityCapability = true;
    entry->pan_id = entry->mac_short_address = 0xffff;
    mac_extended_mac_set(entry, mac64);
    entry->mac_ack_wait_duration = MAC_ACK_WAIT_DURATION;
    entry->mac_mlme_retry_max = MAC_DEFAULT_MAX_FRAME_RETRIES;
    memset(entry->mac_default_key_source, 0xff, 8);
    memset(entry->mac_auto_request.Keysource, 0xff, 8);
    memset(entry->mac_beacon_payload, 0, entry->max_beacon_payload_length);
    entry->mac_auto_request.SecurityLevel = 6;
    entry->mac_auto_request.KeyIndex = 0xff;
    mac_pd_sap_rf_low_level_function_set(entry, entry->dev_driver);
    entry->mac_sequence = randLIB_get_8bit();
    entry->mac_bea_sequence = randLIB_get_8bit();
    entry->fhss_api = NULL;
    entry->macMinBE = 3;
    entry->macMaxBE = 5;
    entry->macMaxCSMABackoffs = MAC_CCA_MAX;
    entry->mac_mcps_timer_event.priority = ARM_LIB_LOW_PRIORITY_EVENT;
    entry->mac_mcps_timer_event.event_type = MAC_MCPS_INDIRECT_TIMER_CB;
    entry->mac_mcps_timer_event.data_ptr = entry;
    entry->mac_mcps_timer_event.receiver = entry->mac_tasklet_id;
    entry->mac_mcps_timer_event.sender = 0;
    entry->mac_mcps_timer_event.event_id = 0;
    bool rf_support = false;
    dev_driver->phy_driver->extension(PHY_EXTENSION_DYNAMIC_RF_SUPPORTED, (uint8_t *)&rf_support);
    entry->rf_csma_extension_supported = rf_support;
    dev_driver->phy_driver->extension(PHY_EXTENSION_FILTERING_SUPPORT, (uint8_t *)&entry->mac_frame_filters);
    if (entry->mac_frame_filters & (1 << MAC_FRAME_VERSION_2)) {
        tr_debug("PHY supports 802.15.4-2015 frame filtering");
    }
    mac_mlme_set_symbol_rate(entry);

    //How many 10us ticks backoff period is for waiting 20symbols which is typically 10 bytes time
    entry->backoff_period_in_10us = mac_backoff_ticks_calc(dev_driver->phy_driver);
    return entry;
}

uint8_t mac_mlme_set_new_sqn(protocol_interface_rf_mac_setup_s *rf_setup)
{
    uint8_t ret_val = 0;

    if (rf_setup) {
        rf_setup->mac_sequence++;
        ret_val = rf_setup->mac_sequence;
    }
    return ret_val;
}

uint8_t mac_mlme_set_new_beacon_sqn(protocol_interface_rf_mac_setup_s *rf_setup)
{
    uint8_t ret_val = 0;

    if (rf_setup) {
        rf_setup->mac_bea_sequence++;
        ret_val = rf_setup->mac_bea_sequence;
    }
    return ret_val;
}

static int8_t mac_mlme_set_panid(struct protocol_interface_rf_mac_setup *rf_setup, uint16_t pan_id)
{
    phy_device_driver_s *dev_driver = rf_setup->dev_driver->phy_driver;
    if (!dev_driver->address_write) {
        if (dev_driver->link_type == PHY_LINK_TUN) {
            rf_setup->pan_id = pan_id;
            return 0;
        }
        return -1;
    }

    uint8_t temp_8[2];
    rf_setup->pan_id = pan_id;
    common_write_16_bit(pan_id, temp_8);

    return dev_driver->address_write(PHY_MAC_PANID, temp_8);
}

static void mac_mle_write_mac16_to_phy(phy_device_driver_s *dev_driver, uint16_t mac16)
{
    uint8_t temp[2];
    common_write_16_bit(mac16, temp);
    if (dev_driver->address_write) {
        dev_driver->address_write(PHY_MAC_16BIT, temp);
    }
}

static int8_t mac_mlme_set_mac16(struct protocol_interface_rf_mac_setup *rf_setup, uint16_t mac16)
{
    int8_t ret_val = -1;
    if (rf_setup) {

        rf_setup->mac_short_address = mac16;
        //SET RF 16-bit
        if (mac16 > 0xfffd) {
            rf_setup->shortAdressValid = false;
        } else {
            rf_setup->shortAdressValid = true;
        }
        mac_mle_write_mac16_to_phy(rf_setup->dev_driver->phy_driver, mac16);
        ret_val = 0;
    }
    return ret_val;
}

static void mac_mlme_write_mac64(protocol_interface_rf_mac_setup_s *rf_setup, uint8_t *addrPtr)
{
    memcpy(addrPtr, rf_setup->mac64, 8);
}

static void mac_mlme_write_mac16(protocol_interface_rf_mac_setup_s *rf_setup, uint8_t *addrPtr)
{
    common_write_16_bit(rf_setup->mac_short_address, addrPtr);
}

uint16_t mac_mlme_get_panid(protocol_interface_rf_mac_setup_s *rf_setup)
{
    uint16_t panId = 0xffff;
    if (rf_setup) {
        panId = rf_setup->pan_id;
    }
    return panId;
}

static bool add_or_update_beacon(mlme_scan_conf_t *conf, mlme_beacon_ind_t *data, fhss_api_t *fhss_api)
{
    bool found = false;
    bool update_beacon = false;
    for (int i = 0; i < conf->ResultListSize; i++) {
        mlme_pan_descriptor_t *cur_desc = conf->PAN_values[i];
        if (!cur_desc) { //Not an active or passive scan
            break;
        }
        /* When FHSS is enabled, logical channel check is not valid
         * as the Beacon can be sent on any enabled channel */
        if (fhss_api || (cur_desc->LogicalChannel == data->PANDescriptor.LogicalChannel)) {
            if (cur_desc->CoordPANId == data->PANDescriptor.CoordPANId) {
                if (cur_desc->LinkQuality < data->PANDescriptor.LinkQuality) {
                    cur_desc->CoordAddrMode = data->PANDescriptor.CoordAddrMode;
                    memcpy(cur_desc->CoordAddress, data->PANDescriptor.CoordAddress, 8);
                    cur_desc->LinkQuality = data->PANDescriptor.LinkQuality;
                    update_beacon = true;
                }
                found = true;
            }
        }
    }
    if (!found && conf->ResultListSize != MLME_MAC_RES_SIZE_MAX) {
        mlme_pan_descriptor_t *desc = ns_dyn_mem_temporary_alloc(sizeof(mlme_pan_descriptor_t));
        if (!desc) {
            return false;
        }
        memset(desc, 0, sizeof(mlme_pan_descriptor_t));
        mlme_pan_descriptor_t *dat = &data->PANDescriptor;

        desc->CoordAddrMode = dat->CoordAddrMode;
        desc->CoordPANId = dat->CoordPANId;
        memcpy(desc->CoordAddress, dat->CoordAddress, 8);
        desc->LogicalChannel = dat->LogicalChannel;
        desc->ChannelPage = dat->ChannelPage;
        memcpy(desc->SuperframeSpec, dat->SuperframeSpec, 2);
        desc->GTSPermit = dat->GTSPermit;
        desc->LinkQuality = dat->LinkQuality;
        desc->Timestamp = dat->Timestamp;
        desc->SecurityFailure = dat->SecurityFailure;

        desc->Key.SecurityLevel = dat->Key.SecurityLevel;
        desc->Key.KeyIdMode = dat->Key.KeyIdMode;
        desc->Key.KeyIndex = dat->Key.KeyIndex;
        memcpy(desc->Key.Keysource, dat->Key.Keysource, 8);

        conf->PAN_values[conf->ResultListSize] = desc;
        conf->ResultListSize++;
        update_beacon = true;
    }
    return update_beacon;
}

int mac_mlme_beacon_notify(protocol_interface_rf_mac_setup_s *rf_mac_setup, mlme_beacon_ind_t *data)
{
    bool update_beacon = true;
    bool contains_fhss_synch_info = false;
    if (!rf_mac_setup || !data) {
        return -1;
    }

    /* Cut FHSS synchronization info from Beacon payload length.
     * Synchronization info is stored later in this function but
     * should not be delivered to upper layer as a part of Beacon payload.
     */
    if (rf_mac_setup->fhss_api) {
        if (data->beacon_data_length > FHSS_SYNCH_INFO_START) {
            data->beacon_data_length -= FHSS_SYNCH_INFO_LENGTH;
            contains_fhss_synch_info = true;
        } else {
            // Received single channel Beacon when FHSS enabled.
            return 0;
        }
    }

    mac_api_t *mac = get_sw_mac_api(rf_mac_setup);
    if (mac && mac->mlme_ind_cb) {
        mac->mlme_ind_cb(mac, MLME_BEACON_NOTIFY, data);
    }

    if (rf_mac_setup->mac_mlme_scan_resp) {
        mlme_scan_conf_t *conf = rf_mac_setup->mac_mlme_scan_resp;
        update_beacon = add_or_update_beacon(conf, data, rf_mac_setup->fhss_api);
    }
    if (rf_mac_setup->fhss_api && (update_beacon == true)) {
        if (contains_fhss_synch_info == true) {
            // Assume synchronization info is found from the end of the Beacon payload
            uint8_t *synch_info_start = data->beacon_data + data->beacon_data_length;
            rf_mac_setup->fhss_api->receive_frame(rf_mac_setup->fhss_api, data->PANDescriptor.CoordPANId, data->PANDescriptor.CoordAddress,
                                                  data->PANDescriptor.Timestamp, synch_info_start, FHSS_SYNCH_FRAME);
        }
    }

    return 0;
}

int8_t mac_mlme_virtual_confirmation_handle(int8_t driver_id, const uint8_t *data_ptr, uint16_t length)
{
    (void) length;
    protocol_interface_rf_mac_setup_s *mac_setup = get_sw_mac_ptr_by_driver_id(driver_id);
    if (!mac_setup) {
        return -1;
    }
    mlme_primitive primitive = (mlme_primitive) * data_ptr;
    if (primitive == MLME_SCAN) {
        mlme_scan_conf_t *resp = ns_dyn_mem_temporary_alloc(sizeof(mlme_scan_conf_t));
        if (!resp) {
            return -1;
        }
        memset(resp, 0, sizeof(mlme_scan_conf_t));
        resp->ScanType = MAC_ACTIVE_SCAN;
        mac_setup->mac_mlme_scan_resp = resp;
        mac_mlme_scan_confirmation_handle(mac_setup);
    }
    return 0;
}

static void mac_mlme_start_confirm_handler(protocol_interface_rf_mac_setup_s *rf_ptr, const mlme_start_conf_t *conf)
{
    if (rf_ptr->tun_extension_rf_driver) {
        virtual_data_req_t start_conf;
        uint8_t buf_temp[2];
        uint8_t payload_buf[2];
        // Add some random data as empty payload is not allowed
        payload_buf[0] = 0;
        payload_buf[1] = 0;

        memset(&start_conf, 0, sizeof(virtual_data_req_t));
        buf_temp[0] = NAP_MLME_CONFIRM;
        buf_temp[1] = MLME_START;
        start_conf.parameters = buf_temp;
        start_conf.parameter_length = sizeof(buf_temp);
        start_conf.msdu = payload_buf;
        start_conf.msduLength = sizeof(payload_buf);

        rf_ptr->tun_extension_rf_driver->phy_driver->arm_net_virtual_tx_cb(&start_conf, rf_ptr->tun_extension_rf_driver->id);
    } else if (get_sw_mac_api(rf_ptr)) {
        if (get_sw_mac_api(rf_ptr)->mlme_conf_cb) {
            get_sw_mac_api(rf_ptr)->mlme_conf_cb(get_sw_mac_api(rf_ptr), MLME_START, conf);
        }
    }
}

static void mac_mlme_scan_confirm_handler(protocol_interface_rf_mac_setup_s *rf_ptr, const mlme_scan_conf_t *conf)
{
    if (rf_ptr->tun_extension_rf_driver) {
        virtual_data_req_t scan_conf;
        uint8_t buf_temp[2];
        uint8_t payload_buf[2];
        // Add some random data as empty payload is not allowed
        payload_buf[0] = 0;
        payload_buf[1] = 0;

        memset(&scan_conf, 0, sizeof(virtual_data_req_t));
        buf_temp[0] = NAP_MLME_CONFIRM;
        buf_temp[1] = MLME_SCAN;
        scan_conf.parameters = buf_temp;
        scan_conf.parameter_length = sizeof(buf_temp);
        scan_conf.msdu = payload_buf;
        scan_conf.msduLength = sizeof(payload_buf);

        rf_ptr->tun_extension_rf_driver->phy_driver->arm_net_virtual_tx_cb(&scan_conf, rf_ptr->tun_extension_rf_driver->id);
    } else if (get_sw_mac_api(rf_ptr)) {
        if (get_sw_mac_api(rf_ptr)->mlme_conf_cb) {
            get_sw_mac_api(rf_ptr)->mlme_conf_cb(get_sw_mac_api(rf_ptr), MLME_SCAN, conf);
        }
    }
}

void mac_mlme_scan_confirmation_handle(protocol_interface_rf_mac_setup_s *rf_ptr)
{
    if (!rf_ptr) {
        return;
    }
    mlme_scan_conf_t *r = rf_ptr->mac_mlme_scan_resp;

    if (r) {
        mac_mlme_scan_confirm_handler(rf_ptr, r);
    }
    mac_mlme_free_scan_temporary_data(rf_ptr);
}

void mac_mlme_mac_radio_disabled(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    if (!rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return;
    }
    platform_enter_critical();
    timer_mac_stop(rf_mac_setup);
    mac_mlme_rf_disable(rf_mac_setup);
    platform_exit_critical();
}

void mac_mlme_mac_radio_enable(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    if (!rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return;
    }
    platform_enter_critical();
    mac_mlme_rf_receiver_enable(rf_mac_setup);
    platform_exit_critical();
}

static int8_t mac_mlme_rf_disable(protocol_interface_rf_mac_setup_s *rf_mac_setup)
{
    int8_t ret_val = 0;
    phy_device_driver_s *dev_driver = rf_mac_setup->dev_driver->phy_driver;
    if (!dev_driver->state_control) {
        if (dev_driver->link_type == PHY_LINK_TUN) {
            rf_mac_setup->macRfRadioOn = false;
            rf_mac_setup->macRfRadioTxActive = false;
            return 0;
        }
        return -1;
    }
    if (rf_mac_setup->macRfRadioOn) {
        ret_val = dev_driver->state_control(PHY_INTERFACE_DOWN, 0);
        rf_mac_setup->macRfRadioOn = false;
        rf_mac_setup->macRfRadioTxActive = false;
    }

    return ret_val;
}

static int8_t mac_mlme_rf_receiver_enable(struct protocol_interface_rf_mac_setup *rf_mac_setup)
{
    int8_t retval;

    phy_device_driver_s *dev_driver = rf_mac_setup->dev_driver->phy_driver;
    if (!dev_driver->state_control) {
        if (dev_driver->link_type == PHY_LINK_TUN) {
            rf_mac_setup->macRfRadioOn = true;
            return 0;
        }
        return -1;
    }

    if (rf_mac_setup->macRfRadioOn) {
        return 0;
    }

    if (rf_mac_setup->macProminousMode) {
        tr_debug("Sniffer mode");
        retval = dev_driver->state_control(PHY_INTERFACE_SNIFFER_STATE, rf_mac_setup->mac_channel);
    } else {
        retval = dev_driver->state_control(PHY_INTERFACE_UP, rf_mac_setup->mac_channel);
    }
    rf_mac_setup->macRfRadioOn = true;
    //tr_debug("Enable radio with channel %u", rf_mac_setup->mac_channel);
    return retval;
}

/**
 * Initialize MAC channel selection sequence
 *
 * TODO: initialize channel select sequence
 *       in coordinator mode
 *
 * \param new_channel channel to set
 *
 * \return 0 success
 * \return -1 HW error
 */
static int8_t mac_mlme_rf_channel_set(struct protocol_interface_rf_mac_setup *rf_setup, uint8_t new_channel)
{
    if (new_channel == rf_setup->mac_channel) {
        return 0;
    }
    mac_pre_build_frame_t *buf;

    //Disable allways
    mac_mlme_mac_radio_disabled(rf_setup);
    buf = rf_setup->active_pd_data_request;
    rf_setup->active_pd_data_request = NULL;
    //Set Channel
    rf_setup->mac_channel = new_channel;
    //Enable Radio
    mac_mlme_mac_radio_enable(rf_setup);
    if (buf) {
        mcps_sap_pd_req_queue_write(rf_setup, buf);
    }

    return 0;
}

/**
 * MAC channel change
 *
 * \param new_channel channel to set
 *
 * \return 0 success
 * \return -1 error
 */
int8_t mac_mlme_rf_channel_change(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint8_t new_channel)
{
    if (!rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return -1;
    }

    if (!rf_mac_setup->dev_driver->phy_driver->extension) {
        if (rf_mac_setup->dev_driver->phy_driver->link_type == PHY_LINK_TUN) {
            rf_mac_setup->mac_channel = new_channel;
            return 0;
        }
        return -1;
    }
    if (new_channel == rf_mac_setup->mac_channel) {
        return 0;
    }
    platform_enter_critical();
    if (rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_CHANNEL, &new_channel) == 0) {
        rf_mac_setup->mac_channel = new_channel;
    }
    platform_exit_critical();
    return 0;
}

void mac_mlme_poll_process_confirm(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint8_t status)
{
    if (!rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) {
        return;
    }

    //Free active Data buffer
    if (rf_mac_setup->active_pd_data_request) {
        mcps_sap_prebuild_frame_buffer_free(rf_mac_setup->active_pd_data_request);
        rf_mac_setup->active_pd_data_request = NULL;
    }
    //Disable timer
    rf_mac_setup->macWaitingData = false;
    rf_mac_setup->macDataPollReq = false;
    rf_mac_setup->macRxDataAtPoll = false;

    if (!rf_mac_setup->macCapRxOnIdle) {
        //Disable Radio If we are RX off at idle device
        //tr_debug("disbale by aceptance data");
        if (!rf_mac_setup->macRfRadioTxActive) {
            //Disable radio if no active TX and radio is enabled
            //tr_debug("RF disable");
            mac_mlme_mac_radio_disabled(rf_mac_setup);
        }
    }

    mac_api_t *mac_api = get_sw_mac_api(rf_mac_setup);
    if (mac_api) {
        mlme_poll_conf_t confirm;
        confirm.status = status;
        mac_api->mlme_conf_cb(mac_api, MLME_POLL, &confirm);
    }
    //Trig Packet from queue
    mac_mcps_trig_buffer_from_queue(rf_mac_setup);

}

void mac_mlme_poll_req(protocol_interface_rf_mac_setup_s *cur, const mlme_poll_t *poll_req)
{
    if (!cur || !poll_req) {
        return;
    }
    if (cur->macDataPollReq) {
        tr_debug("Poll Active do not start new");
        return;
    }
    mac_pre_build_frame_t *buf = mcps_sap_prebuild_frame_buffer_get(0);
    if (!buf) {
        tr_debug("No mem for data Req");
        //Confirmation call here
        return;
    }

    buf->fcf_dsn.frametype = FC_CMD_FRAME;
    buf->fcf_dsn.ackRequested = true;
    buf->fcf_dsn.intraPan = true;

    buf->DstPANId = poll_req->CoordPANId;
    buf->SrcPANId = poll_req->CoordPANId;
    buf->fcf_dsn.DstAddrMode = poll_req->CoordAddrMode;
    memcpy(buf->DstAddr, poll_req->CoordAddress, 8);

    buf->mac_command_id = MAC_DATA_REQ;
    buf->mac_payload = &buf->mac_command_id;
    buf->mac_payload_length = 1;
    buf->mac_header_length_with_security = 3;

    mac_header_security_parameter_set(&buf->aux_header, &poll_req->Key);

    if (buf->aux_header.securityLevel) {
        buf->fcf_dsn.securityEnabled = true;
        buf->fcf_dsn.frameVersion = MAC_FRAME_VERSION_2006;

    }
    cur->macDataPollReq = true;
    cur->macWaitingData = true;
    cur->macRxDataAtPoll = false;

    buf->security_mic_len = mac_security_mic_length_get(buf->aux_header.securityLevel);
    buf->mac_header_length_with_security += mac_header_security_aux_header_length(buf->aux_header.securityLevel, buf->aux_header.KeyIdMode);

    if (cur->mac_short_address > 0xfffd) {
        buf->fcf_dsn.SrcAddrMode = MAC_ADDR_MODE_64_BIT;
    } else {
        buf->fcf_dsn.SrcAddrMode = MAC_ADDR_MODE_16_BIT;
    }
    mac_frame_src_address_set_from_interface(buf->fcf_dsn.SrcAddrMode, cur, buf->SrcAddr);
    //Check PanID presents at header
    buf->fcf_dsn.DstPanPresents = mac_dst_panid_present(&buf->fcf_dsn);
    buf->fcf_dsn.SrcPanPresents = mac_src_panid_present(&buf->fcf_dsn);
    buf->mac_header_length_with_security += mac_header_address_length(&buf->fcf_dsn);
    buf->priority = MAC_PD_DATA_MEDIUM_PRIORITY;
    mcps_sap_pd_req_queue_write(cur, buf);
}

static bool mac_mlme_beacon_at_queue(protocol_interface_rf_mac_setup_s *rf_ptr)
{
    mac_pre_build_frame_t *buf = rf_ptr->active_pd_data_request;
    if (buf && buf->fcf_dsn.frametype == FC_BEACON_FRAME) {
        return true;
    }

    buf = rf_ptr->pd_data_request_queue_to_go;

    while (buf) {
        if (buf->fcf_dsn.frametype == FC_BEACON_FRAME) {
            return true;
        }
        buf = buf->next;
    }
    return false;
}

int8_t mac_mlme_beacon_tx(protocol_interface_rf_mac_setup_s *rf_ptr)
{
    if (!rf_ptr || !rf_ptr->macCapCordinator) {
        return -1;
    }

    //Verify if beacon is already generated to queue
    if (mac_mlme_beacon_at_queue(rf_ptr)) {
        return -2;
    }

    uint16_t length = 4;
    length += rf_ptr->mac_beacon_payload_size;
    // Add more space for FHSS synchronization info
    if (rf_ptr->fhss_api) {
        length += FHSS_SYNCH_INFO_LENGTH;
    }
    /* if (rf_ptr->beacon_join_priority_tx_cb_ptr) {
         length += 2;
     }*/

    mac_pre_build_frame_t *buf = mcps_sap_prebuild_frame_buffer_get(length);
    if (!buf) {
        return -1;
    }
    uint8_t sf_2 = 0x0f;

    buf->fcf_dsn.frametype = FC_BEACON_FRAME;
    buf->fcf_dsn.DstAddrMode = MAC_ADDR_MODE_NONE;

    if (rf_ptr->beaconSrcAddressModeLong) {
        buf->fcf_dsn.SrcAddrMode = MAC_ADDR_MODE_64_BIT;
        buf->mac_header_length_with_security = 13;
    } else {
        if (rf_ptr->shortAdressValid) {
            buf->fcf_dsn.SrcAddrMode = MAC_ADDR_MODE_16_BIT;
            buf->mac_header_length_with_security = 7;
        } else {
            buf->fcf_dsn.SrcAddrMode = MAC_ADDR_MODE_64_BIT;
            buf->mac_header_length_with_security = 13;
        }
    }
    buf->SrcPANId = mac_mlme_get_panid(rf_ptr);
    mac_frame_src_address_set_from_interface(buf->fcf_dsn.SrcAddrMode, rf_ptr, buf->SrcAddr);
    buf->fcf_dsn.DstPanPresents = false;
    buf->fcf_dsn.SrcPanPresents = true;

    uint8_t *ptr = buf->mac_payload;

    *ptr++ = 0xff;//Superframe disabled

    if (rf_ptr->macCapCordinator) {
        sf_2 |= 0x40; //Cord
    }

    if (rf_ptr->macCapBatteryPowered) {
        sf_2 |= 0x10; //Battery
    }

    if (rf_ptr->macCapAssocationPermit) {
        sf_2 |= 0x80; //Permit
    }
    *ptr++ = sf_2; //Superframe desc MSB
    ptr = common_write_16_bit(0, ptr); //NO GTS Support

    if (rf_ptr->mac_beacon_payload_size) {
        memcpy(ptr, rf_ptr->mac_beacon_payload, rf_ptr->mac_beacon_payload_size);

        /*if (rf_ptr->beacon_join_priority_tx_cb_ptr) {
            uint8_t beacon_join_priority = rf_ptr->beacon_join_priority_tx_cb_ptr(rf_ptr->mac_interface_id);
            ptr[PLAIN_BEACON_PAYLOAD_SIZE] = BEACON_OPTION_JOIN_PRIORITY_TYPE_LEN;
            ptr[PLAIN_BEACON_PAYLOAD_SIZE + 1] = beacon_join_priority;
            ptr += BEACON_OPTION_JOIN_PRIORITY_LEN;
        }*/
    }
    buf->priority = MAC_PD_DATA_HIGH_PRIOTITY;
    mcps_sap_pd_req_queue_write(rf_ptr, buf);
    sw_mac_stats_update(rf_ptr, STAT_MAC_BEA_TX_COUNT, 0);
    return 0;
}

