diff options
Diffstat (limited to 'ucs2-lib/src/ucs_amd.c')
-rw-r--r-- | ucs2-lib/src/ucs_amd.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/ucs2-lib/src/ucs_amd.c b/ucs2-lib/src/ucs_amd.c new file mode 100644 index 0000000..c4d3456 --- /dev/null +++ b/ucs2-lib/src/ucs_amd.c @@ -0,0 +1,570 @@ +/*------------------------------------------------------------------------------------------------*/ +/* UNICENS V2.1.0-3491 */ +/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */ +/* */ +/* This program is free software: you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/* */ +/* You may also obtain this software under a propriety license from Microchip. */ +/* Please contact Microchip for further information. */ +/*------------------------------------------------------------------------------------------------*/ + +/*! + * \file + * \brief Implementation of Application Message Distributor + * + * \cond UCS_INTERNAL_DOC + * \addtogroup G_AMD + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "ucs_amd.h" +#include "ucs_misc.h" + +/*! \brief Priority of the Application Message Distribution */ +static const uint8_t AMD_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ +/*! \brief Event which starts the Rx message distribution */ +static const Srv_Event_t AMD_EV_NOTIFY_RX = 1U; +/*! \brief Event triggers notification of messages in tx_notify_queue */ +static const Srv_Event_t AMD_EV_NOTIFY_TX = 2U; +/*! \brief FBlockID of FBlock NetBlock */ +static const uint8_t AMD_FB_NETBLOCK = 1U; +/*! \brief FBlockID of FBlock NetworkMaster */ +static const uint8_t AMD_FB_NETWORKMASTER = 2U; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Amd_Service(void *self); +static void Amd_OnAmsComplete(void* self, Ucs_AmsRx_Msg_t* msg_ptr); +static void Amd_OnEvent(void *self, void *error_code_ptr); +static void Amd_OnTerminateEvent(void *self, void *error_code_ptr); +static void Amd_RxFlush(CAmd *self, CDlList *list_ptr); +#ifdef AMD_TX_DISTRIB +static bool Amd_TxIsRcmMsg(Ucs_AmsTx_Msg_t *msg_ptr); +static bool Amd_TxReceiveInternal(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr); +static void Amd_TxProcessNotifyQueue(CAmd *self); +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CAmd + * \param self The instance + * \param base_ptr Reference to base services + * \param ams_ptr Reference to the AMS + */ +void Amd_Ctor(CAmd *self, CBase *base_ptr, CAms *ams_ptr) +{ + MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */ + + self->base_ptr = base_ptr; + self->ams_ptr = ams_ptr; + + self->started = false; + Srv_Ctor(&self->service, AMD_SRV_PRIO, self, &Amd_Service); /* register service */ + (void)Scd_AddService(&self->base_ptr->scd, &self->service); + + Dl_Ctor(&self->pre_queue, self->base_ptr->ucs_user_ptr); /* init preprocessor queue */ + Dl_Ctor(&self->rx_queue, self->base_ptr->ucs_user_ptr); /* init Rx queue */ + /* register event observer */ + Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Amd_OnEvent); + Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer); + /* register termination events */ + Mobs_Ctor(&self->terminate_observer, self, EH_M_TERMINATION_EVENTS, &Amd_OnTerminateEvent); + Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->terminate_observer); + + Ams_RxAssignReceiver(self->ams_ptr, &Amd_OnAmsComplete, self); +} + + +#ifdef AMD_TX_DISTRIB +/*! \brief Constructor of class CAmd + * \param self The instance + * \param base_ptr Reference to base services + * \param ams_ptr Reference to the AMS + * \param pool_ptr Reference to the AMS message pool + * \param inic_ptr Reference to the INIC + * \param net_ptr Reference to the network management + */ +void Amd_Ctor(CAmd *self, CBase *base_ptr, CAms *ams_ptr, CAmsMsgPool *pool_ptr, CInic *inic_ptr, CNetworkManagement *net_ptr) +{ + MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */ + + self->base_ptr = base_ptr; + self->ams_ptr = ams_ptr; + self->pool_ptr = pool_ptr; + self->inic_ptr = inic_ptr; + self->net_ptr = net_ptr; + + self->started = false; + Srv_Ctor(&self->service, AMD_SRV_PRIO, self, &Amd_Service); /* register service */ + (void)Scd_AddService(&self->base_ptr->scd, &self->service); + + Dl_Ctor(&self->tx_notify_queue, self->base_ptr->ucs_inst_id); /* init queues */ + Dl_Ctor(&self->pre_queue, self->base_ptr->ucs_inst_id); /* init preprocessor queue */ + Dl_Ctor(&self->rx_queue, self->base_ptr->ucs_inst_id); /* init Rx queue */ + /* register event observer */ + Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Amd_OnEvent); + Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer); + /* register termination events */ + Mobs_Ctor(&self->terminate_observer, self, EH_M_TERMINATION_EVENTS, &Amd_OnTerminateEvent); + Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->terminate_observer); + + Ams_RxAssignReceiver(self->ams_ptr, &Amd_OnAmsComplete, self); + Ams_TxAssignTrcvSelector(self->ams_ptr, &Amd_TxIsRcmMsg); +} +#endif + +/*! \brief Assigns a pre-processor callback function for Rx messages + * \details This function must be called during initialization time. + * The AMS shall not already run. + * \param self The instance + * \param callback_fptr Reference to the callback function + * \param inst_ptr Reference to the pre-processor + */ +void Amd_AssignPreprocessor(CAmd *self, Amd_RxMsgCompleteCb_t callback_fptr, void *inst_ptr) +{ + if (callback_fptr != NULL) + { + self->preprocess_fptr = callback_fptr; + self->preprocess_inst_ptr = inst_ptr; + + self->first_receive_fptr = callback_fptr; + self->first_receive_inst_ptr = inst_ptr; + self->first_q_ptr = &self->pre_queue; + } +} + +/*! \brief Assigns a receiver callback function for Rx messages + * \details This function must be called during initialization time. + * The AMS shall not already run. + * \param self The instance + * \param callback_fptr Reference to the callback function + * \param inst_ptr Reference to the receiver + */ +void Amd_AssignReceiver(CAmd *self, Amd_RxMsgCompleteCb_t callback_fptr, void *inst_ptr) +{ + if (callback_fptr != NULL) + { + self->receive_fptr = callback_fptr; + self->receive_inst_ptr = inst_ptr; + + if (self->first_receive_fptr == NULL) + { + self->first_receive_fptr = callback_fptr; + self->first_receive_inst_ptr = inst_ptr; + self->first_q_ptr = &self->rx_queue; + } + } +} + +/*! \brief Assigns as callback function which is able to read and modify the Rx message + * \param self The instance + * \param callback_fptr Reference to the callback function + * \param inst_ptr Reference to the instance (owner of the callback function) + */ +void Amd_RxAssignModificator(CAmd *self, Amd_RxModificationCb_t callback_fptr, void *inst_ptr) +{ + if (callback_fptr != NULL) + { + self->rx_modification_fptr = callback_fptr; + self->rx_modification_inst_ptr = inst_ptr; + } +} + +/*! \brief Service function of CAmd + * \details The processing of the Rx queues shall be started asynchronously + * after the initialization has succeeded. + * \param self The instance + */ +static void Amd_Service(void *self) +{ + CAmd *self_ = (CAmd*)self; + Srv_Event_t event_mask; + Srv_GetEvent(&self_->service, &event_mask); + + if((event_mask & AMD_EV_NOTIFY_RX) == AMD_EV_NOTIFY_RX) /* triggered on internal transmission */ + { + Srv_ClearEvent(&self_->service, AMD_EV_NOTIFY_RX); + if ((self_->started != false) && (self_->first_receive_fptr != NULL)) + { + uint16_t size = Dl_GetSize(self_->first_q_ptr); + if (size > 0U) + { + self_->first_receive_fptr(self_->first_receive_inst_ptr); + } + } + } + +#ifdef AMD_TX_DISTRIB + if((event_mask & AMD_EV_NOTIFY_TX) == AMD_EV_NOTIFY_TX) /* notify Tx distribution failure asynchronously */ + { + Srv_ClearEvent(&self_->service, AMD_EV_NOTIFY_TX); + Amd_TxProcessNotifyQueue(self_); + } +#endif +} + +/*------------------------------------------------------------------------------------------------*/ +/* Events */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function which is invoked on completed application message reception + * \param self The instance + * \param msg_ptr Reference to the completed application message + */ +static void Amd_OnAmsComplete(void *self, Ucs_AmsRx_Msg_t *msg_ptr) +{ + CAmd *self_ = (CAmd*)self; + + if (self_->rx_modification_fptr != NULL) + { + self_->rx_modification_fptr(self_->rx_modification_inst_ptr, msg_ptr); + } + + if (self_->first_receive_fptr != NULL) + { + Amsg_RxEnqueue(msg_ptr, self_->first_q_ptr); + + if (self_->started != false) + { + self_->first_receive_fptr(self_->first_receive_inst_ptr); + } + } + else + { + Ams_RxFreeMsg(self_->ams_ptr, msg_ptr); + } +} + +/*! \brief Callback function if an events leads to the termination of the MNS + * \param self The instance + * \param error_code_ptr Reference to the error code + */ +static void Amd_OnTerminateEvent(void *self, void *error_code_ptr) +{ + CAmd *self_ = (CAmd*)self; + MISC_UNUSED(error_code_ptr); + + TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Starting AMD Cleanup", 0U)); + Amd_RxFlush(self_, &self_->pre_queue); + Amd_RxFlush(self_, &self_->rx_queue); +#ifdef AMD_TX_DISTRIB + Amd_TxProcessNotifyQueue(self_); +#endif + TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Finished AMD Cleanup", 0U)); +} + +/*! \brief Callback function which is invoked if the initialization is complete + * \param self The instance + * \param error_code_ptr Reference to the error code + */ +static void Amd_OnEvent(void *self, void *error_code_ptr) +{ + CAmd *self_ = (CAmd*)self; + MISC_UNUSED(error_code_ptr); + + TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Received init complete event", 0U)); + self_->started = true; + Srv_SetEvent(&self_->service, AMD_EV_NOTIFY_RX); +} + +/*! \brief Flushes a given application Rx message queue + * \param self The instance + * \param list_ptr Reference to a list containing application Rx message objects + */ +static void Amd_RxFlush(CAmd *self, CDlList *list_ptr) +{ + Ucs_AmsRx_Msg_t *msg_ptr; + + for (msg_ptr = Amsg_RxDequeue(list_ptr); msg_ptr != NULL; msg_ptr = Amsg_RxDequeue(list_ptr)) + { + Ams_RxFreeMsg(self->ams_ptr, msg_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Pre-processor methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Peeks the front-most application message from the preprocessing queue + * \param self The instance + * \return Returns a reference to the front-most application message or \c NULL if the queue + * is empty. + */ +Ucs_AmsRx_Msg_t* Amd_PrePeekMsg(CAmd *self) +{ + return (Ucs_AmsRx_Msg_t*)(void*)Amsg_RxPeek(&self->pre_queue); +} + +/*! \brief Removes the front-most application message from the preprocessing queue and frees it + * \param self The instance + */ +void Amd_PreReleaseMsg(CAmd *self) +{ + Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->pre_queue); + + if (msg_ptr != NULL) + { + Ams_RxFreeMsg(self->ams_ptr, msg_ptr); + } +} + +/*! \brief Forwards the front-most application message from the preprocessing queue to the Rx queue + * \param self The instance + */ +void Amd_PreForwardMsg(CAmd *self) +{ + Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->pre_queue); + + if (msg_ptr != NULL) + { + if (self->receive_fptr != NULL) + { + Amsg_RxEnqueue(msg_ptr, &self->rx_queue); + self->receive_fptr(self->receive_inst_ptr); + } + else + { + Ams_RxFreeMsg(self->ams_ptr, msg_ptr); + } + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Receiver methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Peeks the front-most application message from the Rx queue + * \param self The instance + * \return Returns a reference to the front-most application message or \c NULL if the queue + * is empty. + */ +Ucs_AmsRx_Msg_t* Amd_RxPeekMsg(CAmd *self) +{ + return (Ucs_AmsRx_Msg_t*)(void*)Amsg_RxPeek(&self->rx_queue); +} + +/*! \brief Removes the front-most application message from the Rx queue and frees it + * \param self The instance + */ +void Amd_RxReleaseMsg(CAmd *self) +{ + Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->rx_queue); + + if (msg_ptr != NULL) + { + Ams_RxFreeMsg(self->ams_ptr, msg_ptr); + } +} + +/*! \brief Retrieves the number of messages which are appended to the Rx queue + * \param self The instance + * \return Returns the number of messages. + */ +uint16_t Amd_RxGetMsgCnt(CAmd *self) +{ + return Dl_GetSize(&self->rx_queue); +} + +#ifdef AMD_TX_DISTRIB +/*------------------------------------------------------------------------------------------------*/ +/* Transmitter methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Distributes a Tx message internally as Rx message + * \param self The instance + * \param msg_ptr The Tx message + * \param tx_complete_sia_fptr Single instance API callback function which is invoked as soon as + * the transmission was finished. + * \param tx_complete_fptr Multi instance callback function which is invoked as soon as + * the transmission was finished. + * \param tx_complete_inst_ptr Instance pointer which is referred when tx_complete_fptr is invoked. + * \return Returns \c UCS_RET_SUCCESS if the message accepted for transmission. + * Returns \c UCS_RET_ERR_PARAM if the message is refused due to invalid message attributes. + */ +Ucs_Return_t Amd_TxSendMsg(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr, + Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr) +{ + Ucs_Return_t ret = UCS_RET_SUCCESS; + bool tx_internal = false; + bool tx_network = false; + bool tx_ignore_nw_failure = false; + bool tx_check_ni = true; /* if false, transmit to INIC even during NET_OFF */ + + if (Ams_TxIsValidMessage(msg_ptr) != false) + { + Net_IsOwnAddrResult_t addr_type = Net_IsOwnAddress(self->net_ptr, msg_ptr->destination_address); + + TR_ASSERT(self->base_ptr->ucs_user_ptr, "[AMS]", (((tx_complete_sia_fptr != NULL) && (tx_complete_fptr != NULL)) == false)); + Amsg_TxSetCompleteCallback(msg_ptr, tx_complete_sia_fptr, tx_complete_fptr, tx_complete_inst_ptr); + + if (msg_ptr->destination_address == UCS_ADDR_INTERNAL) + { + tx_internal = true; /* do not forward internal messages to INIC */ + } + else if ((msg_ptr->fblock_id == AMD_FB_NETBLOCK) || (msg_ptr->fblock_id == AMD_FB_NETWORKMASTER)) + { + if (addr_type == NET_IS_OWN_ADDR_NODE) /* replace own node address by "1" to force INIC internal routing */ + { /* do not replace multicast addresses (these are static and handled by INIC) */ + Amsg_TxReplaceDestinationAddr(msg_ptr, MSG_ADDR_INIC); + } + else if ((addr_type == NET_IS_OWN_ADDR_GROUP) || + (msg_ptr->destination_address == UCS_ADDR_BROADCAST_BLOCKING) || + (msg_ptr->destination_address == UCS_ADDR_BROADCAST_UNBLOCKING)) + { + tx_ignore_nw_failure = true; + } + + tx_network = true; /* route FBlocks NB and NWM to INIC */ + tx_check_ni = false; /* INIC performs checks independent from NI state */ + } + else if ((msg_ptr->destination_address == UCS_ADDR_BROADCAST_BLOCKING) || + (msg_ptr->destination_address == UCS_ADDR_BROADCAST_UNBLOCKING)) + { + tx_internal = true; /* forward broadcast messages to INIC and distribute internally */ + tx_network = true; + } + else + { + switch (addr_type) + { + case NET_IS_OWN_ADDR_NODE: + tx_internal = true; + break; + case NET_IS_OWN_ADDR_GROUP: + tx_internal = true; + tx_network = true; + break; + case NET_IS_OWN_ADDR_NONE: + default: + tx_network = true; + break; + } + } + + if ((Inic_GetAvailability(self->inic_ptr) == UCS_NW_NOT_AVAILABLE) && (tx_check_ni == true)) + { + tx_network = false; /* abort network transmission */ + Amsg_TxUpdateResult(msg_ptr, UCS_MSG_STAT_ERROR_NA_OFF); + } + + if (tx_internal != false) + { + if (Amd_TxReceiveInternal(self, msg_ptr) == false) + { /* internal transmission failed */ + Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_ERRBUF); + } + else + { /* internal transmission succeeded */ + Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_SUCCESS); + } + } + else if (tx_ignore_nw_failure != false) + { /* INIC routing will succeed while NW transmission fails */ + Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_SUCCESS); + } + + if (tx_network == false) + { /* enqueue message to notification queue and set event */ + Amsg_TxEnqueue(msg_ptr, &self->tx_notify_queue); + Srv_SetEvent(&self->service, AMD_EV_NOTIFY_TX); + } + else + { + Ams_TxSendMsgDirect(self->ams_ptr, msg_ptr); + } + } + else + { + ret = UCS_RET_ERR_PARAM; /* invalid message parameters */ + } + + return ret; +} + +/*! \brief Decides whether to root a message to MCM or RCM FIFO + * \param msg_ptr The Tx message object + * \return Returns \c true if a Tx message shall be routed to RCM FIFO, otherwise returns \c false. + */ +static bool Amd_TxIsRcmMsg(Ucs_AmsTx_Msg_t *msg_ptr) +{ + bool ret = false; + if (((msg_ptr->fblock_id == AMD_FB_NETBLOCK) && (msg_ptr->op_type <= UCS_OP_STARTACK)) /* is NB.Command */ + || ((msg_ptr->fblock_id == AMD_FB_NETWORKMASTER) && (msg_ptr->op_type > UCS_OP_STARTACK))) /* or NWM.Report?*/ + { + ret = true; + } + + return ret; +} + +/*! \brief Distributes a Tx message internally as Rx message + * \param self The instance + * \param msg_ptr The Tx message + * \return Returns \c true if the message distributed successfully. + * Returns \c false if the allocation of the Rx message has failed. + */ +static bool Amd_TxReceiveInternal(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr) +{ + bool ret = false; + Ucs_AmsRx_Msg_t *rx_ptr = Amsp_AllocRxObj(self->pool_ptr, msg_ptr->data_size); + + if (rx_ptr != NULL) + { + uint16_t src_addr = UCS_ADDR_INTERNAL; + if (msg_ptr->destination_address != UCS_ADDR_INTERNAL) + { + src_addr = Inic_GetNodeAddress(self->inic_ptr); + } + Amsg_RxBuildFromTx(rx_ptr, msg_ptr, src_addr); + if (self->first_q_ptr != NULL) + { + Amsg_RxEnqueue(rx_ptr, self->first_q_ptr); + Srv_SetEvent(&self->service, AMD_EV_NOTIFY_RX); + ret = true; + } + else + { + Amsp_FreeRxObj(self->pool_ptr, rx_ptr); + TR_FAILED_ASSERT(self->base_ptr->ucs_user_ptr, "[AMD]"); + } + } + + return ret; +} + +/*! \brief Notifies the transmission result for all messages in the tx_notify_queue + * \param self The instance + */ +static void Amd_TxProcessNotifyQueue(CAmd *self) +{ + Ucs_AmsTx_Msg_t *tx_ptr = NULL; + + for (tx_ptr = Amsg_TxDequeue(&self->tx_notify_queue); tx_ptr != NULL; tx_ptr = Amsg_TxDequeue(&self->tx_notify_queue)) + { + /* just just notify completion, the object is automatically freed to the pool */ + Amsg_TxNotifyComplete(tx_ptr, Amsg_TxGetResultCode(tx_ptr), Amsg_TxGetResultInfo(tx_ptr)); + } +} +#endif + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + |