/*
 * Copyright (c) 2018-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.
 */

#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "mac_common_defines.h"
#include "mac_api.h"
#include "mac_mcps.h"
#include "common_functions.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/MAC/mac_helper.h"
#include "6LoWPAN/MAC/mpx_api.h"
#include "6LoWPAN/MAC/mac_ie_lib.h"
#include "6LoWPAN/ws/ws_common_defines.h"
#include "6LoWPAN/ws/ws_common.h"
#include "6LoWPAN/ws/ws_bootstrap.h"
#include "6LoWPAN/ws/ws_ie_lib.h"
#include "6LoWPAN/ws/ws_llc.h"
#include "6LoWPAN/ws/ws_mpx_header.h"
#include "6LoWPAN/ws/ws_pae_controller.h"
#include "6LoWPAN/ws/ws_cfg_settings.h"
#include "Security/PANA/pana_eap_header.h"
#include "Security/eapol/eapol_helper.h"
#include "Service_Libs/etx/etx.h"
#include "fhss_ws_extension.h"

#ifdef HAVE_WS

#define TRACE_GROUP "wllc"

#define LLC_MESSAGE_QUEUE_LIST_SIZE_MAX   16 //Do not config over 30 never
#define MPX_USER_SIZE 2

typedef struct {
    uint16_t                user_id;        /**< User ID for identify MPX User */
    mpx_data_confirm        *data_confirm;  /**< User registred MPX Data confirmation call back */
    mpx_data_indication     *data_ind;      /**< User registred MPX Data indication call back */
} mpx_user_t;


typedef struct {
    mpx_api_t   mpx_api;                        /**< API for MPX user like Stack and EAPOL */
    mpx_user_t  mpx_user_table[MPX_USER_SIZE];  /**< MPX user list include registered call back pointers and user id's */
    unsigned    mpx_id: 4;                      /**< MPX class sequence number */
} mpx_class_t;


typedef struct {
    uint16_t                supported_channels;     /**< Configured Channel count. This will define Channel infor mask length to some information element */
    uint16_t                network_name_length;    /**< Network name length */
    uint16_t                vendor_payload_length;  /**< Vendor spesific payload length */
    uint8_t                 vendor_header_length;   /**< Vendor spesific header length */
    uint8_t                 gtkhash_length;         /**< GTK hash length */
    ws_pan_information_t    *pan_congiguration;     /**< Pan configururation */
    struct ws_hopping_schedule_s *hopping_schedule;/**< Channel hopping schedule */
    uint8_t                 *gtkhash;               /**< Pointer to GTK HASH user must give pointer which include 4 64-bit HASH array */
    uint8_t                 *network_name;          /**< Network name */
    uint8_t                 *vendor_header_data;    /**< Vendor spesific header data */
    uint8_t                 *vendor_payload;        /**< Vendor spesific payload data */
} llc_ie_params_t;

typedef struct {
    uint8_t dst_address[8];             /**< Destination address */
    uint16_t pan_id;                    /**< Destination Pan-Id */
    unsigned        messsage_type: 3;   /**< Frame type to UTT */
    unsigned        mpx_id: 5;          /**< MPX sequence */
    bool            ack_requested: 1;   /**< ACK requested */
    bool            eapol_temporary: 1; /**< EAPOL TX entry index used */
    unsigned        dst_address_type: 2; /**<  Destination address type */
    unsigned        src_address_type: 2; /**<  Source address type */
    uint8_t         msg_handle;         /**< LLC genetaed unique MAC handle */
    uint8_t         mpx_user_handle;    /**< This MPX user defined handle */
    ns_ie_iovec_t   ie_vector_list[3];  /**< IE vectors: 1 for Header's, 1 for Payload and for MPX payload */
    mcps_data_req_ie_list_t ie_ext;
    ns_list_link_t  link;               /**< List link entry */
    uint8_t         ie_buffer[];        /**< Trailing buffer data */
} llc_message_t;

/** get pointer to Mac header start point*/
#define ws_message_buffer_ptr_get(x)  (&(x)->ie_buffer[0])

typedef NS_LIST_HEAD(llc_message_t, link) llc_message_list_t;

#define MAX_NEIGH_TEMPORARY_MULTICAST_SIZE 5
#define MAX_NEIGH_TEMPORRY_EAPOL_SIZE 20
#define MAX_NEIGH_TEMPORAY_LIST_SIZE (MAX_NEIGH_TEMPORARY_MULTICAST_SIZE + MAX_NEIGH_TEMPORRY_EAPOL_SIZE)

typedef struct {
    ws_neighbor_temp_class_t        neighbour_temporary_table[MAX_NEIGH_TEMPORAY_LIST_SIZE];
    ws_neighbor_temp_list_t         active_multicast_temp_neigh;
    ws_neighbor_temp_list_t         active_eapol_temp_neigh;
    ws_neighbor_temp_list_t         free_temp_neigh;
    llc_message_list_t              llc_eap_pending_list;           /**< Active Message list */
    bool                            active_eapol_session: 1;        /**< Indicating active EAPOL message */
} temp_entriest_t;

/** EDFE response and Enhanced ACK data length */

#define ENHANCED_FRAME_RESPONSE (WH_IE_ELEMENT_HEADER_LENGTH + 2 + WH_IE_ELEMENT_HEADER_LENGTH + 4 + WH_IE_ELEMENT_HEADER_LENGTH + 1 + WH_IE_ELEMENT_HEADER_LENGTH + 5)

typedef struct {
    uint8_t                         mac_handle_base;                /**< Mac handle id base this will be updated by 1 after use */
    uint8_t                         llc_message_list_size;          /**< llc_message_list list size */
    uint16_t                        edfe_rx_wait_timer;
    mpx_class_t                     mpx_data_base;                  /**< MPX data be including USER API Class and user call backs */

    llc_message_list_t              llc_message_list;               /**< Active Message list */
    llc_ie_params_t                 ie_params;                      /**< LLC IE header and Payload data configuration */
    temp_entriest_t                 *temp_entries;

    ws_asynch_ind                   *asynch_ind;                    /**< LLC Asynch data indication call back configured by user */
    ws_asynch_confirm               *asynch_confirm;                /**< LLC Asynch data confirmation call back configured by user */
    ws_neighbor_info_request        *ws_neighbor_info_request_cb;   /**< LLC Neighbour discover API*/
    uint8_t                         ws_enhanced_response_elements[ENHANCED_FRAME_RESPONSE];
    ns_ie_iovec_t                   ws_header_vector;
    protocol_interface_info_entry_t *interface_ptr;                 /**< List link entry */

    ns_list_link_t                  link;                           /**< List link entry */
} llc_data_base_t;

static NS_LIST_DEFINE(llc_data_base_list, llc_data_base_t, link);

static uint16_t ws_wp_nested_message_length(wp_nested_ie_sub_list_t requested_list, llc_ie_params_t *params);
static uint16_t ws_wh_headers_length(wh_ie_sub_list_t requested_list, llc_ie_params_t *params);

/** LLC message local functions */
static llc_message_t *llc_message_discover_by_mac_handle(uint8_t handle, llc_message_list_t *list);
static llc_message_t *llc_message_discover_by_mpx_id(uint8_t handle, llc_message_list_t *list);
static llc_message_t *llc_message_discover_mpx_user_id(uint8_t handle, uint16_t user_id, llc_message_list_t *list);
static void llc_message_free(llc_message_t *message, llc_data_base_t *llc_base);
static void llc_message_id_allocate(llc_message_t *message, llc_data_base_t *llc_base, bool mpx_user);
static llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base);

/** LLC interface sepesific local functions */
static llc_data_base_t *ws_llc_discover_by_interface(struct protocol_interface_info_entry *interface);
static llc_data_base_t *ws_llc_discover_by_mac(const mac_api_t *api);
static llc_data_base_t *ws_llc_discover_by_mpx(const mpx_api_t *api);

static mpx_user_t *ws_llc_mpx_user_discover(mpx_class_t *mpx_class, uint16_t user_id);
static llc_data_base_t *ws_llc_base_allocate(void);
static void ws_llc_mac_confirm_cb(const mac_api_t *api, const mcps_data_conf_t *data, const mcps_data_conf_payload_t *conf_data);
static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext);
static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id);
static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data_req_s *data, uint16_t user_id);
static int8_t ws_llc_mpx_data_cb_register(const mpx_api_t *api, mpx_data_confirm *confirm_cb, mpx_data_indication *indication_cb, uint16_t user_id);
static uint16_t ws_llc_mpx_header_size_get(const mpx_api_t *api, uint16_t user_id);
static uint8_t ws_llc_mpx_data_purge_request(const mpx_api_t *api, struct mcps_purge_s *purge, uint16_t user_id);
static void ws_llc_mpx_init(mpx_class_t *mpx_class);

static void ws_llc_temp_neigh_info_table_reset(temp_entriest_t *base);
static ws_neighbor_temp_class_t *ws_allocate_multicast_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
static ws_neighbor_temp_class_t *ws_llc_discover_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
static void ws_llc_release_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
static ws_neighbor_temp_class_t *ws_allocate_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64);

static void ws_llc_mpx_eapol_send(llc_data_base_t *base, llc_message_t *message);

static bool test_skip_first_init_response = false;
static uint8_t test_drop_data_message = 0;


int8_t ws_test_skip_edfe_data_send(int8_t interface_id, bool skip)
{
    protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur || !ws_info(cur)) {
        return -1;
    }
    test_skip_first_init_response = skip;
    return 0;
}

int8_t  ws_test_drop_edfe_data_frames(int8_t interface_id, uint8_t number_of_dropped_frames)
{
    protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur || !ws_info(cur)) {
        return -1;
    }
    test_drop_data_message = number_of_dropped_frames;
    return 0;
}

/** Discover Message by message handle id */
static llc_message_t *llc_message_discover_by_mac_handle(uint8_t handle, llc_message_list_t *list)
{
    ns_list_foreach(llc_message_t, message, list) {
        if (message->msg_handle == handle) {
            return message;
        }
    }
    return NULL;
}

static llc_message_t *llc_message_discover_by_mpx_id(uint8_t handle, llc_message_list_t *list)
{
    ns_list_foreach(llc_message_t, message, list) {
        if ((message->messsage_type == WS_FT_DATA || message->messsage_type == WS_FT_EAPOL) && message->mpx_id == handle) {
            return message;
        }
    }
    return NULL;
}


static llc_message_t *llc_message_discover_mpx_user_id(uint8_t handle, uint16_t user_id, llc_message_list_t *list)
{
    uint8_t message_type;
    if (user_id == MPX_LOWPAN_ENC_USER_ID) {
        message_type = WS_FT_DATA;
    } else {
        message_type = WS_FT_EAPOL;
    }

    ns_list_foreach(llc_message_t, message, list) {
        if (message->messsage_type == message_type && message->mpx_user_handle == handle) {
            return message;
        }
    }
    return NULL;
}




//Free message and delete from list
static void llc_message_free(llc_message_t *message, llc_data_base_t *llc_base)
{
    ns_list_remove(&llc_base->llc_message_list, message);
    ns_dyn_mem_free(message);
    llc_base->llc_message_list_size--;
}

static void llc_message_id_allocate(llc_message_t *message, llc_data_base_t *llc_base, bool mpx_user)
{
    //Guarantee
    while (1) {
        if (llc_message_discover_by_mac_handle(llc_base->mac_handle_base, &llc_base->llc_message_list)) {
            llc_base->mac_handle_base++;
        } else {
            break;
        }
    }
    if (mpx_user) {
        while (1) {
            if (llc_message_discover_by_mpx_id(llc_base->mpx_data_base.mpx_id, &llc_base->llc_message_list)) {
                llc_base->mpx_data_base.mpx_id++;
            } else {
                break;
            }
        }
    }

    //Storage handle and update base
    message->msg_handle = llc_base->mac_handle_base++;
    if (mpx_user) {
        message->mpx_id = llc_base->mpx_data_base.mpx_id++;
    }
}

static llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base)
{
    if (llc_base->llc_message_list_size >= LLC_MESSAGE_QUEUE_LIST_SIZE_MAX) {
        return NULL;
    }

    llc_message_t *message = ns_dyn_mem_temporary_alloc(sizeof(llc_message_t) + ie_buffer_size);
    if (!message) {
        return NULL;
    }
    message->ack_requested = false;
    message->eapol_temporary = false;
    return message;
}

static llc_data_base_t *ws_llc_discover_by_interface(struct protocol_interface_info_entry *interface)
{
    ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) {
        if (base->interface_ptr == interface) {
            return base;
        }
    }
    return NULL;
}

static llc_data_base_t *ws_llc_discover_by_mac(const mac_api_t *api)
{
    ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) {
        if (base->interface_ptr->mac_api == api) {
            return base;
        }
    }
    return NULL;
}

static llc_data_base_t *ws_llc_discover_by_mpx(const mpx_api_t *api)
{
    ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) {
        if (&base->mpx_data_base.mpx_api == api) {
            return base;
        }
    }
    return NULL;
}

static uint16_t ws_wh_headers_length(wh_ie_sub_list_t requested_list, llc_ie_params_t *params)
{
    uint16_t length = 0;
    if (requested_list.utt_ie) {
        //Static 4 bytes allways UTT
        length += WH_IE_ELEMENT_HEADER_LENGTH + 4;
    }

    if (requested_list.bt_ie) {
        //Static 5 bytes allways
        length += WH_IE_ELEMENT_HEADER_LENGTH + 5;
    }

    if (requested_list.fc_ie) {
        //Static 1 bytes allways
        length += WH_IE_ELEMENT_HEADER_LENGTH + 2;
    }

    if (requested_list.rsl_ie) {
        //Static 1 bytes allways
        length += WH_IE_ELEMENT_HEADER_LENGTH + 1;
    }

    if (requested_list.vh_ie) {
        //Dynamic length
        length += WH_IE_ELEMENT_HEADER_LENGTH + params->vendor_header_length;
    }

    if (requested_list.ea_ie) {
        length += WH_IE_ELEMENT_HEADER_LENGTH + 8;
    }

    return length;
}

static uint16_t ws_wp_nested_message_length(wp_nested_ie_sub_list_t requested_list, llc_ie_params_t *params)
{
    uint16_t length = 0;
    if (requested_list.gtkhash_ie) {
        //Static 32 bytes allways
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->gtkhash_length;
    }

    if (requested_list.net_name_ie) {
        //Dynamic length
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->network_name_length;
    }

    if (requested_list.vp_ie && params->vendor_payload_length) {
        //Dynamic length
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->vendor_payload_length;
    }

    if (requested_list.pan_ie) {
        //Static 5 bytes allways
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH;
        if (params->pan_congiguration) {
            length += 5;
        }
    }

    if (requested_list.pan_version_ie) {
        //Static 2 bytes allways
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH;
        if (params->pan_congiguration) {
            length += 2;
        }
    }

    if (requested_list.bs_ie) {
        ///Dynamic length
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(params->hopping_schedule, false);
    }

    if (requested_list.us_ie) {
        //Dynamic length
        length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(params->hopping_schedule, true);
    }

    return length;
}

static mpx_user_t *ws_llc_mpx_user_discover(mpx_class_t *mpx_class, uint16_t user_id)
{
    for (int i = 0; i < MPX_USER_SIZE; i++) {
        if (mpx_class->mpx_user_table[i].user_id == user_id) {
            return &mpx_class->mpx_user_table[i];
        }
    }
    return NULL;
}

static llc_data_base_t *ws_llc_base_allocate(void)
{
    llc_data_base_t *base = ns_dyn_mem_alloc(sizeof(llc_data_base_t));
    temp_entriest_t *temp_entries = ns_dyn_mem_alloc(sizeof(temp_entriest_t));
    if (!base || !temp_entries) {
        ns_dyn_mem_free(base);
        ns_dyn_mem_free(temp_entries);
        return NULL;
    }
    memset(base, 0, sizeof(llc_data_base_t));
    memset(temp_entries, 0, sizeof(temp_entriest_t));
    ns_list_init(&temp_entries->active_multicast_temp_neigh);
    ns_list_init(&temp_entries->active_eapol_temp_neigh);
    ns_list_init(&temp_entries->free_temp_neigh);
    ns_list_init(&temp_entries->llc_eap_pending_list);
    base->temp_entries = temp_entries;

    ns_list_init(&base->llc_message_list);

    ns_list_add_to_end(&llc_data_base_list, base);
    return base;
}

