/*
 *
 *
 *  Created on: Feb 1, 2022
 *      Author: tux
 */




//------------------------------------------------------------------------------
//! @file WiMOD_SAP_LORAWAN_ProLink.cpp
//! @ingroup WiMOD_SAP_LORAWAN_PROLINK
//! <!------------------------------------------------------------------------->
//! @brief Implementation of the commands of the LoRaWAN SericeAccessPoint for ProLink Firmware
//! @version 0.1
//! <!------------------------------------------------------------------------->
//!
//!
//!
//! <!--------------------------------------------------------------------------
//! Copyright (c) 2022
//! IMST GmbH
//! Carl-Friedrich Gauss Str. 2-4
//! 47475 Kamp-Lintfort
//! --------------------------------------------------------------------------->
//! @author (FB), IMST
//! <!--------------------------------------------------------------------------
//! Target OS:    none
//! Target CPU:   tbd
//! Compiler:     tbd
//! --------------------------------------------------------------------------->
//! @internal
//! @par Revision History:
//! <PRE>
//!-----------------------------------------------------------------------------
//! Version | Date       | Author | Comment
//!-----------------------------------------------------------------------------
//!
//! </PRE>
//------------------------------------------------------------------------------


/*
 * THIS IS AN EXAMPLE IMPLEMENTATION ACCORDING THE THE HCI SPEC: V2.0
 * FOR FIRMWARE: ProLink_
 *
 * SEE FILE: ProLink_LoRaWAN_EndNode_Modem_HCI_Spec.pdf (version 2.0)
 * for detailed information
 *
 */


//------------------------------------------------------------------------------
//
// Section Includes Files
//
//------------------------------------------------------------------------------

#include "WiMOD_SAP_LORAWAN_ProLink.h"
#include <string.h>

//------------------------------------------------------------------------------
//
// Section public functions
//
//------------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/**
 * @brief Constructor
 *
 * @param hci       Pointer to HCI processor object
 *
 * @param buffer    pointer to storage area for building tx frames; MUST BE VALID
 *
 * @param bufferSize    size of the buffer
 *
 */
WiMOD_SAP_LoRaWAN_ProLink::WiMOD_SAP_LoRaWAN_ProLink(TWiMODLRHCI* hci, UINT8* buffer, UINT16 bufferSize):
    WiMOD_SAP_LoRaWAN(hci, buffer, bufferSize)
{
    ResetDevNonceCallback   = NULL;
    LinkDisconnectCallback  = NULL;
    DeviceTimeAnsCallback   = NULL;
    JoinTxIndCallback       = NULL;
    NoDataIndCallback       = NULL;
    TxCDataIndCallback      = NULL;
    TxUDataIndCallback      = NULL;
    RxUDataIndCallback      = NULL;
    RxCDataIndCallback      = NULL;
    RxMacCmdIndCallback     = NULL;
    JoinedNwkIndCallback    = NULL;
    RxAckIndCallback        = NULL;
    MulticastRxDataCallback = NULL;
    MulticastRxInvalidDataCallback = NULL;

    txPayload = buffer;
    txPayloadSize = bufferSize;

    //region = LoRaWAN_Region_EU868; // default init
    ProLink_region = ProLink_LoRaWAN_Region_EU868;
}

//-----------------------------------------------------------------------------
/**
 * @brief Destructor
 */

WiMOD_SAP_LoRaWAN_ProLink::~WiMOD_SAP_LoRaWAN_ProLink(void) {

}

//-----------------------------------------------------------------------------
/**
 * @brief Setup regional settings for the LoRaWAN Firmware of the WiMOD module
 *
 *
 * @param regionalSetting region code for the firmware
 *
 */

void WiMOD_SAP_LoRaWAN_ProLink::setRegion(TProLinkLoRaWANregion regionalSetting) {
    ProLink_region = regionalSetting;
}


//-----------------------------------------------------------------------------
/**
 * @brief SetDeviceNonce Cmd - Sets the device NONCE of WiMOD
 *
 *
 * @param   devNonce    16bit NONCE value - see LoRaWAN spec for details
 * @param   statusRsp   pointer to store status byte of response mesg from WiMOD
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::SetDeviceNonce(const UINT16 devNonce, UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;


    if (statusRsp && (txPayloadSize >= 2)) {
        HTON16(txPayload, devNonce);

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_SET_DEVNONCE_REQ,
                                           LORAWAN_MSG_SET_DEVNONCE_RSP,
                                           txPayload, 0x02);

        if (result == WiMODLR_RESULT_OK) {
            *statusRsp = HciParser->GetRxMessage().Payload[WiMODLR_HCI_RSP_STATUS_POS];
        }
    }
    return result;
}

//-----------------------------------------------------------------------------
/**
 * @brief Gets the current device NONCE value of the WiMOD
 *
 *
 * @param devNonce       pointer to data value for storing the requested information
 *
 * @param statusRsp Status byte contained in the local response of the module
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::GetDeviceNonce(UINT16* devNonce,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = WiMODLR_HCI_RSP_STATUS_POS + 1;

    if ( devNonce && statusRsp) {
        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_GET_DEVNONCE_REQ,
                                           LORAWAN_MSG_GET_DEVNONCE_RSP,
                                           txPayload, 0x00);

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            *devNonce                   = NTOH16(&rx.Payload[offset]);
            offset += 0x02;

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];
       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;

}


//-----------------------------------------------------------------------------
/**
 * @brief SetJoinNonce Cmd - Sets the Join NONCE value of WiMOD
 *
 *
 * @param   joinNonce    16bit nonce value - see LoRaWAN spec for details
 * @param   statusRsp   pointer to store status byte of response mesg from WiMOD
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::SetJoinNonce(const UINT16 joinNonce, UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;


    if (statusRsp && (txPayloadSize >= 2)) {
        HTON16(txPayload, joinNonce);

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_SET_JOINNONCE_REQ,
                                           LORAWAN_MSG_SET_JOINNONCE_RSP,
                                           txPayload, 0x02);

        if (result == WiMODLR_RESULT_OK) {
            *statusRsp = HciParser->GetRxMessage().Payload[WiMODLR_HCI_RSP_STATUS_POS];
        }
    }
    return result;
}

//-----------------------------------------------------------------------------
/**
 * @brief Gets the current Join NONCE value of the WiMOD
 *
 *
 * @param joinNonce    pointer to data value for storing the requested information
 *
 * @param statusRsp Status byte contained in the local response of the module
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::GetJoinNonce(UINT16* joinNonce,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = WiMODLR_HCI_RSP_STATUS_POS + 1;

    if ( joinNonce && statusRsp) {
        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_GET_JOINNONCE_REQ,
                                           LORAWAN_MSG_GET_JOINNONCE_RSP,
                                           txPayload, 0x00);

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            *joinNonce                   = NTOH16(&rx.Payload[offset]);
            offset += 0x02;

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];
       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;

}


//-----------------------------------------------------------------------------
/**
 * @brief Sets a new radio config parameter set of the WiMOD
 *
 *
 * @param data       pointer to data structure containing the new parameters
 *                   @see TWiMODLORAWAN_TX_Data for details
 *
 * @param statusRsp Status byte contained in the local response of the module
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::SetRadioStackConfig(TWiMODProLinkLORAWAN_RadioStackConfig* data,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = 0;

    if ( data && statusRsp) {

        txPayload[offset++] = data->DataRateIndex;
        txPayload[offset++] = data->TXPowerLevel;
        txPayload[offset++] = data->Options;
        txPayload[offset++] = 0x00; // reserved
        txPayload[offset++] = data->Retransmissions;
        txPayload[offset++] = data->BandIndex;
        txPayload[offset++] = data->HeaderMacCmdCapacity & 0x0F;

        if ((ProLink_region == ProLink_LoRaWAN_Region_US915) ||
            (ProLink_region == ProLink_LoRaWAN_Region_AU915)) {
          txPayload[offset++] = data->SubBandMask1;
          txPayload[offset++] = data->SubBandMask2;
        }

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_SET_RSTACK_CONFIG_REQ,
                                           LORAWAN_MSG_SET_RSTACK_CONFIG_RSP,
                                           txPayload, offset);


        // clear RX field
        data->WrongParamErrCode = 0x00;

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            offset = WiMODLR_HCI_RSP_STATUS_POS + 1;
            if (rx.Length > 1) {
                data->WrongParamErrCode = rx.Payload[offset++];
            }

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];
       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }    return result;

}

//-----------------------------------------------------------------------------
/**
 * @brief Gets the current radio config parameter set of the WiMOD
 *
 *
 * @param data       pointer to data structure for storing the requested information
 *                   @see TWiMODLORAWAN_TX_Data for details
 *
 * @param statusRsp Status byte contained in the local response of the module
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::GetRadioStackConfig(TWiMODProLinkLORAWAN_RadioStackConfig* data,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = WiMODLR_HCI_RSP_STATUS_POS + 1;

    if ( data && statusRsp) {
        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_GET_RSTACK_CONFIG_REQ,
                                           LORAWAN_MSG_GET_RSTACK_CONFIG_RSP,
                                           txPayload, 0x00);

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            data->DataRateIndex         = rx.Payload[offset++];
            data->TXPowerLevel          = rx.Payload[offset++];
            data->Options               = rx.Payload[offset++];
            offset++; // reserved
            data->Retransmissions       = rx.Payload[offset++];
            data->BandIndex             = rx.Payload[offset++];
            data->HeaderMacCmdCapacity  = rx.Payload[offset++];

            if ((ProLink_region == ProLink_LoRaWAN_Region_US915) ||
                (ProLink_region == ProLink_LoRaWAN_Region_AU915)) {
                if (rx.Length > offset) {
                  data->SubBandMask1 = rx.Payload[offset++];
                }
                if (rx.Length > offset) {
                  data->SubBandMask2 = rx.Payload[offset++];
                }
            }

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];
       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;

}


//-----------------------------------------------------------------------------
/**
 * @brief Tries to send transmit U-Data to network server via RF link
 *
 *
 * @param data       pointer to data structure containing the TX-data and options.
 *                   @see TWiMODLORAWAN_TX_Data for details
 *
 * @param statusRsp Status byte contained in the local response of the module
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::SendUData(TWiMODProLinkLORAWAN_TX_Data* data,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = 0;
    UINT8              tmpSize;

    if ( data && (data->Length > 0) && statusRsp) {

        tmpSize = MIN((WiMOD_LORAWAN_TX_PAYLOAD_SIZE-1), data->Length);

        if (txPayloadSize >= tmpSize) {
            txPayload[offset++] = data->Port;
            memcpy(&txPayload[offset], data->Payload, MIN((WiMOD_LORAWAN_TX_PAYLOAD_SIZE-1), tmpSize));
            offset += tmpSize;

            result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                               LORAWAN_MSG_SEND_UDATA_REQ,
                                               LORAWAN_MSG_SEND_UDATA_RSP,
                                               txPayload, offset);
            // copy response status
            if (result == WiMODLR_RESULT_OK) {
                *statusRsp = HciParser->GetRxMessage().Payload[WiMODLR_HCI_RSP_STATUS_POS];
                data->ChannelBlockTime = 0;
                if (HciParser->GetRxMessage().Length >= 5) {
                    data->ChannelBlockTime = NTOH32(&HciParser->GetRxMessage().Payload[WiMODLR_HCI_RSP_CMD_PAYLOAD_POS]);
                }
            }
        } else {
            result = WiMODLR_RESULT_PAYLOAD_LENGTH_ERROR;
        }

    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;
}
//-----------------------------------------------------------------------------
/**
 * @brief Tries to send transmit C-Data to network server via RF link
 *
 *
 * @param data       pointer to data structure containing the TX-data and options.
 *                   @see TWiMODLORAWAN_TX_Data for details
 *
 * @param statusRsp Status byte contained in the local response of the module
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::SendCData(TWiMODProLinkLORAWAN_TX_Data* data,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = 0;

    if ( data && (data->Length > 0) && statusRsp) {

        txPayload[offset++] = data->Port;
        memcpy(&txPayload[offset], data->Payload, MIN((WiMOD_LORAWAN_TX_PAYLOAD_SIZE-1), data->Length));
        offset += MIN((WiMOD_LORAWAN_TX_PAYLOAD_SIZE-1), data->Length);

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_SEND_CDATA_REQ,
                                           LORAWAN_MSG_SEND_CDATA_RSP,
                                           txPayload, offset);
        // copy response status
        if (result == WiMODLR_RESULT_OK) {
            *statusRsp = HciParser->GetRxMessage().Payload[WiMODLR_HCI_RSP_STATUS_POS];
            data->ChannelBlockTime = 0;
            if (HciParser->GetRxMessage().Length >= 5) {
                data->ChannelBlockTime = NTOH32(&HciParser->GetRxMessage().Payload[WiMODLR_HCI_RSP_CMD_PAYLOAD_POS]);
            }
        }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;

}

//-----------------------------------------------------------------------------
/**
 * @brief SendNwkTimeRequest Cmd - Sends a network device time request to WiMOD
 *
 * @param   devTimeInfo pointer to store additional response data
 * @param   statusRsp   pointer to store status byte of response mesg from WiMOD
 *
 * @retval WiMODLR_RESULT_OK     if command transmit to WiMOD was ok
 */
TWiMODLRResultCodes WiMOD_SAP_LoRaWAN_ProLink::SendNwkTimeRequest(TWiMODLORAWAN_DevTimeReqInfo* devTimeInfo,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8               offset = WiMODLR_HCI_RSP_STATUS_POS + 1;


    if (statusRsp && devTimeInfo) {
        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_SEND_DEVICETIMEREQ_REQ,
                                           LORAWAN_MSG_SEND_DEVICETIMEREQ_RSP,
                                           txPayload, 0x00);

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            devTimeInfo->ChannelBlockTime = NTOH32(&rx.Payload[offset]);
            offset += 0x04;

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];
       }
    }
    return result;
}

//-----------------------------------------------------------------------------
/**
 * @brief Convert a received low level HCI-Msg to a high-level DevicetimeAnswser structure
 *
 * This function should be used by the DeviceTimeAnswer MAC command response Indication Callback function
 * prior processing the received data message.
 *
 * @param   RxMsg       Reference to low-level HCI message.
 *                      @warning DO NOT MANIPULATE THESE VALUES !!!
 *
 * @param   loraWanMacCmdData Pointer to the buffer where to store the received data
 *
 * @retval true     if the conversion was successful
 */
bool WiMOD_SAP_LoRaWAN_ProLink::convert(TWiMODLR_HCIMessage&        RxMsg,
                                TWiMODLORAWAN_DevTimeAnsInfo*  info)
{
    UINT8 offset = 0;

    if (info) {
        info->GpsTime = 0;
    }

    if (info && RxMsg.Length >= 1) {

        info->Status = RxMsg.Payload[offset++];

        // check if optional attributes are present
        if (offset < RxMsg.Length) {
            info->GpsTime  = (UINT32) NTOH32(&RxMsg.Payload[offset]);
            offset+= 0x04;
        }
        return true;
    }
    return false;
}

//-----------------------------------------------------------------------------
/**
 * @brief Convert a received low level HCI-Msg to a high-level (RX) Multicast Data structure
 *
 * This function should be used by the Rx MultiCast Data Indication Callback function
 * prior processing the received data message.
 *
 * @param   RxMsg       Reference to low-level HCI message.
 *                      @warning DO NOT MANIPULATE THESE VALUES !!!
 *
 * @param   loraWanMacCmdData Pointer to the buffer where to store the received data
 *
 * @retval true     if the conversion was successful
 */
bool WiMOD_SAP_LoRaWAN_ProLink::convert(TWiMODLR_HCIMessage&        RxMsg,
                                        TWiMODLORAWAN_McastData*    mcastData)
{
    UINT8 offset = 0;

    INT16 dataLen = RxMsg.Length;
    INT16 i;

    if (mcastData) {
        mcastData->Length = 0;
        mcastData->OptionalInfoAvaiable = false;
    }

    if (mcastData && RxMsg.Length >= 1) {

        mcastData->StatusFormat = RxMsg.Payload[offset++] & 0x01;


        if (mcastData->StatusFormat & LORAWAN_FORMAT_EXT_HCI_OUT_ACTIVE) {
            dataLen -= (0x01 + 0x05 + 0x05); // format + app data + rx info
            mcastData->OptionalInfoAvaiable = true;
        } else {
            dataLen -= (0x01 + 0x05); // format + app data; only
            mcastData->OptionalInfoAvaiable = false;
        }

        // get the data
        mcastData->DeviceAddress = NTOH32(&RxMsg.Payload[offset]);
        offset += 0x04;

        mcastData->LoRaWANPort = RxMsg.Payload[offset++];

        // copy app payload
        // do not use memcpy here because of potential negative dataLen
        for (i = 0; i < dataLen; i++) {
            mcastData->AppPayload[i] = RxMsg.Payload[offset++];
            mcastData->Length++;
        }

        // check if optional attributes are present
        if (offset < RxMsg.Length) {
            mcastData->ChannelIndex  = (UINT8) RxMsg.Payload[offset++];
            mcastData->DataRateIndex = (UINT8) RxMsg.Payload[offset++];
            mcastData->RSSI          = (INT8)  RxMsg.Payload[offset++];
            mcastData->SNR           = (INT8)  RxMsg.Payload[offset++];
            mcastData->RxSlot        = (UINT8) RxMsg.Payload[offset++];
        }
        return true;
    }
    return false;
}