/** WS LLC MAC data extension confirmation  */
static void ws_llc_mac_confirm_cb(const mac_api_t *api, const mcps_data_conf_t *data, const mcps_data_conf_payload_t *conf_data)
{
    (void) conf_data;
    llc_data_base_t *base = ws_llc_discover_by_mac(api);
    if (!base) {
        return;
    }

    protocol_interface_info_entry_t *interface = base->interface_ptr;
    llc_message_t *message = llc_message_discover_by_mac_handle(data->msduHandle, &base->llc_message_list);
    if (!message) {
        return;
    }

    uint8_t messsage_type = message->messsage_type;
    uint8_t mpx_user_handle = message->mpx_user_handle;
    if (message->eapol_temporary) {
        //Clear
        ws_bootstrap_eapol_tx_temporary_clear(interface);

        if (data->status == MLME_SUCCESS || data->status == MLME_NO_DATA) {
            //Update timeout
            ws_neighbor_temp_class_t *temp_entry = ws_llc_discover_eapol_temp_entry(base->temp_entries, message->dst_address);
            if (temp_entry) {
                //Update Temporary Lifetime
                temp_entry->eapol_temp_info.eapol_timeout = interface->ws_info->cfg->timing.temp_eapol_min_timeout + 1;
            }
        }
    }
    //ETX update
    llc_neighbour_req_t neighbor_info;
    neighbor_info.ws_neighbor = NULL;
    neighbor_info.neighbor = NULL;
    if (message->ack_requested && messsage_type == WS_FT_DATA) {

        bool success = false;

        if (message->dst_address_type == MAC_ADDR_MODE_64_BIT) {
            base->ws_neighbor_info_request_cb(interface, message->dst_address, &neighbor_info, false);
        }
        switch (data->status) {
            case MLME_SUCCESS:
            case MLME_TX_NO_ACK:
            case MLME_NO_DATA:
                if (data->status == MLME_SUCCESS || data->status == MLME_NO_DATA) {
                    success = true;
                }

                if (neighbor_info.ws_neighbor && neighbor_info.neighbor && neighbor_info.neighbor->link_lifetime == WS_NEIGHBOR_LINK_TIMEOUT) {
                    etx_transm_attempts_update(interface->id, 1 + data->tx_retries, success, neighbor_info.neighbor->index, neighbor_info.neighbor->mac64);
                    //TODO discover RSL from Enchanced ACK Header IE elements
                    ws_utt_ie_t ws_utt;
                    if (ws_wh_utt_read(conf_data->headerIeList, conf_data->headerIeListLength, &ws_utt)) {
                        //UTT header
                        if (success) {
                            neighbor_info.neighbor->lifetime = neighbor_info.neighbor->link_lifetime;
                        }

                        ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp, neighbor_info.neighbor->mac64);
                    }

                    int8_t rsl;
                    if (ws_wh_rsl_read(conf_data->headerIeList, conf_data->headerIeListLength, &rsl)) {
                        ws_neighbor_class_rsl_out_calculate(neighbor_info.ws_neighbor, rsl);
                    }
                }

                break;
            default:
                break;
        }

    }
    //Free message
    llc_message_free(message, base);

    if (messsage_type == WS_FT_DATA || messsage_type == WS_FT_EAPOL) {
        mpx_user_t *user_cb;
        uint16_t mpx_user_id;
        if (messsage_type == WS_FT_DATA) {
            mpx_user_id = MPX_LOWPAN_ENC_USER_ID;
        } else {
            mpx_user_id = MPX_KEY_MANAGEMENT_ENC_USER_ID;
            base->temp_entries->active_eapol_session = false;
        }

        user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_user_id);
        if (user_cb && user_cb->data_confirm) {
            //Call MPX registered call back
            mcps_data_conf_t data_conf = *data;
            data_conf.msduHandle = mpx_user_handle;
            user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf);
        }

        if (messsage_type == WS_FT_EAPOL) {
            message = ns_list_get_first(&base->temp_entries->llc_eap_pending_list);
            if (message) {
                //Start A pending EAPOL
                ns_list_remove(&base->temp_entries->llc_eap_pending_list, message);
                ws_llc_mpx_eapol_send(base, message);
            }
        } else {
            if (neighbor_info.ws_neighbor && neighbor_info.neighbor && neighbor_info.neighbor->link_lifetime < WS_NEIGHBOUR_TEMPORARY_NEIGH_MAX_LIFETIME) {
                //Remove temp neighbour
                tr_debug("Remove Temp Entry by TX confirm");
                mac_neighbor_table_neighbor_remove(mac_neighbor_info(interface), neighbor_info.neighbor);
            }
        }

        return;
    }
    //Async message Confirmation
    base->asynch_confirm(base->interface_ptr, messsage_type);

}

static void ws_llc_ack_data_req_ext(const mac_api_t *api, mcps_ack_data_payload_t *data, int8_t rssi, uint8_t lqi)
{
    (void) lqi;
    llc_data_base_t *base = ws_llc_discover_by_mac(api);
    if (!base) {
        return;
    }
    /* Init all by zero */
    memset(data, 0, sizeof(mcps_ack_data_payload_t));
    //Add just 2 header elements to inside 1 block
    data->ie_elements.headerIeVectorList = &base->ws_header_vector;
    base->ws_header_vector.ieBase = base->ws_enhanced_response_elements;

    data->ie_elements.headerIovLength = 1;

    //Write Data to block
    uint8_t *ptr = base->ws_enhanced_response_elements;
    ptr = ws_wh_utt_write(ptr, WS_FT_ACK);
    ptr = ws_wh_rsl_write(ptr, ws_neighbor_class_rsl_from_dbm_calculate(rssi));
    base->ws_header_vector.iovLen = ptr - base->ws_enhanced_response_elements;
}


static llc_data_base_t *ws_llc_mpx_frame_common_validates(const mac_api_t *api, const mcps_data_ind_t *data, ws_utt_ie_t ws_utt)
{
    llc_data_base_t *base = ws_llc_discover_by_mac(api);
    if (!base) {
        return NULL;
    }

    if (!base->ie_params.gtkhash && ws_utt.message_type == WS_FT_DATA) {
        return NULL;
    }

    if (data->SrcAddrMode != ADDR_802_15_4_LONG) {
        return NULL;
    }

    protocol_interface_info_entry_t *interface = base->interface_ptr;

    if (interface->mac_parameters->pan_id != 0xffff && data->SrcPANId != interface->mac_parameters->pan_id) {
        //Drop wrong PAN-id messages in this phase.
        return NULL;
    }

    return base;

}

static mpx_user_t *ws_llc_mpx_header_parse(llc_data_base_t *base, const mcps_data_ie_list_t *ie_ext, mpx_msg_t *mpx_frame, mac_payload_IE_t *mpx_ie)
{

    mpx_ie->id = MAC_PAYLOAD_MPX_IE_GROUP_ID;
    if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, mpx_ie) < 1) {
        // NO MPX
        return NULL;
    }
    //Validate MPX header
    if (!ws_llc_mpx_header_frame_parse(mpx_ie->content_ptr, mpx_ie->length, mpx_frame)) {
        return NULL;
    }

    if (mpx_frame->transfer_type != MPX_FT_FULL_FRAME) {
        return NULL; //Support only FULL Frame's
    }

    // Discover MPX handler
    mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_frame->multiplex_id);
    if (!user_cb || !user_cb->data_ind) {
        return NULL;
    }

    return user_cb;
}


static void ws_llc_data_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext, ws_utt_ie_t ws_utt)
{
    llc_data_base_t *base = ws_llc_mpx_frame_common_validates(api, data, ws_utt);
    if (!base) {
        return;
    }

    //Discover MPX header and handler
    mac_payload_IE_t mpx_ie;
    mpx_msg_t mpx_frame;
    mpx_user_t *user_cb = ws_llc_mpx_header_parse(base, ie_ext, &mpx_frame, &mpx_ie);
    if (!user_cb) {
        return;
    }

    mac_payload_IE_t ws_wp_nested;
    ws_us_ie_t us_ie;
    bool us_ie_inline = false;
    bool bs_ie_inline = false;
    ws_wp_nested.id = WS_WP_NESTED_IE;
    ws_bs_ie_t ws_bs_ie;
    if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) > 2) {
        us_ie_inline = ws_wp_nested_us_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &us_ie);
        bs_ie_inline = ws_wp_nested_bs_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &ws_bs_ie);
    }

    protocol_interface_info_entry_t *interface = base->interface_ptr;

    //Validate Unicast shedule Channel Plan
    if (us_ie_inline &&
            (!ws_bootstrap_validate_channel_plan(&us_ie, interface) ||
             !ws_bootstrap_validate_channel_function(&us_ie, NULL))) {
        //Channel plan or channel function configuration mismatch
        return;
    }

    if (bs_ie_inline && !ws_bootstrap_validate_channel_function(NULL, &ws_bs_ie)) {
        return;
    }

    //Free Old temporary entry
    if (data->Key.SecurityLevel) {
        ws_llc_release_eapol_temp_entry(base->temp_entries, data->SrcAddr);
    }

    llc_neighbour_req_t neighbor_info;
    bool multicast;
    bool request_new_entry;
    if (data->DstAddrMode == ADDR_802_15_4_LONG) {
        multicast = false;
        request_new_entry = us_ie_inline;
    } else {
        multicast = true;
        request_new_entry = false;
    }

    if (!base->ws_neighbor_info_request_cb(interface, data->SrcAddr, &neighbor_info, request_new_entry)) {
        if (!multicast) {
            //tr_debug("Drop message no neighbor");
            return;
        } else {
            //Allocate temporary entry
            ws_neighbor_temp_class_t *temp_entry = ws_allocate_multicast_temp_entry(base->temp_entries, data->SrcAddr);
            neighbor_info.ws_neighbor = &temp_entry->neigh_info_list;
            //Storage Signal info for future ETX update possibility
            temp_entry->mpduLinkQuality = data->mpduLinkQuality;
            temp_entry->signal_dbm = data->signal_dbm;
        }
    }

    if (!multicast && !data->DSN_suppressed && !ws_neighbor_class_neighbor_duplicate_packet_check(neighbor_info.ws_neighbor, data->DSN, data->timestamp)) {
        tr_info("Drop duplicate message");
        return;
    }

    ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp, (uint8_t *) data->SrcAddr);
    if (us_ie_inline) {
        ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &us_ie, &interface->ws_info->hopping_schdule);
    }
    //Update BS if it is part of message
    if (bs_ie_inline) {
        ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie);
    }

    //Update BT if it is part of message
    ws_bt_ie_t ws_bt;
    if (ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt)) {
        ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt, data->timestamp);
        if (neighbor_info.neighbor && neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
            ns_fhss_ws_set_parent(interface->ws_info->fhss_api, neighbor_info.neighbor->mac64, &neighbor_info.ws_neighbor->fhss_data.bc_timing_info, false);
        }
    }

    if (data->DstAddrMode == ADDR_802_15_4_LONG) {
        neighbor_info.ws_neighbor->unicast_data_rx = true;
    }

    // Calculate RSL for all UDATA packets heard
    ws_neighbor_class_rf_sensitivity_calculate(interface->ws_info->device_min_sens, data->signal_dbm);
    ws_neighbor_class_rsl_in_calculate(neighbor_info.ws_neighbor, data->signal_dbm);

    if (neighbor_info.neighbor) {
        //Refresh ETX dbm
        etx_lqi_dbm_update(interface->id, data->mpduLinkQuality, data->signal_dbm, neighbor_info.neighbor->index, neighbor_info.neighbor->mac64);
        if (data->Key.SecurityLevel) {
            //SET trusted state
            mac_neighbor_table_trusted_neighbor(mac_neighbor_info(interface), neighbor_info.neighbor, true);
        }
    }

    mcps_data_ind_t data_ind = *data;
    if (!neighbor_info.neighbor) {
        data_ind.Key.SecurityLevel = 0; //Mark unknow device
    }
    data_ind.msdu_ptr = mpx_frame.frame_ptr;
    data_ind.msduLength = mpx_frame.frame_length;
    user_cb->data_ind(&base->mpx_data_base.mpx_api, &data_ind);

}

static void ws_llc_eapol_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext, ws_utt_ie_t ws_utt)
{
    llc_data_base_t *base = ws_llc_mpx_frame_common_validates(api, data, ws_utt);
    if (!base) {
        return;
    }

    if (data->DstAddrMode != ADDR_802_15_4_LONG) {
        return;
    }

    //Discover MPX header and handler
    mac_payload_IE_t mpx_ie;
    mpx_msg_t mpx_frame;
    mpx_user_t *user_cb = ws_llc_mpx_header_parse(base, ie_ext, &mpx_frame, &mpx_ie);
    if (!user_cb) {
        return;
    }

    mac_payload_IE_t ws_wp_nested;
    ws_us_ie_t us_ie;
    bool us_ie_inline = false;
    bool bs_ie_inline = false;
    ws_wp_nested.id = WS_WP_NESTED_IE;
    ws_bs_ie_t ws_bs_ie;
    if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) > 2) {
        us_ie_inline = ws_wp_nested_us_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &us_ie);
        bs_ie_inline = ws_wp_nested_bs_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &ws_bs_ie);
    }

    protocol_interface_info_entry_t *interface = base->interface_ptr;

    //Validate Unicast shedule Channel Plan
    if (us_ie_inline &&
            (!ws_bootstrap_validate_channel_plan(&us_ie, interface) ||
             !ws_bootstrap_validate_channel_function(&us_ie, NULL))) {
        //Channel plan or channel function configuration mismatch
        return;
    }

    if (bs_ie_inline && !ws_bootstrap_validate_channel_function(NULL, &ws_bs_ie)) {
        return;
    }

    llc_neighbour_req_t neighbor_info;

    if (!base->ws_neighbor_info_request_cb(interface, data->SrcAddr, &neighbor_info, false)) {
        //Allocate temporary entry
        ws_neighbor_temp_class_t *temp_entry = ws_allocate_eapol_temp_entry(base->temp_entries, data->SrcAddr);
        if (!temp_entry) {
            tr_warn("EAPOL temp pool empty");
            return;
        }
        //Update Temporary Lifetime
        temp_entry->eapol_temp_info.eapol_timeout = interface->ws_info->cfg->timing.temp_eapol_min_timeout + 1;

        neighbor_info.ws_neighbor = &temp_entry->neigh_info_list;
        //Storage Signal info for future ETX update possibility
        temp_entry->mpduLinkQuality = data->mpduLinkQuality;
        temp_entry->signal_dbm = data->signal_dbm;
    }
    uint8_t auth_eui64[8];
    ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp, (uint8_t *) data->SrcAddr);
    if (us_ie_inline) {
        ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &us_ie, &interface->ws_info->hopping_schdule);
    }
    //Update BS if it is part of message
    if (bs_ie_inline) {
        ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie);
    }

    //Discover and write Auhtenticator EUI-64
    if (ws_wh_ea_read(ie_ext->headerIeList, ie_ext->headerIeListLength, auth_eui64)) {
        ws_pae_controller_border_router_addr_write(base->interface_ptr, auth_eui64);
    }

    //Update BT if it is part of message
    ws_bt_ie_t ws_bt;
    if (ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt)) {
        ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt, data->timestamp);
        if (neighbor_info.neighbor) {
            ws_bootstrap_eapol_parent_synch(interface, &neighbor_info);
        }
    }


    mcps_data_ind_t data_ind = *data;
    data_ind.msdu_ptr = mpx_frame.frame_ptr;
    data_ind.msduLength = mpx_frame.frame_length;
    user_cb->data_ind(&base->mpx_data_base.mpx_api, &data_ind);
}

static void ws_llc_asynch_indication(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext, ws_utt_ie_t ws_utt)
{
    llc_data_base_t *base = ws_llc_discover_by_mac(api);
    if (!base || !base->asynch_ind) {
        return;
    }

    //Asynch Message

    mac_payload_IE_t ws_wp_nested;

    ws_wp_nested.id = WS_WP_NESTED_IE;
    if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) < 2) {
        // NO WS_WP_NESTED_IE Payload
        return;
    }

    switch (ws_utt.message_type) {
        case WS_FT_PAN_ADVERT:
        case WS_FT_PAN_CONF:
        case WS_FT_PAN_CONF_SOL:
            ws_llc_release_eapol_temp_entry(base->temp_entries, data->SrcAddr);
            break;
        default:
            break;
    }

    mcps_data_ie_list_t asynch_ie_list;
    asynch_ie_list.headerIeList = ie_ext->headerIeList,
    asynch_ie_list.headerIeListLength = ie_ext->headerIeListLength;
    asynch_ie_list.payloadIeList = ws_wp_nested.content_ptr;
    asynch_ie_list.payloadIeListLength = ws_wp_nested.length;
    base->asynch_ind(base->interface_ptr, data, &asynch_ie_list, ws_utt.message_type);
}

/** WS LLC MAC data extension indication  */
static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext)
{


    //Discover Header WH_IE_UTT_TYPE
    ws_utt_ie_t ws_utt;
    if (!ws_wh_utt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_utt)) {
        // NO UTT header
        return;
    }

    if (ws_utt.message_type < WS_FT_DATA) {
        ws_llc_asynch_indication(api, data, ie_ext, ws_utt);
        return;
    }

    if (ws_utt.message_type == WS_FT_DATA) {
        ws_llc_data_indication_cb(api, data, ie_ext, ws_utt);
        return;
    }

    if (ws_utt.message_type == WS_FT_EAPOL) {
        ws_llc_eapol_indication_cb(api, data, ie_ext, ws_utt);
        return;
    }
}

static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id)
{
    //TODO add WS_WP_NESTED_IE support
    uint16_t header_size = 0;
    if (user_id == MPX_LOWPAN_ENC_USER_ID) {
        header_size += 7 + 8 + 5 + 2; //UTT+BTT+ MPX + Padding
        if (base->ie_params.vendor_header_length) {
            header_size += base->ie_params.vendor_header_length + 3;
        }

        if (base->ie_params.vendor_payload_length) {
            header_size += base->ie_params.vendor_payload_length + 2;
        }

        //Dynamic length
        header_size += 2 + WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, true) + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, false);

    } else if (MPX_KEY_MANAGEMENT_ENC_USER_ID) {
        header_size += 7 + 5 + 2;
        //Dynamic length
        header_size += 2 + WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, true);
    }
    return header_size;
}

static bool ws_eapol_handshake_first_msg(uint8_t *pdu, uint16_t length, protocol_interface_info_entry_t *cur)
{
    if (!ws_eapol_relay_state_active(cur)) {
        return false;
    }

    eapol_pdu_t eapol_pdu;
    uint8_t kmp_type = *pdu++;
    length--;
    if (!eapol_parse_pdu_header(pdu, length, &eapol_pdu)) {
        return false;
    }
    if (eapol_pdu.packet_type == EAPOL_EAP_TYPE) {
        if (eapol_pdu.msg.eap.eap_code == EAP_REQ && eapol_pdu.msg.eap.type == EAP_IDENTITY) {
            return true;
        }
    } else {

        uint8_t key_mask = eapol_pdu_key_mask_get(&eapol_pdu);
        if (kmp_type == 6 && key_mask == KEY_INFO_KEY_ACK) {
            //FWK first message validation
            return true;
        } else if (kmp_type == 7 && key_mask == (KEY_INFO_KEY_ACK | KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME)) {
            //GWK first message validation
            return true;
        }
    }

    return false;
}