//-----------------------------------------------------------------------------
/**
 * @brief Convert a received low level HCI-Msg to a high-level Invalid/No Multicast Data structure
 *
 * This function should be used by the Rx Invalid MultiCast Data Indication Callback function
 * prior processing the received data message.
 *
 * @param   RxMsg       Reference to low-level HCI message.
 *                      @warning DO NOT MANIPULATE THESE VALUES !!!
 *
 * @param   loraWanMacCmdData Pointer to the buffer where to store the received data
 *
 * @retval true     if the conversion was successful
 */
bool WiMOD_SAP_LoRaWAN_ProLink::convert(TWiMODLR_HCIMessage&        RxMsg,
                                        TWiMODLORAWAN_McastNoData*  mcastErrData)
{
    UINT8 offset = 0;

    if (mcastErrData && RxMsg.Length >= 6) {

        mcastErrData->StatusFormat = RxMsg.Payload[offset++] & 0x01;

        mcastErrData->ErrorCode = RxMsg.Payload[offset++] & 0x01;

        mcastErrData->DeviceAddress = NTOH32(&RxMsg.Payload[offset]);
        offset += 0x04;
        return true;
    }
    return false;
}



TWiMODLRResultCodes
WiMOD_SAP_LoRaWAN_ProLink::SetMulticastConfig(TWiMODLORAWAN_McastConfig& mcastCfg,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = 0;

    if (statusRsp
            && (txPayloadSize >= (1+4+WiMODLORAWAN_NWK_SESSION_KEY_LEN+WiMODLORAWAN_APP_SESSION_KEY_LEN))) {

        txPayload[offset++] = (UINT8) mcastCfg.MulticastIndex;
        HTON32(&txPayload[offset], mcastCfg.DeviceAddress);
        offset += 0x04;
        memcpy(&txPayload[offset], mcastCfg.NwkSKey, WiMODLORAWAN_NWK_SESSION_KEY_LEN);
        offset += WiMODLORAWAN_NWK_SESSION_KEY_LEN;
        memcpy(&txPayload[offset], mcastCfg.AppSKey, WiMODLORAWAN_APP_SESSION_KEY_LEN);
        offset += WiMODLORAWAN_APP_SESSION_KEY_LEN;

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_SET_MCAST_CONFIG_REQ,
                                           LORAWAN_MSG_SET_MCAST_CONFIG_RSP,
                                           txPayload, offset);


        offset = WiMODLR_HCI_RSP_STATUS_POS + 1;

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];

       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;
}