static void ws_llc_lowpan_mpx_header_set(llc_message_t *message, uint16_t user_id)
{
    uint8_t *ptr = (uint8_t *)message->ie_vector_list[1].ieBase;
    ptr += message->ie_vector_list[1].iovLen;
    ptr  = mac_ie_payload_base_write(ptr, MAC_PAYLOAD_MPX_IE_GROUP_ID, message->ie_vector_list[2].iovLen + 3);
    mpx_msg_t mpx_header;
    mpx_header.transfer_type = MPX_FT_FULL_FRAME;
    mpx_header.transaction_id = message->mpx_id;
    mpx_header.multiplex_id = user_id;
    ptr = ws_llc_mpx_header_write(ptr, &mpx_header);
    message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase;
}

static void ws_llc_lowpan_mpx_data_request(llc_data_base_t *base, mpx_user_t *user_cb, const struct mcps_data_req_s *data)
{
    wh_ie_sub_list_t ie_header_mask;
    memset(&ie_header_mask, 0, sizeof(wh_ie_sub_list_t));

    wp_nested_ie_sub_list_t nested_wp_id;
    memset(&nested_wp_id, 0, sizeof(wp_nested_ie_sub_list_t));
    ie_header_mask.utt_ie = true;

    ie_header_mask.bt_ie = true;
    if (base->ie_params.vendor_header_length) {
        ie_header_mask.vh_ie = true;
    }

    if (base->ie_params.vendor_payload_length) {
        nested_wp_id.vp_ie = true;
    }

    if (data->ExtendedFrameExchange && data->TxAckReq) {
        ie_header_mask.fc_ie = true;
    }
    if (!data->TxAckReq) {
        nested_wp_id.bs_ie = true;
    }

    nested_wp_id.us_ie = true;

    uint16_t ie_header_length = ws_wh_headers_length(ie_header_mask, &base->ie_params);
    uint16_t nested_ie_length = ws_wp_nested_message_length(nested_wp_id, &base->ie_params);

    uint16_t over_head_size = ie_header_length;
    if (nested_ie_length) {
        over_head_size += nested_ie_length + 2;
    }
    //Mpx header size
    over_head_size += 5; //MPX FuLL frame 3 bytes + IE header 2 bytes

    //Allocate Message
    llc_message_t *message = llc_message_allocate(over_head_size, base);
    if (!message) {
        mcps_data_conf_t data_conf;
        memset(&data_conf, 0, sizeof(mcps_data_conf_t));
        data_conf.msduHandle = data->msduHandle;
        data_conf.status = MLME_TRANSACTION_OVERFLOW;
        user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf);
        return;
    }

    //Add To active list
    llc_message_id_allocate(message, base, true);
    base->llc_message_list_size++;
    ns_list_add_to_end(&base->llc_message_list, message);

    mcps_data_req_t data_req;
    message->mpx_user_handle = data->msduHandle;
    message->ack_requested = data->TxAckReq;
    if (data->TxAckReq) {
        message->dst_address_type = data->DstAddrMode;
        memcpy(message->dst_address, data->DstAddr, 8);
    }
    data_req = *data;
    data_req.msdu = NULL;
    data_req.msduLength = 0;
    data_req.msduHandle = message->msg_handle;

    if (data->ExtendedFrameExchange && data->TxAckReq) {
        data_req.SeqNumSuppressed = true;
        data_req.PanIdSuppressed = true;
        data_req.TxAckReq = true; // This will be changed inside MAC
    } else {
        data_req.ExtendedFrameExchange = false; //Do not accept EDFE for non unicast traffic
        if (!data->TxAckReq) {
            data_req.PanIdSuppressed = false;
            data_req.DstAddrMode = MAC_ADDR_MODE_NONE;
        } else {
            data_req.PanIdSuppressed = true;
        }
    }

    uint8_t *ptr = ws_message_buffer_ptr_get(message);
    message->messsage_type = WS_FT_DATA;

    message->ie_vector_list[0].ieBase = ptr;
    if (ie_header_mask.fc_ie) {
        ws_fc_ie_t fc_ie;
        fc_ie.tx_flow_ctrl = 50;//No data at initial frame
        fc_ie.rx_flow_ctrl = 255;
        //Write Flow control for 1 packet send this will be modified at real data send
        ptr = ws_wh_fc_write(ptr, &fc_ie);
    }
    //Write UTT

    ptr = ws_wh_utt_write(ptr, message->messsage_type);
    if (ie_header_mask.bt_ie) {
        ptr = ws_wh_bt_write(ptr);
    }

    if (ie_header_mask.vh_ie) {
        ptr = ws_wh_vh_write(ptr, base->ie_params.vendor_header_data, base->ie_params.vendor_header_length);
    }

    message->ie_vector_list[0].iovLen = ie_header_length;
    message->ie_ext.headerIeVectorList = &message->ie_vector_list[0];
    message->ie_ext.headerIovLength = 1;
    message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1];
    message->ie_ext.payloadIovLength = 2;
    message->ie_vector_list[1].ieBase = ptr;

    if (nested_ie_length) {
        ptr = ws_wp_base_write(ptr, nested_ie_length);
        //Write unicast schedule
        ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true);

        if (nested_wp_id.bs_ie) {
            //Write Broadcastcast schedule
            ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false);
        }
    }
    // SET Payload IE Length
    message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase;
    message->ie_vector_list[2].ieBase = data->msdu;
    message->ie_vector_list[2].iovLen =  data->msduLength;

    ws_llc_lowpan_mpx_header_set(message, MPX_LOWPAN_ENC_USER_ID);
    if (data->ExtendedFrameExchange) {
        message->ie_ext.payloadIovLength = 0; //Set Back 2 at response handler
    }

    base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, NULL);
}

static bool ws_llc_eapol_temp_entry_set(llc_data_base_t *base, const uint8_t *mac64)
{
    //Discover Temporary entry
    ws_neighbor_temp_class_t *temp_neigh = ws_llc_discover_eapol_temp_entry(base->temp_entries, mac64);

    if (!temp_neigh) {
        return false;
    }
    ws_neighbor_class_entry_t *entry = ws_bootstrap_eapol_tx_temporary_set(base->interface_ptr, mac64);
    if (!entry) {
        return false;
    }
    *entry = temp_neigh->neigh_info_list;
    return true;

}


static void ws_llc_eapol_data_req_init(mcps_data_req_t *data_req, llc_message_t *message)
{
    memset(data_req, 0, sizeof(mcps_data_req_t));
    data_req->TxAckReq = message->ack_requested;
    data_req->DstPANId = message->pan_id;
    data_req->SrcAddrMode = message->src_address_type;
    data_req->ExtendedFrameExchange = false;
    if (!data_req->TxAckReq) {
        data_req->PanIdSuppressed = false;
        data_req->DstAddrMode = MAC_ADDR_MODE_NONE;
    } else {
        data_req->PanIdSuppressed = true;
        data_req->DstAddrMode = message->dst_address_type;
        memcpy(data_req->DstAddr, message->dst_address, 8);
    }


    data_req->msdu = NULL;
    data_req->msduLength = 0;
    data_req->msduHandle = message->msg_handle;

    ws_llc_lowpan_mpx_header_set(message, MPX_KEY_MANAGEMENT_ENC_USER_ID);
}

static void ws_llc_mpx_eapol_send(llc_data_base_t *base, llc_message_t *message)
{
    mcps_data_req_t data_req;
    llc_message_id_allocate(message, base, true);
    base->llc_message_list_size++;
    ns_list_add_to_end(&base->llc_message_list, message);
    message->eapol_temporary = ws_llc_eapol_temp_entry_set(base, message->dst_address);
    ws_llc_eapol_data_req_init(&data_req, message);
    base->temp_entries->active_eapol_session = true;
    base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, NULL);
}


static void ws_llc_mpx_eapol_request(llc_data_base_t *base, mpx_user_t *user_cb, const struct mcps_data_req_s *data)
{
    wh_ie_sub_list_t ie_header_mask;
    memset(&ie_header_mask, 0, sizeof(wh_ie_sub_list_t));

    wp_nested_ie_sub_list_t nested_wp_id;
    memset(&nested_wp_id, 0, sizeof(wp_nested_ie_sub_list_t));
    ie_header_mask.utt_ie = true;
    ie_header_mask.bt_ie = ws_eapol_relay_state_active(base->interface_ptr);
    ie_header_mask.ea_ie = ws_eapol_handshake_first_msg(data->msdu, data->msduLength, base->interface_ptr);
    nested_wp_id.bs_ie = ie_header_mask.ea_ie;


    nested_wp_id.us_ie = true;

    uint16_t ie_header_length = ws_wh_headers_length(ie_header_mask, &base->ie_params);
    uint16_t nested_ie_length = ws_wp_nested_message_length(nested_wp_id, &base->ie_params);

    uint16_t over_head_size = ie_header_length;
    if (nested_ie_length) {
        over_head_size += nested_ie_length + 2;
    }
    //Mpx header size
    over_head_size += 5; //MPX FuLL frame 3 bytes + IE header 2 bytes

    //Allocate Message
    llc_message_t *message = llc_message_allocate(over_head_size, base);
    if (!message) {
        mcps_data_conf_t data_conf;
        memset(&data_conf, 0, sizeof(mcps_data_conf_t));
        data_conf.msduHandle = data->msduHandle;
        data_conf.status = MLME_TRANSACTION_OVERFLOW;
        user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf);
        return;
    }
    message->mpx_user_handle = data->msduHandle;
    message->ack_requested = data->TxAckReq;

    message->src_address_type = data->SrcAddrMode;
    memcpy(message->dst_address, data->DstAddr, 8);
    message->dst_address_type = data->DstAddrMode;
    message->pan_id = data->DstPANId;
    message->messsage_type = WS_FT_EAPOL;

    uint8_t *ptr = ws_message_buffer_ptr_get(message);

    message->ie_vector_list[0].ieBase = ptr;
    //Write UTT
    ptr = ws_wh_utt_write(ptr, message->messsage_type);
    if (ie_header_mask.bt_ie) {
        ptr = ws_wh_bt_write(ptr);
    }

    if (ie_header_mask.ea_ie) {
        uint8_t eapol_auth_eui64[8];
        ws_pae_controller_border_router_addr_read(base->interface_ptr, eapol_auth_eui64);
        ptr = ws_wh_ea_write(ptr, eapol_auth_eui64);
    }

    message->ie_vector_list[0].iovLen = ie_header_length;
    message->ie_ext.headerIeVectorList = &message->ie_vector_list[0];
    message->ie_ext.headerIovLength = 1;
    message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1];
    message->ie_ext.payloadIovLength = 2;
    message->ie_vector_list[1].ieBase = ptr;

    if (nested_ie_length) {
        ptr = ws_wp_base_write(ptr, nested_ie_length);
        //Write unicast schedule
        ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true);

        if (nested_wp_id.bs_ie) {
            //Write Broadcastcast schedule
            ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false);
        }
    }

    // SET Payload IE Length
    message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase;
    message->ie_vector_list[2].ieBase = data->msdu;
    message->ie_vector_list[2].iovLen =  data->msduLength;

    if (base->temp_entries->active_eapol_session) {
        //Move to pending list
        ns_list_add_to_end(&base->temp_entries->llc_eap_pending_list, message);
    } else {
        ws_llc_mpx_eapol_send(base, message);
    }

}


static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data_req_s *data, uint16_t user_id)
{
    llc_data_base_t *base = ws_llc_discover_by_mpx(api);
    if (!base) {
        return;
    }

    mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, user_id);
    if (!user_cb || !user_cb->data_confirm || !user_cb->data_ind) {
        return;
    }

    if (user_id == MPX_KEY_MANAGEMENT_ENC_USER_ID) {
        ws_llc_mpx_eapol_request(base, user_cb, data);
    } else if (user_id == MPX_LOWPAN_ENC_USER_ID) {
        ws_llc_lowpan_mpx_data_request(base, user_cb, data);
    }
}


static int8_t ws_llc_mpx_data_cb_register(const mpx_api_t *api, mpx_data_confirm *confirm_cb, mpx_data_indication *indication_cb, uint16_t user_id)
{
    llc_data_base_t *base = ws_llc_discover_by_mpx(api);
    if (!base) {
        return -1;
    }

    mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, user_id);
    if (!user_cb) {
        return -1;
    }
    user_cb->data_confirm = confirm_cb;
    user_cb->data_ind = indication_cb;
    return 0;
}

static uint16_t ws_llc_mpx_header_size_get(const mpx_api_t *api, uint16_t user_id)
{
    llc_data_base_t *base = ws_llc_discover_by_mpx(api);
    if (!base) {
        return 0;
    }

    return ws_mpx_header_size_get(base, user_id);
}

static uint8_t ws_llc_mpx_data_purge_request(const mpx_api_t *api, struct mcps_purge_s *purge, uint16_t user_id)
{
    llc_data_base_t *base = ws_llc_discover_by_mpx(api);
    if (!base) {
        return MLME_INVALID_HANDLE;
    }
    llc_message_t *message = llc_message_discover_mpx_user_id(purge->msduHandle, user_id, &base->llc_message_list);
    if (!message) {
        return MLME_INVALID_HANDLE;
    }

    mcps_purge_t purge_req;
    uint8_t purge_status;
    purge_req.msduHandle = message->msg_handle;
    purge_status = base->interface_ptr->mac_api->mcps_purge_req(base->interface_ptr->mac_api, &purge_req);
    if (purge_status == 0) {
        llc_message_free(message, base);
    }

    return purge_status;
}

static void ws_llc_mpx_init(mpx_class_t *mpx_class)
{
    //Init Mbed Class and API
    mpx_class->mpx_user_table[0].user_id = MPX_LOWPAN_ENC_USER_ID;
    mpx_class->mpx_user_table[1].user_id = MPX_KEY_MANAGEMENT_ENC_USER_ID;
    mpx_class->mpx_api.mpx_headroom_size_get = &ws_llc_mpx_header_size_get;
    mpx_class->mpx_api.mpx_user_registration = &ws_llc_mpx_data_cb_register;
    mpx_class->mpx_api.mpx_data_request = &ws_llc_mpx_data_request;
    mpx_class->mpx_api.mpx_data_purge = &ws_llc_mpx_data_purge_request;
}

static void ws_llc_clean(llc_data_base_t *base)
{
    //Clean Message queue's
    mcps_purge_t purge_req;
    ns_list_foreach_safe(llc_message_t, message, &base->llc_message_list) {
        purge_req.msduHandle = message->msg_handle;
        llc_message_free(message, base);
        base->interface_ptr->mac_api->mcps_purge_req(base->interface_ptr->mac_api, &purge_req);

    }

    ns_list_foreach_safe(llc_message_t, message, &base->temp_entries->llc_eap_pending_list) {
        ns_list_remove(&base->temp_entries->llc_eap_pending_list, message);
        ns_dyn_mem_free(message);
    }
    base->temp_entries->active_eapol_session = false;
    memset(&base->ie_params, 0, sizeof(llc_ie_params_t));

    ws_llc_temp_neigh_info_table_reset(base->temp_entries);
}

static void ws_llc_temp_neigh_info_table_reset(temp_entriest_t *base)
{
    //Empty active list
    ns_list_init(&base->active_multicast_temp_neigh);
    ns_list_init(&base->active_eapol_temp_neigh);
    ns_list_init(&base->free_temp_neigh);

    //Add to free list to full
    for (int i = 0; i < MAX_NEIGH_TEMPORAY_LIST_SIZE; i++) {
        ns_list_add_to_end(&base->free_temp_neigh, &base->neighbour_temporary_table[i]);
    }
}

static ws_neighbor_temp_class_t *ws_llc_discover_mc_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
{
    ns_list_foreach(ws_neighbor_temp_class_t, entry, &base->active_multicast_temp_neigh) {
        if (memcmp(entry->mac64, mac64, 8) == 0) {
            return entry;
        }
    }
    return NULL;
}

static ws_neighbor_temp_class_t *ws_llc_discover_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
{
    ns_list_foreach(ws_neighbor_temp_class_t, entry, &base->active_eapol_temp_neigh) {
        if (memcmp(entry->mac64, mac64, 8) == 0) {
            return entry;
        }
    }
    return NULL;
}

static void ws_llc_release_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
{
    ws_neighbor_temp_class_t *neighbor = ws_llc_discover_eapol_temp_entry(base, mac64);
    if (!neighbor) {
        return;
    }

    ns_list_remove(&base->active_eapol_temp_neigh, neighbor);
    ns_list_add_to_end(&base->free_temp_neigh, neighbor);
}

ws_neighbor_temp_class_t *ws_llc_get_multicast_temp_entry(protocol_interface_info_entry_t *interface, const uint8_t *mac64)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return NULL;
    }

    return ws_llc_discover_mc_temp_entry(base->temp_entries, mac64);
}


static void ws_init_temporary_neigh_data(ws_neighbor_temp_class_t *entry, const uint8_t *mac64)
{
    //Clear Old data
    memset(&entry->neigh_info_list, 0, sizeof(ws_neighbor_class_entry_t));
    entry->neigh_info_list.rsl_in = RSL_UNITITIALIZED;
    entry->neigh_info_list.rsl_out = RSL_UNITITIALIZED;
    memcpy(entry->mac64, mac64, 8);
    entry->eapol_temp_info.eapol_rx_relay_filter = 0;
}