TWiMODLRResultCodes
WiMOD_SAP_LoRaWAN_ProLink::GetMulticastConfig(TWiMODLORAWAN_McastConfig* mcastCfg, UINT8* statusRsp) {
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = WiMODLR_HCI_RSP_STATUS_POS;

    if ( mcastCfg && statusRsp) {
        txPayload[offset++] = mcastCfg->MulticastIndex;

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_GET_MCAST_CONFIG_REQ,
                                           LORAWAN_MSG_GET_MCAST_CONFIG_RSP,
                                           txPayload, 0x01);

        memset(mcastCfg->AppSKey, 0x00, WiMODLORAWAN_APP_SESSION_KEY_LEN);
        memset(mcastCfg->NwkSKey, 0x00, WiMODLORAWAN_NWK_SESSION_KEY_LEN);

        if (result == WiMODLR_RESULT_OK) {
            offset = WiMODLR_HCI_RSP_STATUS_POS + 1;
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            mcastCfg->MulticastIndex  = (UINT8) rx.Payload[offset++];
            mcastCfg->MulticastStatus = (UINT8) rx.Payload[offset++];
            mcastCfg->DeviceAddress   = (UINT32) NTOH32(&rx.Payload[offset]);
            offset += 0x04;

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];
       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;
}

TWiMODLRResultCodes
WiMOD_SAP_LoRaWAN_ProLink::RemoveMulticastConfig(UINT8 mcastIndex,
        UINT8* statusRsp)
{
    TWiMODLRResultCodes result = WiMODLR_RESULT_TRANMIT_ERROR;
    UINT8              offset = 0;

    if (statusRsp
            && (txPayloadSize >= (1)) ) {

        txPayload[offset++] = (UINT8) mcastIndex;

        result = HciParser->SendHCIMessage(LORAWAN_SAP_ID,
                                           LORAWAN_MSG_DEL_MCAST_CONFIG_REQ,
                                           LORAWAN_MSG_DEL_MCAST_CONFIG_RSP,
                                           txPayload, offset);


        offset = WiMODLR_HCI_RSP_STATUS_POS + 1;

        if (result == WiMODLR_RESULT_OK) {
            const TWiMODLR_HCIMessage& rx = HciParser->GetRxMessage();

            // copy response status
            *statusRsp = rx.Payload[WiMODLR_HCI_RSP_STATUS_POS];

       }
    } else {
        result = WiMODLR_RESULT_PAYLOAD_PTR_ERROR;
    }
    return result;
}

//-----------------------------------------------------------------------------
/**
 * @brief Register a callback function for the event "Reset DeviceNonce Indication"
 *
 * This registered callback is called when the specified event is called by
 * the WiMOD stack.
 *
 * @param   cb          pointer to a callback function that should be called
 *                      if the event occurs.
 */
void WiMOD_SAP_LoRaWAN_ProLink::RegisterResetDevNonceIndicationClient(TResetDevNonceIndicationCallback cb)
{
    ResetDevNonceCallback =  cb;
}