static ws_neighbor_temp_class_t *ws_allocate_multicast_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
{

    ws_neighbor_temp_class_t *entry = ws_llc_discover_mc_temp_entry(base, mac64);
    if (entry) {
        ns_list_remove(&base->active_multicast_temp_neigh, entry);
        ns_list_add_to_start(&base->active_multicast_temp_neigh, entry);
        return entry;
    }

    if (ns_list_count(&base->active_multicast_temp_neigh) < MAX_NEIGH_TEMPORARY_MULTICAST_SIZE) {
        entry = ns_list_get_first(&base->free_temp_neigh);
    }

    if (entry) {
        ns_list_remove(&base->free_temp_neigh, entry);
    } else {
        //Replace last entry and put it to first
        entry = ns_list_get_last(&base->active_multicast_temp_neigh);
        ns_list_remove(&base->active_multicast_temp_neigh, entry);
    }
    //Add to list
    ns_list_add_to_start(&base->active_multicast_temp_neigh, entry);
    //Clear Old data
    ws_init_temporary_neigh_data(entry, mac64);
    return entry;
}

static ws_neighbor_temp_class_t *ws_allocate_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
{

    ws_neighbor_temp_class_t *entry = ws_llc_discover_eapol_temp_entry(base, mac64);
    if (entry) {
        //TODO referesh Timer here
        return entry;
    }

    if (ns_list_count(&base->active_eapol_temp_neigh) < MAX_NEIGH_TEMPORRY_EAPOL_SIZE) {
        entry = ns_list_get_first(&base->free_temp_neigh);
    }

    if (!entry) {
        return NULL;
    }

    ns_list_remove(&base->free_temp_neigh, entry);
    //Add to list
    ns_list_add_to_start(&base->active_eapol_temp_neigh, entry);
    //Clear Old data
    ws_init_temporary_neigh_data(entry, mac64);
    return entry;
}

void ws_llc_free_multicast_temp_entry(protocol_interface_info_entry_t *cur, ws_neighbor_temp_class_t *neighbor)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(cur);
    if (!base) {
        return;
    }
    ns_list_remove(&base->temp_entries->active_multicast_temp_neigh, neighbor);
    ns_list_add_to_end(&base->temp_entries->free_temp_neigh, neighbor);
}


static void  ws_llc_build_edfe_response(llc_data_base_t *base, mcps_edfe_response_t *response_message, ws_fc_ie_t fc_ie)
{
    memset(&response_message->ie_response, 0, sizeof(mcps_data_req_ie_list_t));
    response_message->ie_response.headerIeVectorList = &base->ws_header_vector;
    base->ws_header_vector.ieBase = base->ws_enhanced_response_elements;
    response_message->ie_response.headerIovLength = 1;

    //Write Data to block
    uint8_t *ptr = base->ws_header_vector.ieBase;
    ptr = ws_wh_fc_write(ptr, &fc_ie);
    ptr = ws_wh_utt_write(ptr, WS_FT_DATA);
    ptr = ws_wh_bt_write(ptr);
    ptr = ws_wh_rsl_write(ptr, ws_neighbor_class_rsl_from_dbm_calculate(response_message->rssi));
    base->ws_header_vector.iovLen = ptr - base->ws_enhanced_response_elements;
    response_message->SrcAddrMode = MAC_ADDR_MODE_NONE;
    response_message->wait_response = false;
    response_message->PanIdSuppressed = true;
}

static void  ws_llc_build_edfe_frame(llc_message_t *message, mcps_edfe_response_t *response_message, ws_fc_ie_t fc_ie)
{
    memset(&response_message->ie_response, 0, sizeof(mcps_data_req_ie_list_t));
    uint8_t *ptr = message->ie_vector_list[0].ieBase;
    fc_ie.tx_flow_ctrl = 0;//Put Data with Handshake
    fc_ie.rx_flow_ctrl = 255;
    //Write Flow control for 1 packet send this will be modified at real data send
    ptr = ws_wh_fc_write(ptr, &fc_ie);
    response_message->ie_response.headerIeVectorList = &message->ie_vector_list[0];
    response_message->ie_response.headerIovLength = 1;
    response_message->ie_response.payloadIeVectorList = &message->ie_vector_list[1];
    response_message->ie_response.payloadIovLength = 2;
    response_message->SrcAddrMode = MAC_ADDR_MODE_NONE;
    response_message->wait_response = true;
    response_message->PanIdSuppressed = true;
    //tr_debug("FC:Send Data frame");
    response_message->edfe_message_status = MCPS_EDFE_TX_FRAME;
}

static void ws_llc_mcps_edfe_handler(const mac_api_t *api, mcps_edfe_response_t *response_message)
{
    // INSIDE this shuold not print anything
    response_message->edfe_message_status = MCPS_EDFE_NORMAL_FRAME;
    llc_data_base_t *base = ws_llc_discover_by_mac(api);
    if (!base) {
        return;
    }
    //Discover Here header FC-IE element
    ws_fc_ie_t fc_ie;
    if (!ws_wh_fc_read(response_message->ie_elements.headerIeList, response_message->ie_elements.headerIeListLength, &fc_ie)) {
        return;
    }
    //tr_debug("Flow ctrl(%u TX,%u RX)", fc_ie.tx_flow_ctrl, fc_ie.rx_flow_ctrl);
    if (fc_ie.tx_flow_ctrl == 0 && fc_ie.rx_flow_ctrl) {

        llc_message_t *message = NULL;
        if (response_message->use_message_handle_to_discover) {
            message = llc_message_discover_by_mac_handle(response_message->message_handle, &base->llc_message_list);
        }

        if (!message) {
            //tr_debug("FC:Send a Final Frame");
            if (test_drop_data_message) {
                test_drop_data_message--;
                base->edfe_rx_wait_timer += 99;
                response_message->edfe_message_status = MCPS_EDFE_MALFORMED_FRAME;
                return;
            }
            fc_ie.rx_flow_ctrl = 0;
            base->edfe_rx_wait_timer = 0;
            ws_llc_build_edfe_response(base, response_message, fc_ie);
            response_message->edfe_message_status = MCPS_EDFE_FINAL_FRAME_TX;
        } else {
            if (test_skip_first_init_response) {
                //Skip data send and test timeout at Slave side
                test_skip_first_init_response = false;
                response_message->edfe_message_status = MCPS_EDFE_FINAL_FRAME_RX;
                return;
            }
            ws_llc_build_edfe_frame(message, response_message, fc_ie);
        }

    } else if (fc_ie.tx_flow_ctrl == 0 && fc_ie.rx_flow_ctrl == 0) {
        //tr_debug("FC:Received a Final Frame");
        base->edfe_rx_wait_timer = 0;
        response_message->edfe_message_status = MCPS_EDFE_FINAL_FRAME_RX;
    } else if (fc_ie.tx_flow_ctrl && fc_ie.rx_flow_ctrl) {
        base->edfe_rx_wait_timer = fc_ie.tx_flow_ctrl + 99;
        fc_ie.tx_flow_ctrl = 0;
        fc_ie.rx_flow_ctrl = 255;
        //tr_debug("FC:Send a response");
        //Enable or refesh timeout timer
        ws_llc_build_edfe_response(base, response_message, fc_ie);
        response_message->edfe_message_status = MCPS_EDFE_RESPONSE_FRAME;
    }
}



int8_t ws_llc_create(struct protocol_interface_info_entry *interface, ws_asynch_ind *asynch_ind_cb, ws_asynch_confirm *asynch_cnf_cb, ws_neighbor_info_request *ws_neighbor_info_request_cb)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (base) {
        ws_llc_clean(base);
        return 0;
    }

    //Allocate Data base
    base = ws_llc_base_allocate();
    if (!base) {
        return -2;
    }

    base->interface_ptr = interface;
    base->asynch_ind = asynch_ind_cb;
    base->asynch_confirm = asynch_cnf_cb;
    base->ws_neighbor_info_request_cb = ws_neighbor_info_request_cb;
    //Register MAC Extensions
    base->interface_ptr->mac_api->mac_mcps_extension_enable(base->interface_ptr->mac_api, &ws_llc_mac_indication_cb, &ws_llc_mac_confirm_cb, &ws_llc_ack_data_req_ext);
    base->interface_ptr->mac_api->mac_mcps_edfe_enable(base->interface_ptr->mac_api, &ws_llc_mcps_edfe_handler);
    //Init MPX class
    ws_llc_mpx_init(&base->mpx_data_base);
    ws_llc_temp_neigh_info_table_reset(base->temp_entries);
    return 0;
}

int8_t ws_llc_delete(struct protocol_interface_info_entry *interface)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return -1;
    }

    ws_llc_clean(base);

    ns_list_remove(&llc_data_base_list, base);
    //Disable Mac extension
    base->interface_ptr->mac_api->mac_mcps_extension_enable(base->interface_ptr->mac_api, NULL, NULL, NULL);
    ns_dyn_mem_free(base->temp_entries);
    ns_dyn_mem_free(base);
    return 0;
}



void ws_llc_reset(struct protocol_interface_info_entry *interface)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }
    ws_llc_clean(base);
}