//-----------------------------------------------------------------------------
/**
 * @brief Register a callback function for the event "Link Disconnect Indication"
 *
 * This registered callback is called when the specified event is called by
 * the WiMOD stack.
 *
 * @param   cb          pointer to a callback function that should be called
 *                      if the event occurs.
 */
void WiMOD_SAP_LoRaWAN_ProLink::RegisterLinkDisconnectIndicationClient(TLinkDisconnectIndicationCallback cb)
{
    LinkDisconnectCallback =  cb;
}

//-----------------------------------------------------------------------------
/**
 * @brief Register a callback function for the event "Nwk DeviceTime Answer Indication"
 *
 * This registered callback is called when the specified event is called by
 * the WiMOD stack.
 *
 * @param   cb          pointer to a callback function that should be called
 *                      if the event occurs.
 */
void WiMOD_SAP_LoRaWAN_ProLink::RegisterDeviceTimeAnsIndicationClient(TNwkDeviceTimeAnsIndicationCallback cb) {
    DeviceTimeAnsCallback = cb;
}

//-----------------------------------------------------------------------------
/**
 * @brief Register a callback function for the event "RX Multicast Data Indication"
 *
 * This registered callback is called when the specified event is called by
 * the WiMOD stack.
 *
 * @param   cb          pointer to a callback function that should be called
 *                      if the event occurs.
 */
void WiMOD_SAP_LoRaWAN_ProLink::RegisterMulticastDataIndicationClient(TMCastDataIndicationCallback cb) {
    MulticastRxDataCallback = cb;
}



//-----------------------------------------------------------------------------
/**
 * @brief Register a callback function for the event "RX Invalid Multicast Data"
 *
 * This registered callback is called when the specified event is called by
 * the WiMOD stack.
 *
 * @param   cb          pointer to a callback function that should be called
 *                      if the event occurs.
 */
void WiMOD_SAP_LoRaWAN_ProLink::RegisterMulticastInvalidDataIndicationClient(TMCastInvalidDataIndicationCallback cb) {
    MulticastRxInvalidDataCallback = cb;
}




//------------------------------------------------------------------------------
//
// Section protected functions
//
//------------------------------------------------------------------------------

/**
 * @internal
 *
 * @brief Dispatch messages from the WiMOD (aka indications)
 *
 * @param rxMsg reference to the complete received HCI message; DO NOT MODIFIY it!
 *
 * @endinternal
 */
void
WiMOD_SAP_LoRaWAN_ProLink::DispatchProLinkLoRaWANMessage(TWiMODLR_HCIMessage& rxMsg) {
    switch (rxMsg.MsgID)
    {
        case LORAWAN_MSG_DEVNONCE_RESET_IND:
            if (ResetDevNonceCallback) {
                ResetDevNonceCallback();
            }
            break;
        case LORAWAN_MSG_LINK_DISCONNECT_IND:
            if (LinkDisconnectCallback) {
                LinkDisconnectCallback();
            }
            break;
        case LORAWAN_MSG_DEVICETIMEANS_IND:
            if (DeviceTimeAnsCallback) {
                DeviceTimeAnsCallback(rxMsg);
            }
            break;
        case LORAWAN_MSG_JOIN_NETWORK_TX_IND:
            if (JoinTxIndCallback) {
                JoinTxIndCallback(rxMsg);
            }
            break;
        case LORAWAN_MSG_RECV_MCAST_DATA_IND:
            if (MulticastRxDataCallback) {
                MulticastRxDataCallback(rxMsg);
            }
            break;
        case LORAWAN_MSG_RECV_MCAST_NO_DATA_IND:
            if (MulticastRxInvalidDataCallback) {
                MulticastRxInvalidDataCallback(rxMsg);
            }
            break;
        default:
            this->DispatchLoRaWANMessage(rxMsg);
            break;
    }
    return;
}