mpx_api_t *ws_llc_mpx_api_get(struct protocol_interface_info_entry *interface)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return NULL;
    }
    return &base->mpx_data_base.mpx_api;
}

int8_t ws_llc_asynch_request(struct protocol_interface_info_entry *interface, asynch_request_t *request)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return -1;
    }

    //Calculate IE Buffer size
    request->wh_requested_ie_list.fc_ie = false; //Never should not be a part Asynch message
    request->wh_requested_ie_list.rsl_ie = false; //Never should not be a part Asynch message
    request->wh_requested_ie_list.vh_ie = false;
    uint16_t header_buffer_length = ws_wh_headers_length(request->wh_requested_ie_list, &base->ie_params);
    uint16_t wp_nested_payload_length = ws_wp_nested_message_length(request->wp_requested_nested_ie_list, &base->ie_params);

    //Allocated
    uint16_t total_length = header_buffer_length;
    if (wp_nested_payload_length) {
        total_length += 2 + wp_nested_payload_length;
    }
    //Allocate LLC message pointer

    llc_message_t *message = llc_message_allocate(total_length, base);
    if (!message) {
        if (base->asynch_confirm) {
            base->asynch_confirm(interface, request->message_type);
        }
        return 0;
    }

    //Add To active list
    llc_message_id_allocate(message, base, false);
    base->llc_message_list_size++;
    ns_list_add_to_end(&base->llc_message_list, message);
    message->messsage_type = request->message_type;


    mcps_data_req_t data_req;
    memset(&data_req, 0, sizeof(mcps_data_req_t));
    data_req.SeqNumSuppressed = true;
    data_req.SrcAddrMode = MAC_ADDR_MODE_64_BIT;
    data_req.Key = request->security;
    data_req.msduHandle = message->msg_handle;
    data_req.ExtendedFrameExchange = false;
    if (request->message_type == WS_FT_PAN_ADVERT_SOL) {
        // PANID not know yet must be supressed
        data_req.PanIdSuppressed = true;
    }

    uint8_t *ptr = ws_message_buffer_ptr_get(message);

    message->ie_vector_list[0].ieBase = ptr;
    message->ie_vector_list[0].iovLen = header_buffer_length;

    message->ie_ext.headerIeVectorList = &message->ie_vector_list[0];
    message->ie_ext.headerIovLength = 1;


    //Write UTT
    if (request->wh_requested_ie_list.utt_ie) {
        ptr = ws_wh_utt_write(ptr, message->messsage_type);
    }

    if (request->wh_requested_ie_list.bt_ie) {
        //Static 5 bytes allways
        ptr = ws_wh_bt_write(ptr);
    }

    if (wp_nested_payload_length) {
        message->ie_vector_list[1].ieBase = ptr;
        message->ie_vector_list[1].iovLen = 2 + wp_nested_payload_length;
        message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1];
        message->ie_ext.payloadIovLength = 1;
        ptr = ws_wp_base_write(ptr, wp_nested_payload_length);

        if (request->wp_requested_nested_ie_list.us_ie) {
            //Write unicast schedule
            ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true);
        }

        if (request->wp_requested_nested_ie_list.bs_ie) {
            //Write Broadcastcast schedule
            ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false);
        }

        if (request->wp_requested_nested_ie_list.pan_ie) {
            //Write Pan information
            ptr = ws_wp_nested_pan_info_write(ptr, base->ie_params.pan_congiguration);
        }

        if (request->wp_requested_nested_ie_list.net_name_ie) {
            //Write network name
            ptr = ws_wp_nested_netname_write(ptr, base->ie_params.network_name, base->ie_params.network_name_length);
        }

        if (request->wp_requested_nested_ie_list.pan_version_ie) {
            //Write pan version
            ptr = ws_wp_nested_pan_ver_write(ptr, base->ie_params.pan_congiguration);
        }

        if (request->wp_requested_nested_ie_list.gtkhash_ie) {
            //Write GTKHASH
            ptr = ws_wp_nested_gtkhash_write(ptr, base->ie_params.gtkhash, base->ie_params.gtkhash_length);
        }

        if (request->wp_requested_nested_ie_list.vp_ie) {
            //Write Vendor spesific payload
            ptr = ws_wp_nested_vp_write(ptr, base->ie_params.vendor_payload, base->ie_params.vendor_payload_length);
        }
    }

    base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, &request->channel_list);

    return 0;
}


void ws_llc_set_vendor_header_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_header, uint8_t vendor_header_length)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }
    base->ie_params.vendor_header_data = vendor_header;
    base->ie_params.vendor_header_length = vendor_header_length;
}


void ws_llc_set_vendor_payload_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_payload, uint8_t vendor_payload_length)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }

    base->ie_params.vendor_payload = vendor_payload;
    base->ie_params.vendor_payload_length = vendor_payload_length;
}


void ws_llc_set_network_name(struct protocol_interface_info_entry *interface, uint8_t *name, uint8_t name_length)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }

    base->ie_params.network_name = name;
    base->ie_params.network_name_length = name_length;
}

void  ws_llc_set_gtkhash(struct protocol_interface_info_entry *interface, uint8_t *gtkhash)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }

    base->ie_params.gtkhash = gtkhash;
    if (base->ie_params.gtkhash) {
        base->ie_params.gtkhash_length = 32;
    } else {
        base->ie_params.gtkhash_length = 0;
    }
}



void ws_llc_set_pan_information_pointer(struct protocol_interface_info_entry *interface, struct ws_pan_information_s *pan_information_pointer)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }

    base->ie_params.pan_congiguration = pan_information_pointer;
}

void ws_llc_hopping_schedule_config(struct protocol_interface_info_entry *interface, struct ws_hopping_schedule_s *hopping_schedule)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }
    base->ie_params.hopping_schedule = hopping_schedule;
}

void ws_llc_fast_timer(struct protocol_interface_info_entry *interface, uint16_t ticks)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base || !base->edfe_rx_wait_timer) {
        return;
    }

    if (ticks > 0xffff / 100) {
        ticks = 0xffff;
    } else if (ticks == 0) {
        ticks = 1;
    } else {
        ticks *= 100;
    }

    if (base->edfe_rx_wait_timer > ticks) {
        base->edfe_rx_wait_timer -= ticks;
    } else {
        base->edfe_rx_wait_timer = 0;
        tr_debug("EDFE Data Wait Timeout");
        //MAC edfe wait data timeout
        if (interface->mac_api && interface->mac_api->mlme_req) {
            mlme_set_t set_req;
            uint8_t value = 0;
            set_req.attr = macEdfeForceStop;
            set_req.attr_index = 0;
            set_req.value_pointer = &value;
            set_req.value_size = 1;
            interface->mac_api->mlme_req(interface->mac_api, MLME_SET, &set_req);
        }
    }
}

void ws_llc_timer_seconds(struct protocol_interface_info_entry *interface, uint16_t seconds_update)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return;
    }

    ns_list_foreach_safe(ws_neighbor_temp_class_t, entry, &base->temp_entries->active_eapol_temp_neigh) {
        if (entry->eapol_temp_info.eapol_timeout <= seconds_update) {
            ns_list_remove(&base->temp_entries->active_eapol_temp_neigh, entry);
            ns_list_add_to_end(&base->temp_entries->free_temp_neigh, entry);
        } else {
            entry->eapol_temp_info.eapol_timeout -= seconds_update;
            if (entry->eapol_temp_info.eapol_rx_relay_filter == 0) {
                //No active filter period
                continue;
            }

            //Update filter time
            if (entry->eapol_temp_info.eapol_rx_relay_filter <= seconds_update) {
                entry->eapol_temp_info.eapol_rx_relay_filter = 0;
            } else {
                entry->eapol_temp_info.eapol_rx_relay_filter -= seconds_update;
            }
        }
    }
}

bool ws_llc_eapol_relay_forward_filter(struct protocol_interface_info_entry *interface, const uint8_t *joiner_eui64, uint8_t mac_sequency, uint32_t rx_timestamp)
{
    llc_data_base_t *base = ws_llc_discover_by_interface(interface);
    if (!base) {
        return false;
    }

    ws_neighbor_temp_class_t *neighbor = ws_llc_discover_eapol_temp_entry(base->temp_entries, joiner_eui64);
    if (!neighbor) {
        llc_neighbour_req_t neighbor_info;
        //Discover here Normal Neighbour
        if (!base->ws_neighbor_info_request_cb(interface, joiner_eui64, &neighbor_info, false)) {
            return false;
        }
        return ws_neighbor_class_neighbor_duplicate_packet_check(neighbor_info.ws_neighbor, mac_sequency, rx_timestamp);
    }

    if (neighbor->eapol_temp_info.eapol_rx_relay_filter && neighbor->eapol_temp_info.last_rx_mac_sequency == mac_sequency) {
        return false;
    }
    neighbor->eapol_temp_info.last_rx_mac_sequency = mac_sequency;
    neighbor->eapol_temp_info.eapol_rx_relay_filter = 6; //Activate 5-5.99 seconds filter time
    return true;

}


#endif
