diff options
author | Christian Gromm <christian.gromm@microchip.com> | 2016-12-08 13:51:04 +0100 |
---|---|---|
committer | Christian Gromm <christian.gromm@microchip.com> | 2016-12-08 13:51:04 +0100 |
commit | 8c5f2324d7aa61669324aec1a0ad091fe1379489 (patch) | |
tree | 29cbdcdbe3657e1400d596ec3d560a2573d80817 /mnsl | |
parent | e89ba11bd9111f02a940f227cf979f1947e975ac (diff) |
src: unicens: import sources
This patch adds the source tree of the NetworkManager v3.0.4.
Additionally, it provides the needed configuration scripts.
Change-Id: I23778b51423b51a4f87741957e0fb208bceb79b3
Signed-off-by: Christian Gromm <christian.gromm@microchip.com>
Diffstat (limited to 'mnsl')
58 files changed, 13402 insertions, 0 deletions
diff --git a/mnsl/mns_alm.c b/mnsl/mns_alm.c new file mode 100644 index 0000000..5c5e76d --- /dev/null +++ b/mnsl/mns_alm.c @@ -0,0 +1,277 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the API locking manager. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ALM + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_alm.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constant */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Interval for garbage collection */ +static const uint16_t ALM_GARBAGE_COLLECTOR_INTERVAL = 2600U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Alm_HandleInternalErrors(void *self, void *error_code_ptr); +static void Alm_GarbageCollector(void *self); +static bool Alm_CheckRegisteredApi(void *current_alm_ptr, void *alm_inst_ptr); +static void Alm_StartTimeout(CApiLockingManager *self); +static void Alm_ClearTimeout(CApiLockingManager *self); +static bool Alm_SearchLockedApi(void *current_alm_ptr, void *alm_inst_ptr); +static void Alm_ResetRegisteredApis(CApiLockingManager *self); +static bool Alm_ResetApi(void *current_alm_ptr, void *alm_inst_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CApiLockingManager */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the API locking manager class. + * \param self Instance pointer + * \param tm_ptr Reference to timer management instance + * \param eh_ptr Reference to event handler instance + * \param mns_inst_id MOST NetServices instance ID + */ +void Alm_Ctor(CApiLockingManager *self, + CTimerManagement *tm_ptr, + CEventHandler *eh_ptr, + uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + T_Ctor(&self->garbage_collector); + self->tm_ptr = tm_ptr; + self->eh_ptr = eh_ptr; + self->mns_inst_id = mns_inst_id; + + /* Observe internal errors and events */ + Mobs_Ctor(&self->internal_error_obs, self, EH_M_TERMINATION_EVENTS, &Alm_HandleInternalErrors); + Eh_AddObsrvInternalEvent(self->eh_ptr, &self->internal_error_obs); +} + +/*! \brief Handles internal errors and events + * \param self Instance pointer + * \param error_code_ptr Reference to reported error code + */ +static void Alm_HandleInternalErrors(void *self, void *error_code_ptr) +{ + CApiLockingManager *self_ = (CApiLockingManager *)self; + MISC_UNUSED(error_code_ptr); + + Tm_ClearTimer(self_->tm_ptr, &self_->garbage_collector); /* Clear timeout */ + Alm_ResetRegisteredApis(self_); /* Reset all registered APIs */ +} + +/*! \brief Checks for API locking timeouts. This method is the callback function of timer + * \c garbage_collector. + * \param self Instance pointer + */ +static void Alm_GarbageCollector(void *self) +{ + CApiLockingManager *self_ = (CApiLockingManager *)self; + (void)Dl_Foreach(&self_->api_list, &Alm_CheckRegisteredApi, self_); +} + +/*! \brief This method is used by Alm_GarbageCollector() to process each registered API. + * \param current_alm_ptr Reference to the current API + * \param alm_inst_ptr Instance of the API locking manager + * \return \c false to process all registered APIs + */ +static bool Alm_CheckRegisteredApi(void *current_alm_ptr, void *alm_inst_ptr) +{ + CApiLockingManager *self = (CApiLockingManager *)alm_inst_ptr; + CApiLocking *alm_ptr_ = (CApiLocking *)current_alm_ptr; + MISC_UNUSED(self); + + if(alm_ptr_->timeout_mask != 0U) + { + Alm_ModuleMask_t tmp_mask = 1U; + while(alm_ptr_->timeout_mask != 0U) + { + if(tmp_mask == (tmp_mask & alm_ptr_->timeout_mask)) + { + Ssub_Notify(&alm_ptr_->subject, &tmp_mask, false); + alm_ptr_->method_mask &= ~tmp_mask; + alm_ptr_->timeout_mask &= ~tmp_mask; + } + tmp_mask <<= 1; + } + Alm_ClearTimeout(self); + } + if(alm_ptr_->method_mask != 0U) + { + alm_ptr_->timeout_mask = alm_ptr_->method_mask; + } + return false; +} + +/*! \brief Registers a new API locking object. + * \param self Instance pointer + * \param al_ptr Reference to the API to register + */ +void Alm_RegisterApi(CApiLockingManager *self, CApiLocking *al_ptr) +{ + Dl_InsertTail(&self->api_list, &al_ptr->node); + Dln_SetData(&al_ptr->node, al_ptr); + al_ptr->alm_ptr = self; +} + +/*! \brief Starts the garbage collecting timer. + * \param self Instance pointer + */ +static void Alm_StartTimeout(CApiLockingManager *self) +{ + if(T_IsTimerInUse(&self->garbage_collector) == false) + { + Tm_SetTimer(self->tm_ptr, + &self->garbage_collector, + &Alm_GarbageCollector, + self, + ALM_GARBAGE_COLLECTOR_INTERVAL, + ALM_GARBAGE_COLLECTOR_INTERVAL); + } +} + +/*! \brief Clears the garbage collecting timer. The timer is clear if no API locking flag is + * currently pending. + * \param self Instance pointer + */ +static void Alm_ClearTimeout(CApiLockingManager *self) +{ + if(NULL == Dl_Foreach(&self->api_list, &Alm_SearchLockedApi, self)) + { + Tm_ClearTimer(self->tm_ptr, &self->garbage_collector); + } +} + +/*! \brief Used by Alm_ClearTimeout() to check if at least one registered API is locked. + * \param current_alm_ptr Reference to the current API locking object + * \param alm_inst_ptr Instance of the API locking manager + * \return \c true if a locked API was found, otherwise \c false + */ +static bool Alm_SearchLockedApi(void *current_alm_ptr, void *alm_inst_ptr) +{ + CApiLocking *alm_ptr_ = (CApiLocking *)current_alm_ptr; + bool ret_val = false; + MISC_UNUSED(alm_inst_ptr); + + if(alm_ptr_->method_mask != 0U) + { + ret_val = true; + } + return ret_val; +} + +/*! \brief Resets all registered APIs. Called if an internal error has been occurred. + * \param self Instance pointer + */ +static void Alm_ResetRegisteredApis(CApiLockingManager *self) +{ + (void)Dl_Foreach(&self->api_list, &Alm_ResetApi, self); +} + +/*! \brief Used by Alm_ResetRegisteredApis() to reset all registered APIs. + * \param current_alm_ptr Reference to the current API locking object + * \param alm_inst_ptr Instance of the API locking manager + * \return \c false (process all registered APIs) + */ +static bool Alm_ResetApi(void *current_alm_ptr, void *alm_inst_ptr) +{ + CApiLocking *alm_ptr_ = (CApiLocking *)current_alm_ptr; + MISC_UNUSED(alm_inst_ptr); + + alm_ptr_->method_mask = 0U; + alm_ptr_->timeout_mask = 0U; + + return false; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CApiLocking */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the API locking class. + * \param self Instance pointer + * \param obs_ptr Observer to signal locked API methods + * \param mns_inst_id MOST NetServices instance ID + */ +void Al_Ctor(CApiLocking *self, CSingleObserver *obs_ptr, uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = mns_inst_id; + Dln_Ctor(&self->node, NULL); + if(obs_ptr != NULL) + { + Ssub_Ctor(&self->subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->subject, obs_ptr); + } +} + +/*! \brief Locks the given API method. + * \param self Instance pointer + * \param method Bitmask of method to lock + * \return \c true if the API has been locked successfully + * \return \c false if the API was already locked + */ +bool Al_Lock(CApiLocking *self, Alm_ModuleMask_t method) +{ + bool ret_val = false; + if((self->method_mask & method) == 0U) + { + ret_val = true; + self->method_mask |= method; + self->timeout_mask &= ~method; + Alm_StartTimeout(self->alm_ptr); + } + return ret_val; +} + +/*! \brief Releases the lock of the given API method. + * \param self Instance pointer + * \param method Bitmask of method to lock + */ +void Al_Release(CApiLocking *self, Alm_ModuleMask_t method) +{ + self->method_mask &= ~method; + self->timeout_mask &= ~method; + Alm_ClearTimeout(self->alm_ptr); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_alm.h b/mnsl/mns_alm.h new file mode 100644 index 0000000..92e3e46 --- /dev/null +++ b/mnsl/mns_alm.h @@ -0,0 +1,113 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the API locking module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ALM + * @{ + */ + +#ifndef MNS_ALM_H +#define MNS_ALM_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_eh.h" +#include "mns_timer.h" +#include "mns_obs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Bitmask used to store locked API methods */ +typedef uint32_t Alm_ModuleMask_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of the API locking manager */ +typedef struct CApiLockingManager_ +{ + CTimerManagement *tm_ptr; /*!< \brief Reference to timer management instance */ + CEventHandler *eh_ptr; /*!< \brief Reference to event handler instance */ + CTimer garbage_collector; /*!< \brief Timer for garbage collection */ + CDlList api_list; /*!< \brief List of registered APIs */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + CMaskedObserver internal_error_obs; /*!< \brief Error observer to handle internal errors and + events */ + +} CApiLockingManager; + +/*! \brief Class structure of the API locking */ +typedef struct CApiLocking_ +{ + CDlNode node; /*!< \brief Node of the doubly linked (API-) list */ + CApiLockingManager *alm_ptr; /*!< \brief Reference to CApiLockingManager instance */ + Alm_ModuleMask_t method_mask; /*!< \brief Bitmask which holds locked API methods */ + Alm_ModuleMask_t timeout_mask; /*!< \brief Bitmask to report timeouts */ + CSingleSubject subject; /*!< \brief Subject to update registered observer */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CApiLocking; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CApiLockingManager */ +/*------------------------------------------------------------------------------------------------*/ +extern void Alm_Ctor(CApiLockingManager *self, + CTimerManagement *tm_ptr, + CEventHandler *eh_ptr, + uint8_t mns_inst_id); +extern void Alm_RegisterApi(CApiLockingManager *self, CApiLocking *al_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CApiLocking */ +/*------------------------------------------------------------------------------------------------*/ +extern void Al_Ctor(CApiLocking *self, CSingleObserver *obs_ptr, uint8_t mns_inst_id); +extern bool Al_Lock(CApiLocking *self, Alm_ModuleMask_t method); +extern void Al_Release(CApiLocking *self, Alm_ModuleMask_t method); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_ALM_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ams.c b/mnsl/mns_ams.c new file mode 100644 index 0000000..338047b --- /dev/null +++ b/mnsl/mns_ams.c @@ -0,0 +1,665 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Service + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSC + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams.h" +#include "mns_amsmessage.h" +#include "mns_dl.h" +#include "mns_misc.h" +#include "mns_pmp.h" +#include "mns_encoder.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Priority of the Application Message Service */ +static const uint8_t AMS_SRV_PRIO = 253U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ +/*! \brief Event which triggers the Rx service */ +static const Srv_Event_t AMS_EV_RX_SERVICE = 1U; +/*! \brief Event which triggers the Tx service */ +static const Srv_Event_t AMS_EV_TX_SERVICE = 2U; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Ams_Cleanup(CAms *self); +static void Ams_Service(void *self); +static void Ams_OnEhEvent(void *self, void *error_code_ptr); + +static void Ams_TxService(CAms *self); +static void Ams_TxOnStatus(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); +static uint8_t Ams_TxGetNextFollowerId(CAms *self); + +static void Ams_RxOnTelComplete(CAms *self, Msg_MostTel_t *tel_ptr); +static void Ams_RxReleaseTel(CAms *self, Msg_MostTel_t *tel_ptr); +static void Ams_RxProcessWaitingQ(CAms *self); +static void Ams_RxOnSegError(void *self, Msg_MostTel_t *tel_ptr, Segm_Error_t error); +static void Ams_RxOnFreedMsg(void *self, void *data_ptr); +static void Ams_RxFlush(CAms *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization Methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CAms + * \param self The instance + * \param base_ptr Reference to base services + * \param mcm_trcv_ptr Reference to the MCM transceiver + * \param rcm_trcv_ptr Reference to the RCM transceiver + * \param pool_ptr Reference to the pool for application message handles + * \param rx_def_payload_sz Default memory size that is allocated when receiving segmented messages + * without size prefix + */ +void Ams_Ctor(CAms *self, CBase *base_ptr, CTransceiver *mcm_trcv_ptr, CTransceiver *rcm_trcv_ptr, + CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz) +{ + MISC_UNUSED(rcm_trcv_ptr); + MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */ + self->trcv_mcm_ptr = mcm_trcv_ptr; + self->trcv_rcm_ptr = rcm_trcv_ptr; + self->base_ptr = base_ptr; + self->pool_ptr = pool_ptr; + + self->tx.default_llrbc = AMS_LLRBC_DEFAULT; /* set initial retries */ + self->tx.next_follower_id = 1U; /* set initial follower id */ + /* init pools */ + Obs_Ctor(&self->rx.message_freed_observer, self, &Ams_RxOnFreedMsg); + Amsp_AssignRxFreedObs(self->pool_ptr, &self->rx.message_freed_observer); + Telq_Ctor(&self->rx.waiting_queue, self->base_ptr->mns_inst_id); /* init Rx waiting queue */ + + Dl_Ctor(&self->tx.queue, self->base_ptr->mns_inst_id); + + Srv_Ctor(&self->service, AMS_SRV_PRIO, self, &Ams_Service); /* register service */ + (void)Scd_AddService(&self->base_ptr->scd, &self->service); + + Segm_Ctor(&self->segmentation, self->base_ptr, self->pool_ptr, rx_def_payload_sz); + Segm_AssignRxErrorHandler(&self->segmentation, &Ams_RxOnSegError, self); + + if (self->trcv_mcm_ptr != NULL) + { + Trcv_RxAssignReceiver(self->trcv_mcm_ptr, &Ams_RxOnMcmTelComplete, self); + } + if (self->trcv_rcm_ptr != NULL) + { + Trcv_RxAssignReceiver(self->trcv_rcm_ptr, &Ams_RxOnRcmTelComplete, self); + } + + Mobs_Ctor(&self->unsync_result_observer, self, EH_M_TERMINATION_EVENTS, &Ams_OnEhEvent); /* register error observer */ + Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->unsync_result_observer); +} + +/*! \brief Sets the default retry values used for Application Messages + * \param self The instance + * \param llrbc The default low level retry block count + */ +void Ams_TxSetDefaultRetries(CAms* self, uint8_t llrbc) +{ + self->tx.default_llrbc = llrbc; +} + +/*! \brief Assigns a function of another class to receive application messages + * \param self The instance + * \param cb_fptr Callback function + * \param inst_ptr The instance of the receiver class + */ +void Ams_RxAssignReceiver(CAms *self, Amsg_RxCompleteCb_t cb_fptr, void *inst_ptr) +{ + self->rx.complete_fptr = cb_fptr; + self->rx.complete_inst_ptr = inst_ptr; +} + +/*! \brief Assigns an observer which is invoked if a Tx application message is freed. + * \details The observer is only notified a previous allocation of a Tx object has failed. + * The data_ptr of the update callback function is not used (always \c NULL). + * See \ref Obs_UpdateCb_t. + * \param self The instance + * \param observer_ptr The observer + */ +void Ams_TxAssignMsgFreedObs(CAms *self, CObserver *observer_ptr) +{ + Amsp_AssignTxFreedObs(self->pool_ptr, observer_ptr); +} + +/*! \brief Assigns a callback function that selects FIFO routing for a Tx message. + * \details If no callback function is assigned, then all Tx messages are routed to RCM FIFO. + * \param self The instance + * \param cb_fptr The callback function + */ +void Ams_TxAssignTrcvSelector(CAms *self, Ams_TxIsRcmMsgCb_t cb_fptr) +{ + self->tx.is_rcm_fptr = cb_fptr; +} + +/*! \brief Performs a cleanup of the Tx message queue and notifies + * the transmission error MNS_AMSTX_RES_NOT_AVAILABLE. + * \param self The instance + */ +static void Ams_Cleanup(CAms *self) +{ + Mns_AmsTx_Msg_t *tx_ptr = NULL; + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Starting AMS Cleanup", 0U)); + /* cleanup Tx queue */ + for (tx_ptr = Amsg_TxDequeue(&self->tx.queue); tx_ptr != NULL; tx_ptr = Amsg_TxDequeue(&self->tx.queue)) + { + /* just just notify completion, the object is automatically freed to the pool */ + Amsg_TxNotifyComplete(tx_ptr, MNS_AMSTX_RES_ERR_NOT_AVAILABLE, MNS_AMSTX_I_ERR_UNSYNCED); + } + + Segm_Cleanup(&self->segmentation); /* cleanup Rx */ + Ams_RxFlush(self); + Amsp_Cleanup(self->pool_ptr); /* final cleanup of pool */ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Finished AMS Cleanup", 0U)); +} + +/*! \brief Callback function which is invoked by the event handler + * on any termination event. + * \param self The instance + * \param error_code_ptr Reference to the error code + */ +static void Ams_OnEhEvent(void *self, void *error_code_ptr) +{ + CAms *self_ = (CAms*)self; + MISC_UNUSED(error_code_ptr); + Ams_Cleanup(self_); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Service */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The AMS service function + * \param self The instance + */ +static void Ams_Service(void *self) +{ + CAms *self_ = (CAms*)self; + Srv_Event_t event_mask; + Srv_GetEvent(&self_->service, &event_mask); + + if (AMS_EV_TX_SERVICE == (event_mask & AMS_EV_TX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, AMS_EV_TX_SERVICE); + Ams_TxService(self_); + } + + if (AMS_EV_RX_SERVICE == (event_mask & AMS_EV_RX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, AMS_EV_RX_SERVICE); + Ams_RxProcessWaitingQ(self_); + } +} + +/*! \brief Allocates and transmits MCMs for the dedicated Application Messages + * \param self The instance + */ +static void Ams_TxService(CAms *self) +{ + CDlNode *node1_ptr; + /* run as long as messages are available in Tx queue */ + for (node1_ptr = Dl_PeekHead(&self->tx.queue); node1_ptr != NULL; node1_ptr = Dl_PeekHead(&self->tx.queue)) + { + Msg_MostTel_t *tel_ptr = NULL; + CTransceiver *trcv_ptr = self->trcv_mcm_ptr; + Mns_AmsTx_Msg_t *tx_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node1_ptr); + + if (self->tx.is_rcm_fptr != NULL) + { + if (self->tx.is_rcm_fptr(tx_ptr) != false) + { + trcv_ptr = self->trcv_rcm_ptr; + } + } + /* allocate telegram object with 2 bytes for TelId 4 */ + tel_ptr = Trcv_TxAllocateMsg(trcv_ptr, 2U); /* remaining message payload is attached as external memory */ + + if (tel_ptr != NULL) /* transmit message if telegram object is available */ + { + bool done; + CDlNode *node2_ptr = Dl_PopHead(&self->tx.queue); /* get application message from queue */ + + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (node1_ptr == node2_ptr)); + tx_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node2_ptr); + done = Segm_TxBuildSegment(&self->segmentation, tx_ptr, tel_ptr); /* run segmentation */ + Trcv_TxSendMsgExt(trcv_ptr, tel_ptr, &Ams_TxOnStatus, self); /* transmit telegram */ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Ams_TxService(tel_ptr=0x%p)", 1U, tel_ptr)); + + if (done == false) + { + Dl_InsertHead(&self->tx.queue, node2_ptr); + } + } + else + { + break; + } + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* AMS Tx handles */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves a message Tx handle + * \details The payload provided is limited the supported size of the memory management. + * The application may also attach own payload to a message object. Therefore, + * Ams_TxGetMsg() shall be called with size "0". + * \param self The instance + * \param size Payload size in bytes or "0" to use application provided payload. + * The payload provided by MNS is limited to a size of 45 bytes. + * Valid values: 0..45. + * \return A Tx message object or \c NULL if no message object is available. + */ +Mns_AmsTx_Msg_t * Ams_TxGetMsg(CAms *self, uint16_t size) +{ + Mns_AmsTx_Msg_t * msg_ptr = Amsp_AllocTxObj(self->pool_ptr, size); + + if (msg_ptr != NULL) + { + msg_ptr->destination_address = AMS_ADDR_RSVD_RANGE; /* set invalid address to prevent internal transmission*/ + msg_ptr->llrbc = self->tx.default_llrbc; + } + + return msg_ptr; +} + +/*! \brief Frees an unused or completed Tx message to the pool + * \param self The instance + * \param msg_ptr Reference to the related message object + */ +void Ams_TxFreeUnusedMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr) +{ + MISC_UNUSED(self); + Amsg_TxFreeUnused(msg_ptr); /* the object is automatically freed to the pool */ +} + +/*------------------------------------------------------------------------------------------------*/ +/* AMS Transmission */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Transmits a MOST Application Message + * \details After the transmission completed the function will call one callback function. Therefore + * the caller is able to assign one of two different callback function. The difference between + * the callback function is that tx_complete_sia_fptr does no provide a self pointer whether + * tx_complete_fptr and tx_complete_inst_ptr allow to invoke a class method. + * \param self The instance + * \param msg_ptr Reference to the related message object + * \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 Possible return values are + * - \c MNS_RET_SUCCESS if the transmission was started successfully + * - \c MNS_RET_ERR_PARAM if the transmission was refused due to an invalid parameter + */ +Mns_Return_t Ams_TxSendMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr, + Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr) +{ + Mns_Return_t ret_val = MNS_RET_ERR_PARAM; + + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Called Ams_TxSendMsg(0x%p)", 1U, msg_ptr)); + + if (Ams_TxIsValidMessage(msg_ptr)) /* prevent application messages to loc. INIC */ + { /* do not set both callback pointers */ + TR_ASSERT(self->base_ptr->mns_inst_id, "[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); + Ams_TxSendMsgDirect(self, msg_ptr); + ret_val = MNS_RET_SUCCESS; + } + + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (ret_val == MNS_RET_SUCCESS)); + + return ret_val; +} + +/*! \brief Transmits a MOST Application Message without attributes check + * \details This method shall be only be used by AMD and AMS internally + * \param self The instance + * \param msg_ptr Reference to the related message object + */ +void Ams_TxSendMsgDirect(CAms *self, Mns_AmsTx_Msg_t *msg_ptr) +{ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Called Ams_TxSendMsg(0x%p)", 1U, msg_ptr)); + + if (msg_ptr->data_size > SEGM_MAX_SIZE_TEL) /* set follower id to be used for all segments */ + { + Amsg_TxSetFollowerId(msg_ptr, Ams_TxGetNextFollowerId(self)); + } + + Amsg_TxEnqueue(msg_ptr, &self->tx.queue); /* schedule transmission */ + Srv_SetEvent(&self->service, AMS_EV_TX_SERVICE); +} + +/*! \brief Callback function which is invoked as soon as MCM transmission + * was finished in PMS. + * \param self The instance + * \param tel_ptr Reference to the telegram + * \param status Transmission status + */ +static void Ams_TxOnStatus(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status) +{ + CAms *self_ = (CAms*)self; + Mns_AmsTx_Msg_t* msg_ptr = (Mns_AmsTx_Msg_t*)tel_ptr->info_ptr; + + TR_INFO((self_->base_ptr->mns_inst_id, "[AMS]", "Ams_TxOnStatus(tel_ptr=0x%p, %d)", 2U, tel_ptr, status)); + + if (msg_ptr != NULL) /* MOST Telegram has AMS parent? */ + { + Amsg_TxUpdateResult(msg_ptr, status); + + if ((0U == tel_ptr->tel.tel_id) || (3U == tel_ptr->tel.tel_id)) /* is finished? */ + { /* just just notify completion, the object is */ + Amsg_TxNotifyComplete(msg_ptr, Amsg_TxGetResultCode(msg_ptr), Amsg_TxGetResultInfo(msg_ptr)); /* automatically freed to the pool */ + } + else if (status != MNS_MSG_STAT_OK) /* check transmission needs termination before transmission end */ + { + TR_ASSERT(self_->base_ptr->mns_inst_id, "[AMS]", (Amsg_TxGetFollowerId(msg_ptr) != 0U)); + + if (((uint8_t)Amsg_TxGetNextSegmCnt(msg_ptr) == (uint8_t)(tel_ptr->tel.tel_cnt + 1U)) /* is last transmitted segment */ + || ((Amsg_TxGetNextSegmCnt(msg_ptr) == 0U) && (4U == tel_ptr->tel.tel_id))) /* or TelId 4 and the first segment is pending */ + { + Amsg_TxRemoveFromQueue(msg_ptr, &self_->tx.queue); + Amsg_TxNotifyComplete(msg_ptr, Amsg_TxGetResultCode(msg_ptr), Amsg_TxGetResultInfo(msg_ptr)); /* just just notify completion, the object is */ + } /* automatically freed to the pool */ + } + } + Trcv_TxReleaseMsg(tel_ptr); /* release message object to pool */ + + if ((Dl_GetSize(&self_->tx.queue) > 0U) && (status != MNS_MSG_STAT_ERROR_SYNC)) /* Application Messages are available for Tx */ + { + Srv_SetEvent(&self_->service, AMS_EV_TX_SERVICE); + } +} + +/*! \brief Checks if the destination address of the Tx message is valid and payload is consistent + * \param msg_ptr Reference to the Tx message object + * \return Returns \c true if the destination is correct, otherwise \c false. + */ +bool Ams_TxIsValidMessage(Mns_AmsTx_Msg_t *msg_ptr) +{ + bool ret = false; + + if (msg_ptr != NULL) + { + if ((msg_ptr->destination_address == MNS_ADDR_INTERNAL) + ||(msg_ptr->destination_address > AMS_ADDR_RSVD_RANGE)) /* is not reserved address? */ + { + if (((msg_ptr->destination_address & 0xFF00U) != 0x0300U)/* is single-cast? */ + || (msg_ptr->data_size <= SEGM_MAX_SIZE_TEL)) /* or not segmented */ + { + if (!((msg_ptr->data_size > 0U) && (msg_ptr->data_ptr == NULL))) + { + ret = true; + } + } + } + } + + return ret; +} + +/*! \brief Retrieves the next follower id to use for segmented transfer + * \param self The instance + * \return The follower id + */ +static uint8_t Ams_TxGetNextFollowerId(CAms *self) +{ + uint8_t ret; + ret = self->tx.next_follower_id; + self->tx.next_follower_id++; + + if (self->tx.next_follower_id == 0U) /* skip zero since it means */ + { /* "no follower" */ + self->tx.next_follower_id = 1U; + } + + return ret; +} + +/*! \brief Retrieves the number of messages that are queued for transmission + * \param self The instance + * \return The number of messages in the Tx queue + */ +uint16_t Ams_TxGetMsgCnt(CAms *self) +{ + return Dl_GetSize(&self->tx.queue); +} + +/*------------------------------------------------------------------------------------------------*/ +/* AMS Reception */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Rx callback function that can be assigned to the MCM transceiver + * \details The associated transceiver reference will be stored in the telegrams \c info_ptr. + * Later on the telegram must be released via Ams_RxReleaseTel(). + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +void Ams_RxOnMcmTelComplete(void *self, Msg_MostTel_t *tel_ptr) +{ + CAms *self_ = (CAms*)self; + TR_ASSERT(self_->base_ptr->mns_inst_id, "[AMS]", (tel_ptr->info_ptr == NULL)); + tel_ptr->info_ptr = self_->trcv_mcm_ptr; + Ams_RxOnTelComplete(self_, tel_ptr); +} + +/*! \brief Rx callback function that can be assigned to the RCM transceiver + * \details The associated transceiver reference will be stored in the telegrams \c info_ptr. + * Later on the telegram must be released via Ams_RxReleaseTel(). + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +void Ams_RxOnRcmTelComplete(void *self, Msg_MostTel_t *tel_ptr) +{ + CAms *self_ = (CAms*)self; + TR_ASSERT(self_->base_ptr->mns_inst_id, "[AMS]", (tel_ptr->info_ptr == NULL)); + tel_ptr->info_ptr = self_->trcv_rcm_ptr; + Ams_RxOnTelComplete(self_, tel_ptr); +} + +/*! \brief Releases an Rx telegram to the associated transceiver + * \details The associated transceiver reference is stored in the telegrams \c info_ptr + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +static void Ams_RxReleaseTel(CAms *self, Msg_MostTel_t *tel_ptr) +{ + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", ((tel_ptr != NULL) && (tel_ptr->info_ptr != NULL))); + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", ((tel_ptr->info_ptr == self->trcv_mcm_ptr)||(tel_ptr->info_ptr == self->trcv_rcm_ptr))); + + if (tel_ptr->info_ptr != NULL) + { + Trcv_RxReleaseMsg((CTransceiver*)tel_ptr->info_ptr, tel_ptr); + } + + MISC_UNUSED(self); +} + +/*! \brief Internal callback function which is invoked as soon as the transceiver + * reference is stored to the telegrams info_ptr. + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +static void Ams_RxOnTelComplete(CAms *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Ams_RxOnComplete(0x%p)", 1U, tel_ptr)); + + if (self->rx.complete_fptr == NULL) + { + /* no processing required, tel_ptr shall be freed */ + msg_ptr = NULL; + } + else if (Telq_GetSize(&self->rx.waiting_queue) > 0U) /* asynchronous Rx is running */ + { /* queue Rx telegram for later processing */ + Telq_Enqueue(&self->rx.waiting_queue, tel_ptr); + tel_ptr = NULL; /* do not free Rx telegram */ + msg_ptr = NULL; + } + else + { + Segm_Result_t result; /* synchronous processing is possible now */ + msg_ptr = Segm_RxExecuteSegmentation(&self->segmentation, tel_ptr, &result); + + if (result == SEGM_RES_RETRY) + { + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (msg_ptr == NULL)); + Telq_Enqueue(&self->rx.waiting_queue, tel_ptr); + tel_ptr = NULL; /* do not free Rx telegram */ + } + } + + if (msg_ptr != NULL) + { + self->rx.complete_fptr(self->rx.complete_inst_ptr, (Mns_AmsRx_Msg_t*)(void*)msg_ptr); + } + + if (tel_ptr != NULL) + { + Ams_RxReleaseTel(self, tel_ptr); /* free Rx telegram */ + } +} + +/*! \brief Processes all telegrams in waiting queue + * \details Stops if allocation of Rx messages fails + * \param self The instance + */ +static void Ams_RxProcessWaitingQ(CAms *self) +{ + Msg_MostTel_t *tel_ptr; + Mns_AmsRx_Msg_t *msg_ptr = NULL; + + for (tel_ptr = Telq_Peek(&self->rx.waiting_queue); tel_ptr != NULL; tel_ptr = Telq_Peek(&self->rx.waiting_queue)) + { + Segm_Result_t result; + msg_ptr = Segm_RxExecuteSegmentation(&self->segmentation, tel_ptr, &result); + + if (result == SEGM_RES_OK) /* segmentation process succeeded */ + { + (void)Telq_Dequeue(&self->rx.waiting_queue); /* remove telegram from waitingQ */ + Ams_RxReleaseTel(self, tel_ptr); /* free telegram */ + tel_ptr = NULL; /* parasoft-suppress MISRA2004-13_6 "variable is not used as a counter" */ + + if (msg_ptr != NULL) + { + self->rx.complete_fptr(self->rx.complete_inst_ptr, (Mns_AmsRx_Msg_t*)(void*)msg_ptr); + } + } + else + { + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (msg_ptr == NULL)); + break; /* wait until further Rx messages can be allocated - abort loop */ + } + } +} + +/*! \brief Callback function which is invoked by segmentation process to notify a segmentation error + * \param self The instance + * \param tel_ptr The related Rx telegram which caused the segmentation error + * \param error The segmentation error number + */ +static void Ams_RxOnSegError(void *self, Msg_MostTel_t *tel_ptr, Segm_Error_t error) +{ + const uint8_t ERR_SZ = 2U; + CAms *self_ = (CAms*)self; + Msg_MostTel_t* error_tel_ptr = NULL; + + TR_ERROR((self_->base_ptr->mns_inst_id, "[AMS]", "Ams_RxOnComplete(0x%p, %d)", 2U, tel_ptr, error)); + + if (tel_ptr->source_addr != MSG_ADDR_INIC) + { /* only generate segmentation errors */ + error_tel_ptr = Trcv_TxAllocateMsg(self_->trcv_mcm_ptr, ERR_SZ); /* for messages which are NOT locally routed by the INIC */ + } + + if (error_tel_ptr != NULL) + { + error_tel_ptr->destination_addr = tel_ptr->source_addr; + error_tel_ptr->id = tel_ptr->id; + error_tel_ptr->id.op_type = MNS_OP_ERROR; + error_tel_ptr->tel.tel_data_ptr[0] = 0x0CU; + error_tel_ptr->tel.tel_data_ptr[1] = (uint8_t)error; + error_tel_ptr->opts.llrbc = 0U; + + Trcv_TxSendMsg(self_->trcv_mcm_ptr, error_tel_ptr); /* just fire the message */ + } +} + +/*! \brief Callback function that is invoked if application Rx messages are available again + * \param self The instance + * \param data_ptr Unused parameter of observer callback + */ +static void Ams_RxOnFreedMsg(void *self, void *data_ptr) +{ + CAms *self_ = (CAms*) self; + Srv_SetEvent(&self_->service, AMS_EV_RX_SERVICE); + MISC_UNUSED(data_ptr); +} + +/*! \brief Removes and frees a message from the Rx queue + * \details The application must not access the passed + * message any more. + * \param self The instance + * \param msg_ptr Reference to the message, or \c NULL for the front-most + * message in the Rx queue. + */ +void Ams_RxFreeMsg(CAms *self, Mns_AmsRx_Msg_t *msg_ptr) +{ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Ams_RxFreeMsg(msg_ptr=0x%p)", 1U, msg_ptr)); + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (msg_ptr != NULL)); + + if (msg_ptr != NULL) + { + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); /* free external payload */ + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); /* return message to Rx pool */ + } +} + +/*! \brief Removes all messages located in Rx queues + * \param self The instance + */ +static void Ams_RxFlush(CAms *self) +{ + Msg_MostTel_t *tel_ptr; + + for (tel_ptr = Telq_Dequeue(&self->rx.waiting_queue); tel_ptr != NULL; tel_ptr = Telq_Dequeue(&self->rx.waiting_queue)) + { + Ams_RxReleaseTel(self, tel_ptr); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ams.h b/mnsl/mns_ams.h new file mode 100644 index 0000000..fb291c1 --- /dev/null +++ b/mnsl/mns_ams.h @@ -0,0 +1,163 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of Application Message Service + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSC + * @{ + */ + +#ifndef MNS_AMS_H +#define MNS_AMS_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams_pb.h" +#include "mns_amsmessage.h" +#include "mns_amspool.h" +#include "mns_pool.h" +#include "mns_transceiver.h" +#include "mns_pmchannel.h" +#include "mns_ret.h" +#include "mns_segmentation.h" +#include "mns_message.h" +#include "mns_telqueue.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines the default LLR number for Application Messages */ +#define AMS_LLRBC_DEFAULT (MSG_LLRBC_DEFAULT) +/*! \brief Defines the maximum LLR number for Application Messages */ +#define AMS_LLRBC_MAX (MSG_LLRBC_MAX) +/*! \brief Default memory size that is allocated when receiving segmented messages + * without size prefix */ +#define AMS_RX_DEF_SIZE_PAYLOAD 400U +/*! \brief Maximum destination address which is reserved for internal transmission */ +#define AMS_ADDR_RSVD_RANGE 0x000FU + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function type to request FIFO routing for a Tx message. + * \param msg_ptr Reference to a Tx message object + * \return Returns \c true if a Tx message shall be routed to RCM FIFO, otherwise returns \c false. + */ +typedef bool (*Ams_TxIsRcmMsgCb_t)(Mns_AmsTx_Msg_t *msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Class */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Application Message Service Class + * \details Allows transmission and reception of MOST Application Messages + */ +typedef struct CAms_ +{ + CService service; /*!< \brief Service object */ + CSegmentation segmentation; /*!< \brief Segmentation object */ + CMaskedObserver unsync_result_observer; /*!< \brief Observes un-sync result */ + + CBase *base_ptr; /*!< \brief Reference to basic services */ + CAmsMsgPool *pool_ptr; /*!< \brief Pool providing Rx and Tx objects/payload */ + CTransceiver *trcv_mcm_ptr; /*!< \brief Reference to MCM transceiver */ + CTransceiver *trcv_rcm_ptr; /*!< \brief Reference to RCM transceiver */ + + struct Ams_tx_ + { + CDlList queue; /*!< \brief Input queue of Tx Application Messages */ + uint8_t default_llrbc; /*!< \brief Default LowLevelRetryBlockCount. Valid values: 0..100 */ + Ams_TxIsRcmMsgCb_t is_rcm_fptr; /*!< \brief Assignable callback function to request the correct transceiver */ + uint8_t next_follower_id; /*!< \brief The follower id for the next segmented + * message + */ + } tx; + + struct Ams_rx_ + { + CObserver message_freed_observer; /*!< \brief Observes message freed event */ + + Amsg_RxCompleteCb_t complete_fptr; /*!< \brief Callback function which is invoked on + * message reception + */ + void *complete_inst_ptr; /*!< \brief Instance which is notified on + * message reception + */ + CTelQueue waiting_queue; /*!< \brief Queue of unprocessed single telegrams */ + + } rx; + +} CAms; + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ams_Ctor(CAms *self, CBase *base_ptr, CTransceiver *mcm_trcv_ptr, CTransceiver *rcm_trcv_ptr, + CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz); + +/*------------------------------------------------------------------------------------------------*/ +/* Public methods / Tx */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ams_TxSetDefaultRetries(CAms *self, uint8_t llrbc); +extern void Ams_TxAssignMsgFreedObs(CAms *self, CObserver *observer_ptr); +extern void Ams_TxAssignTrcvSelector(CAms *self, Ams_TxIsRcmMsgCb_t cb_fptr); +extern Mns_AmsTx_Msg_t* Ams_TxGetMsg(CAms *self, uint16_t size); +extern void Ams_TxFreeUnusedMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr); +extern uint16_t Ams_TxGetMsgCnt(CAms *self); +extern bool Ams_TxIsValidMessage(Mns_AmsTx_Msg_t *msg_ptr); +extern void Ams_TxSendMsgDirect(CAms *self, Mns_AmsTx_Msg_t *msg_ptr); +extern Mns_Return_t Ams_TxSendMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr, + Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Public methods / Rx */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ams_RxAssignReceiver(CAms *self, Amsg_RxCompleteCb_t cb_fptr, void *inst_ptr); +extern void Ams_RxFreeMsg(CAms *self, Mns_AmsRx_Msg_t *msg_ptr); +extern void Ams_RxOnMcmTelComplete(void *self, Msg_MostTel_t *tel_ptr); +extern void Ams_RxOnRcmTelComplete(void *self, Msg_MostTel_t *tel_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_AMS_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ams_pb.h b/mnsl/mns_ams_pb.h new file mode 100644 index 0000000..a559c94 --- /dev/null +++ b/mnsl/mns_ams_pb.h @@ -0,0 +1,262 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Public header file of Application Message Service + */ +/*! + * \addtogroup G_MNS_AMS_TYPES + * @{ + */ + +#ifndef MNS_AMS_PB_H +#define MNS_AMS_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_message_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! \brief Defines which address type was used by the transmitter of a message. */ +typedef enum Mns_AmsRx_ReceiveType_ +{ + MNS_AMSRX_RCT_SINGLECAST = 0U, /*!< \brief Message was transmitted as singlecast */ + MNS_AMSRX_RCT_GROUPCAST = 1U, /*!< \brief Message was transmitted as groupcast */ + MNS_AMSRX_RCT_BROADCAST = 2U /*!< \brief Message was transmitted as broadcast */ + +} Mns_AmsRx_ReceiveType_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Application message Tx type */ +typedef struct Mns_AmsTx_Msg_ +{ + uint16_t destination_address; /*!< \brief Destination address. Find some predefined addresses \ref G_MNS_AMS "here". */ + uint8_t fblock_id; /*!< \brief Function block ID (MOST FBlockID). */ + uint8_t instance_id; /*!< \brief Instance ID (MOST InstID). */ + uint16_t function_id; /*!< \brief Function ID (MOST FktID). */ + Mns_OpType_t op_type; /*!< \brief Operation type (MOST OpType). */ + uint8_t llrbc; /*!< \brief Specifies the "Low-Level Retry Block Count" (LLRBC) + * \details Valid values: 0..100. Default value: configurable via \ref Mns_AmsTx_InitData_t "default_llrbc" + * of the initialization structure \ref Mns_AmsTx_InitData_t. + * \mns_ic_inic{ See also <i>INIC API User's Guide</i>, section \ref SEC_OS81118_19. } + */ + uint8_t *data_ptr; /*!< \brief Payload data */ + uint16_t data_size; /*!< \brief The size of payload data in bytes */ + void *custom_info_ptr; /*!< \brief Customer specific reference + * \details The application is allowed to use this attribute to assign an + * own reference to the message object. The reference is initialized + * by MOST NetServices with \c NULL and will not alter until the + * transmission has finished. + */ +} Mns_AmsTx_Msg_t; + +/*! \brief Application message Rx type */ +typedef struct Mns_AmsRx_Msg_ +{ + uint16_t source_address; /*!< \brief Source address */ + uint8_t fblock_id; /*!< \brief Function block ID (MOST FBlockID). */ + uint8_t instance_id; /*!< \brief Instance ID (MOST InstID). */ + uint16_t function_id; /*!< \brief Function ID (MOST FktID). */ + Mns_OpType_t op_type; /*!< \brief Operation type (MOST OpType). */ + uint8_t *data_ptr; /*!< \brief Reference to payload */ + uint16_t data_size; /*!< \brief Payload size in bytes */ + void *custom_info_ptr; /*!< \brief Customer specific reference */ + Mns_AmsRx_ReceiveType_t receive_type; /*!< \brief Defines which address type was used by the transmitter of this message */ + +} Mns_AmsRx_Msg_t; + +/*! \brief Transmission result of an application message */ +typedef enum Mns_AmsTx_Result_ +{ + MNS_AMSTX_RES_SUCCESS = 0x00U,/*!< \brief The transmission succeeded. */ + + MNS_AMSTX_RES_ERR_RETRIES_EXP = 0x01U,/*!< \brief The transmission including all retries have failed. + * \details The following issues may have caused the failure: + * - message corruption + * - transmission timeouts + * - full receive buffers of the destination device + * - full receive buffers of the local device if the + * destination was the own address, own group or broadcast + * address + * . + */ + MNS_AMSTX_RES_ERR_INVALID_TGT = 0x02U,/*!< \brief The transmission failed because the specified destination + * address is not found or not valid. + * \details The following issues may have caused the failure: + * - device with the given destination address is not found + * - destination address is reserved (for future use) + * - destination address is 0xFFFF (un-initialized logical + * node address is not supported) + * . + */ + MNS_AMSTX_RES_ERR_NOT_AVAILABLE = 0x03U,/*!< \brief The transmission failed since the network or the INIC + * is not available. + */ + MNS_AMSTX_RES_ERR_BUF_INTERNAL = 0xFEU,/*!< \brief The transmission failed because the allocation of an Rx message object failed. + * The Rx message object is required to receive the message via the own Rx message queue. + * \details This is possible in the following cases: + * - A message is transmitted to the own node address and the allocation + * of an Rx message object failed. + * - The network transmission to the own group address or broadcast address + * succeeded but the allocation of an Rx message object failed. The application + * has to decide whether to retransmit the message to the own address again. + */ + MNS_AMSTX_RES_ERR_UNEXPECTED = 0xFFU /*!< \brief The transmission failed due to an unexpected error. + * The cause of this failure may be an invalid INIC configuration, + * or an INIC to MOST NetServices incompatibility issue. + */ +} Mns_AmsTx_Result_t; + + +/*! \brief Detailed INIC transmission information which might be useful for debugging purposes. */ +typedef enum Mns_AmsTx_Info_ +{ + MNS_AMSTX_I_SUCCESS = 0x00U, /*!< \brief The transmission succeeded. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_SUCCESS. + */ + MNS_AMSTX_I_ERR_CFG_NORECEIVER = 0x01U, /*!< \brief The transmission failed because the MOST network is not accessible for + * MCM in the current attach state or for ICM in general. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_UNEXPECTED. + */ + MNS_AMSTX_I_ERR_BF = 0x08U, /*!< \brief The transmission failed because the receivers buffer is full. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_CRC = 0x09U, /*!< \brief The transmission failed because of a failed CRC. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_ID = 0x0AU, /*!< \brief The transmission failed because of corrupted identifiers. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_ACK = 0x0BU, /*!< \brief The transmission failed because of corrupted PACK or CACK. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_TIMEOUT = 0x0CU, /*!< \brief The transmission failed because of a transmission timeout. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_FATAL_WT = 0x10U, /*!< \brief The transmission failed because of destination is not available. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_INVALID_TGT. + */ + MNS_AMSTX_I_ERR_FATAL_OA = 0x11U, /*!< \brief The transmission failed because of the destination is the own node address. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_INVALID_TGT. + */ + MNS_AMSTX_I_ERR_UNAVAIL_TRANS = 0x18U, /*!< \brief The transmission canceled during the transition from network interface state + * "available" to "not available". + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_NOT_AVAILABLE. + */ + MNS_AMSTX_I_ERR_UNAVAIL_OFF = 0x19U, /*!< \brief The transmission failed because the network interface state is "not available". + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_NOT_AVAILABLE. + */ + MNS_AMSTX_I_ERR_UNKNOWN = 0xFEU, /*!< \brief The transmission failed because of an unknown INIC error code. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_UNEXPECTED. + * Please check if the MNS version is compatible with the applied INIC firmware version. + */ + MNS_AMSTX_I_ERR_UNSYNCED = 0xFFU /*!< \brief The transmission failed because the communication between the EHC (MOST NetServices) + * and the INIC is lost. + * \details The reason can be a communication error between the EHC and the INIC or that + * the application has called Mns_Stop().\n + * The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_NOT_AVAILABLE. + */ +} Mns_AmsTx_Info_t; + + +/*! \brief Defines the usage of a requested memory chunk */ +typedef enum Mns_Ams_MemUsage_ +{ + MNS_AMS_RX_OBJECT, /*!< \brief Memory is required to allocate an Rx message object */ + MNS_AMS_RX_PAYLOAD, /*!< \brief Memory is required to allocate Rx message payload */ + MNS_AMS_TX_OBJECT, /*!< \brief Memory is required to allocate a Tx message object */ + MNS_AMS_TX_PAYLOAD /*!< \brief Memory is required to allocate Tx message payload */ + +} Mns_Ams_MemUsage_t; + +/*! \brief Callback function type that is invoked to allocate external payload for a segmented Rx message + * \param inst_ptr Reference to the (external) memory management + * \param mem_size Reference to the required memory size in bytes. Valid values: 0..65535. + * \param type Declares how the memory is used by the MOST NetServices + * \param custom_info_pptr Reference which is related to the memory chunk and can be set by + * the application. + * \return Pointer to the provided memory chunk. The application has to guarantee that the memory size + * is equal or greater than \c mem_size. The application has to return \c NULL if it is not able + * to allocate the required memory at this moment. + */ +typedef void* (*Mns_Ams_AllocMemCb_t)(void *inst_ptr, uint16_t mem_size, Mns_Ams_MemUsage_t type, void** custom_info_pptr); + +/*! \brief Callback function type that is invoked to free external payload for a segmented Rx message + * \param inst_ptr Reference to the (external) memory management + * \param mem_ptr Reference to the external payload memory + * \param type Declares how the memory is used by the MOST NetServices + * \param custom_info_ptr Reference to memory related information which was set by the application + * during memory allocation + */ +typedef void (*Mns_Ams_FreeMemCb_t)(void *inst_ptr, void *mem_ptr, Mns_Ams_MemUsage_t type, void* custom_info_ptr); + +/*! \brief Type of a callback function that is invoked by the MOST NetServices as soon as a + * message transmission was finished + * \details The callback function notifies the result of a completed transmission. If + * the message has external payload, the application must decide whether + * to re-use or to free the external payload. + * \param msg_ptr Reference to the related Tx message object. When the callback function returns + * the reference is no longer valid. + * \param result The transmission result. + * \param info Detailed INIC transmission result, which might be helpful for debug purposes. + */ +typedef void (*Mns_AmsTx_CompleteCb_t)(Mns_AmsTx_Msg_t* msg_ptr, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); + +/*! + * @} + * \addtogroup G_MNS_AMS + * @{ + */ + +/*! \brief Type of a callback function that is invoked by MOST NetServices to notify that + * a Tx application message object is available again while a previous + * allocation using Mns_AmsTx_AllocMsg() has failed. + */ +typedef void (*Mns_AmsTx_MsgFreedCb_t)(void); + +/*! \brief Callback function type that is invoked if the MOST NetServices has received a message + * completely and appended to the Rx message queue. + */ +typedef void (*Mns_AmsRx_MsgReceivedCb_t)(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ifndef MNS_AMS_PB_H */ + +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amsmessage.c b/mnsl/mns_amsmessage.c new file mode 100644 index 0000000..11ba985 --- /dev/null +++ b/mnsl/mns_amsmessage.c @@ -0,0 +1,656 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Classes + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSMSG + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_amsmessage.h" +#include "mns_dl.h" +#include "mns_misc.h" +#include "mns_trace.h" + +#define SELF_RX ((Amsg_IntMsgRx_t*)(void*)(self)) +#define SELF_TX ((Amsg_IntMsgTx_t*)(void*)(self)) + +#define AMSG_TX_BACKUP_ADDR_NONE 0U + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static Mns_AmsRx_ReceiveType_t Amsg_RxGetReceiveType(uint16_t destination_address); +static void Amsg_TxRestoreDestinationAddr(Mns_AmsTx_Msg_t *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Tx Message */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes aggregated objects + * \details Needs to be called once before first usage. Call Amsg_TxHandleSetup() before + * repeated usage. + * \param self Reference to an internal Application Message Tx handle + * \param info_ptr Memory information required to free the object + * \param free_fptr Callback function which is invoked when the object is freed + * \param free_inst_ptr The instance which is passed to free_fptr + */ +void Amsg_TxCtor(Mns_AmsTx_Msg_t *self, void *info_ptr, Amsg_TxFreedCb_t free_fptr, void *free_inst_ptr) +{ + /* cleanup complete object */ + MISC_MEM_SET((void*)self, 0, sizeof(Amsg_IntMsgTx_t)); + + /* reset default references + SELF_TX->memory_ptr = NULL; + SELF_TX->memory_sz = NULL; + SELF_TX->memory_info_ptr = NULL; + + SELF_TX->complete_fptr = NULL; + SELF_TX->complete_inst_ptr = NULL; + SELF_TX->complete_sia_fptr = NULL; + + SELF_TX->backup_dest_address = AMSG_TX_BACKUP_ADDR_NONE;*/ + + SELF_TX->info_ptr = info_ptr; + SELF_TX->free_fptr = free_fptr; + SELF_TX->free_inst_ptr = free_inst_ptr; + SELF_TX->next_segment_cnt = 0xFFFFU; /* start with TelId "4" */ + SELF_TX->temp_result = MNS_MSG_STAT_OK; + SELF_TX->internal_status = AMSG_TX_INTRES_NONE; + SELF_TX->ignore_wrong_target = false; + + Dln_Ctor(&SELF_TX->node, self); /* initialize node */ +} + +/*! \brief Sets payload memory provided by memory management and updates data pointer and size + * \param self The instance + * \param mem_ptr Reference to the provided memory chunk + * \param mem_size Size of the provided memory chunk + * \param mem_info_ptr Optional reference for memory management + */ +void Amsg_TxSetInternalPayload(Mns_AmsTx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *mem_info_ptr) +{ + SELF_TX->memory_ptr = mem_ptr; + SELF_TX->memory_sz = mem_size; + SELF_TX->memory_info_ptr = mem_info_ptr; + + SELF_TX->pb_msg.data_ptr = mem_ptr; + SELF_TX->pb_msg.data_size = mem_size; +} + +/*! \brief Prepares the message object for re-usage + * \details The public message structure is re-initialized. The internal payload + * is assigned to the public data reference. + * \param self The instance + */ +void Amsg_TxReuse(Mns_AmsTx_Msg_t *self) +{ + MISC_MEM_SET((void *)&SELF_TX->pb_msg, 0, sizeof(SELF_TX->pb_msg)); /* cleanup public object */ + /* SELF_TX->backup_dest_address = AMSG_TX_BACKUP_ADDR_NONE; */ + + SELF_TX->pb_msg.data_ptr = SELF_TX->memory_ptr; /* reset public payload */ + SELF_TX->pb_msg.data_size = SELF_TX->memory_sz; + + SELF_TX->next_segment_cnt = 0xFFFFU; /* start with TelId "4" */ + SELF_TX->temp_result = MNS_MSG_STAT_OK; + SELF_TX->internal_status = AMSG_TX_INTRES_NONE; +} + +/*! \brief Assigns a Tx complete callback function + * \details It is not possible to assign the single and multiple instance callback + * at the same time. This function shall be called before message transmission. + * \param self The instance + * \param compl_sia_fptr Reference to the single instance callback function + * \param compl_fptr Reference to a multiple instance callback function + * \param compl_inst_ptr Instance which is invoked by compl_fptr() + */ +void Amsg_TxSetCompleteCallback(Mns_AmsTx_Msg_t *self, Amsg_TxCompleteSiaCb_t compl_sia_fptr, + Amsg_TxCompleteCb_t compl_fptr, void* compl_inst_ptr) +{ + SELF_TX->complete_sia_fptr = compl_sia_fptr; + SELF_TX->complete_fptr = compl_fptr; + SELF_TX->complete_inst_ptr = compl_inst_ptr; +} + +/*! \brief Invokes the correct callback function to notify the transmission result + * and frees the memory + * \param self Reference to the related message object + * \param result The transmission result + * \param info The INIC transmission result + */ +void Amsg_TxNotifyComplete(Mns_AmsTx_Msg_t *self, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info) +{ + Amsg_TxRestoreDestinationAddr(self); + + if (SELF_TX->complete_sia_fptr != NULL) /* invoke single instance API callback */ + { + SELF_TX->complete_sia_fptr(self, result, info); + } + else if (SELF_TX->complete_fptr != NULL) + { + SELF_TX->complete_fptr(SELF_TX->complete_inst_ptr, self, result, info); + } + + TR_ASSERT(0xFU, "[AMSG_TX]", (SELF_TX->free_fptr != NULL)); + if (SELF_TX->free_fptr != NULL) + { + SELF_TX->free_fptr(SELF_TX->free_inst_ptr, self); + } +} + +/*! \brief Frees an unused message object to the owning pool + * \param self Reference to the message object + */ +void Amsg_TxFreeUnused(Mns_AmsTx_Msg_t *self) +{ + TR_ASSERT(0xFU, "[AMSG_TX]", (SELF_TX->free_fptr != NULL)); + if (SELF_TX->free_fptr != NULL) + { + SELF_TX->free_fptr(SELF_TX->free_inst_ptr, self); + } +} + +/*! \brief Updates the transmission result + * \param self Reference to the related message object + * \param result The latest MCM transmission result + * \details Since the transmission result of an application message may + * consist of multiple telegram transmission results, it is + * important to store the final transmission error. An error cannot + * be overwritten by a success. + */ +void Amsg_TxUpdateResult(Mns_AmsTx_Msg_t *self, Mns_MsgTxStatus_t result) +{ + if (result != MNS_MSG_STAT_OK) /* store the latest error and do not overwrite with success */ + { + SELF_TX->temp_result = result; + } +} + +/*! \brief Updates the internal transmission result + * \param self Reference to the related message object + * \param result The internal transmission result + * \details The internal transmission result must be updated if the + * the message is transmitted to the internal Rx queue. + */ +void Amsg_TxUpdateInternalResult(Mns_AmsTx_Msg_t *self, Amsg_TxIntStatus_t result) +{ + SELF_TX->internal_status = result; +} + +/*! \brief Returns the latest AMS transmission result code + * \param self Reference to the related message object + * \return Returns the transmission result which shall be notified to the application + */ +Mns_AmsTx_Result_t Amsg_TxGetResultCode(Mns_AmsTx_Msg_t *self) +{ + Mns_AmsTx_Result_t res = MNS_AMSTX_RES_SUCCESS; /* success is the expected result */ + + switch (SELF_TX->temp_result) + { + case MNS_MSG_STAT_OK: + if (SELF_TX->internal_status == AMSG_TX_INTRES_ERRBUF) + { + res = MNS_AMSTX_RES_ERR_BUF_INTERNAL; /* internal transmission error overrules network success */ + } + break; + case MNS_MSG_STAT_ERROR_BF: + case MNS_MSG_STAT_ERROR_CRC: + case MNS_MSG_STAT_ERROR_ID: + case MNS_MSG_STAT_ERROR_ACK: + case MNS_MSG_STAT_ERROR_TIMEOUT: + res = MNS_AMSTX_RES_ERR_RETRIES_EXP; /* transmission failed, retries are possible */ + break; + case MNS_MSG_STAT_ERROR_FATAL_WT: + case MNS_MSG_STAT_ERROR_FATAL_OA: + if (SELF_TX->internal_status == AMSG_TX_INTRES_ERRBUF) + { + res = MNS_AMSTX_RES_ERR_BUF_INTERNAL; /* internal transmission error and network node not found */ + } + else if (SELF_TX->internal_status == AMSG_TX_INTRES_NONE) + { + res = MNS_AMSTX_RES_ERR_INVALID_TGT; /* not transmitted internally and no network node found */ + } + /* else -> internal success -> target node was found locally */ + break; + case MNS_MSG_STAT_ERROR_NA_TRANS: + case MNS_MSG_STAT_ERROR_NA_OFF: + if (SELF_TX->internal_status != AMSG_TX_INTRES_SUCCESS) + { + res = MNS_AMSTX_RES_ERR_NOT_AVAILABLE; /* successful if internal transmission succeeded, otherwise "not available" */ + } + break; + case MNS_MSG_STAT_ERROR_SYNC: + res = MNS_AMSTX_RES_ERR_NOT_AVAILABLE; + break; + default: + res = MNS_AMSTX_RES_ERR_UNEXPECTED; /* unexpected network transmission state */ + break; + } + + return res; +} + +/*! \brief Returns the latest MCM transmission error + * \param self Reference to the related message object + * \return Returns the INIC transmission result which is provided as additional info + */ +Mns_AmsTx_Info_t Amsg_TxGetResultInfo(Mns_AmsTx_Msg_t *self) +{ + Mns_AmsTx_Info_t res = (Mns_AmsTx_Info_t)SELF_TX->temp_result; + + if ((SELF_TX->temp_result == MNS_MSG_STAT_ERROR_FATAL_WT) && (SELF_TX->ignore_wrong_target != false)) + { + res = MNS_AMSTX_I_SUCCESS; + } + + return res; +} + +/*! \brief Queues a Tx message at the tail of a list + * \param self The instance + * \param list_ptr Reference to the list + */ +void Amsg_TxEnqueue(Mns_AmsTx_Msg_t* self, CDlList* list_ptr) +{ + Dl_InsertTail(list_ptr, &SELF_TX->node); +} + +/*! \brief Retrieves the next segment count + * \param self The instance + * \return The next segment count as uint16_t + */ +uint16_t Amsg_TxGetNextSegmCnt(Mns_AmsTx_Msg_t *self) +{ + return SELF_TX->next_segment_cnt; +} + +/*! \brief Increments the next segment count + * \param self The instance + */ +void Amsg_TxIncrementNextSegmCnt(Mns_AmsTx_Msg_t *self) +{ + SELF_TX->next_segment_cnt++; +} + +/*! \brief Retrieves the follower id which labels all telegrams of a segmented message + * \param self The instance + * \return The follower id + */ +uint8_t Amsg_TxGetFollowerId(Mns_AmsTx_Msg_t *self) +{ + return SELF_TX->follower_id; +} + +/*! \brief Sets the follower id which labels all telegrams of a segmented message + * \param self The instance + * \param id The follower id + */ +void Amsg_TxSetFollowerId(Mns_AmsTx_Msg_t *self, uint8_t id) +{ + SELF_TX->follower_id = id; +} + +/*! \brief Replaces the current destination address by a new one. + * \details The current destination address can be restore by Amsg_TxRestoreDestinationAddr(). + * \param self The instance + * \param new_destination The new destination address + */ +void Amsg_TxReplaceDestinationAddr(Mns_AmsTx_Msg_t *self, uint16_t new_destination) +{ + SELF_TX->backup_dest_address = self->destination_address; /* internal backup of current destination address */ + self->destination_address = new_destination; /* replace public destination address */ +} + +/*! \brief Restores the destination address which was saved by calling Amsg_TxReplaceDestinationAddr(). + * \param self The instance + */ +static void Amsg_TxRestoreDestinationAddr(Mns_AmsTx_Msg_t *self) +{ + if (SELF_TX->backup_dest_address != AMSG_TX_BACKUP_ADDR_NONE) + { + self->destination_address = SELF_TX->backup_dest_address;/* restore public destination address */ + } +} + +/*! \brief Removes a message from a given queue + * \param self The instance + * \param list_ptr The queue that contains the message + */ +void Amsg_TxRemoveFromQueue(Mns_AmsTx_Msg_t *self, CDlList *list_ptr) +{ + (void)Dl_Remove(list_ptr, &SELF_TX->node); +} + +/*! \brief Peeks a Tx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Tx message + */ +Mns_AmsTx_Msg_t* Amsg_TxPeek(CDlList* list_ptr) +{ + Mns_AmsTx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PeekHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*! \brief Removes a Tx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Tx message + */ +Mns_AmsTx_Msg_t* Amsg_TxDequeue(CDlList* list_ptr) +{ + Mns_AmsTx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PopHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx Message */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes aggregated objects + * \details Needs to be called once before first usage. Call Amsg_RxHandleSetup() before + * repeated usage. + * \param self Reference to an internal Application Message Rx handle + * \param info_ptr Memory information required to free the object + */ +void Amsg_RxCtor(Mns_AmsRx_Msg_t *self, void *info_ptr) +{ + Dln_Ctor(&SELF_RX->node, SELF_RX); + SELF_RX->info_ptr = info_ptr; /* reset memory information */ + SELF_RX->memory_sz = 0U; + SELF_RX->memory_ptr = NULL; + SELF_RX->memory_info_ptr = NULL; +} + +/*! \brief Copies all attributes and payload from a Tx message to the Rx message + * \details The caller has to ensure that the payload size of the Rx message is equal + * or greater than the payload size of the Tx message. + * \param self Reference to an Rx message object + * \param tx_ptr Reference to an Tx message object + * \param source_address The source address that shall be set in the Rx message + */ +void Amsg_RxBuildFromTx(Mns_AmsRx_Msg_t *self, Mns_AmsTx_Msg_t *tx_ptr, uint16_t source_address) +{ + TR_ASSERT(0xFU,"[AMSG]", (SELF_RX->memory_sz >= tx_ptr->data_size)); + + self->receive_type = Amsg_RxGetReceiveType(tx_ptr->destination_address); + self->source_address = source_address; + self->fblock_id = tx_ptr->fblock_id; + self->instance_id = tx_ptr->instance_id; + self->function_id = tx_ptr->function_id; + self->op_type = tx_ptr->op_type; + self->data_size = tx_ptr->data_size; + + Misc_MemCpy(self->data_ptr, tx_ptr->data_ptr, (size_t)self->data_size); +} + +/*! \brief Sets all attributes of an internal Rx message to valid values + * \param self Reference to an internal Rx message object + * \details Assigned payload memory has to be freed before calling this function + */ +void Amsg_RxHandleSetup(Mns_AmsRx_Msg_t *self) +{ + MISC_MEM_SET((void *)&SELF_RX->pb_msg, 0, sizeof(SELF_RX->pb_msg)); /* cleanup public message object */ + SELF_RX->pb_msg.data_ptr = SELF_RX->memory_ptr; /* set data to valid memory */ + SELF_RX->gc_marker = false; /* reset garbage collector flag */ + SELF_RX->exp_tel_cnt = 0U; /* reset TelCnt */ +} + +/*! \brief Evaluates if an Application Message has the same functional address + * as a MOST telegram + * \param self Reference to an internal Application Message Rx handle + * \param tel_ptr Reference to a MOST message object + * \return Returns \c true if both message objects have the same functional address, + * otherwise \c false. + */ +bool Amsg_RxHandleIsIdentical(Mns_AmsRx_Msg_t *self, Msg_MostTel_t *tel_ptr) +{ + bool result; + + if ((self->source_address == tel_ptr->source_addr) + && (self->fblock_id == tel_ptr->id.fblock_id) + && (self->instance_id == tel_ptr->id.instance_id) + && (self->function_id == tel_ptr->id.function_id) + && (self->op_type == tel_ptr->id.op_type)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/*! \brief Copies the Rx message signature from a MOST message object to an + * internal Application message object + * \param self Reference to an internal Application Message Rx handle + * \param src_ptr Reference to a MOST message object + */ +void Amsg_RxCopySignatureFromTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr) +{ + self->source_address = src_ptr->source_addr; + self->receive_type = Amsg_RxGetReceiveType(src_ptr->destination_addr); + self->fblock_id = src_ptr->id.fblock_id; + self->instance_id = src_ptr->id.instance_id; + self->function_id = src_ptr->id.function_id; + self->op_type = src_ptr->id.op_type; +} + +/*! \brief Copies the Rx message signature from an internal Application + * message object to a MOST message object + * \param self Reference to an internal Application Message Rx handle + * \param target_ptr Reference to a MOST message object + */ +void Amsg_RxCopySignatureToTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* target_ptr) +{ + target_ptr->source_addr = self->source_address; + target_ptr->destination_addr = MNS_ADDR_DEBUG; + target_ptr->id.fblock_id = self->fblock_id; + target_ptr->id.instance_id = self->instance_id; + target_ptr->id.function_id = self->function_id; + target_ptr->id.op_type = self->op_type; +} + +/*! \brief Retrieves the addressing type related to a destination_address of an Rx message + * \param destination_address The destination address of an Rx message + * \return The receive type related to the destination address + */ +static Mns_AmsRx_ReceiveType_t Amsg_RxGetReceiveType(uint16_t destination_address) +{ + Mns_AmsRx_ReceiveType_t ret = MNS_AMSRX_RCT_SINGLECAST; + + if ((destination_address == MNS_ADDR_BROADCAST_BLOCKING) || + (destination_address == MNS_ADDR_BROADCAST_UNBLOCKING)) + { + ret = MNS_AMSRX_RCT_BROADCAST; + } + else if ((destination_address >= 0x0300U) && /* 0x300..0x3FF is reserved for group cast */ + (destination_address < 0x0400U)) + { + ret = MNS_AMSRX_RCT_GROUPCAST; + } + + return ret; +} + +/*! \brief Appends payload of an Rx MOST message object to internal Application + * message object + * \param self Reference to an internal Application Message Rx handle + * \param src_ptr Reference to a MOST message object + * \return Returns \c true if the payload was appended successfully, + * otherwise \c false. + */ +bool Amsg_RxAppendPayload(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr) +{ + uint8_t cnt; + bool ret = false; + const uint16_t curr_size = SELF_RX->pb_msg.data_size; /* get current message size */ + + if ((SELF_RX->memory_sz - src_ptr->tel.tel_len) >= SELF_RX->pb_msg.data_size) /* is size sufficient */ + { + for (cnt = 0U; cnt < src_ptr->tel.tel_len; cnt++) + { + SELF_RX->pb_msg.data_ptr[curr_size + (uint16_t)cnt] = src_ptr->tel.tel_data_ptr[cnt]; + } + + SELF_RX->pb_msg.data_size = curr_size + src_ptr->tel.tel_len; /* update message size */ + SELF_RX->exp_tel_cnt++; + ret = true; + } + + return ret; +} + +/*! \brief Copies data to allocated payload buffer + * \param self The instance + * \param data Reference to external payload data + * \param data_sz Size of external payload data + */ +void Amsg_RxCopyToPayload(Mns_AmsRx_Msg_t *self, uint8_t data[], uint8_t data_sz) +{ + MISC_MEM_CPY(&self->data_ptr[0], &data[0], (size_t)data_sz); /* parasoft-suppress MISRA2004-20_3 "data_sz is limited and checked via Msg_VerifyContent()" */ + self->data_size = data_sz; /* remember payload size */ +} + +/*! \brief Checks if the message has externally allocated payload memory + * \param self The instance + * \return Returns \c true if external payload is assigned to the message, otherwise \c false. + */ +bool Amsg_RxHasExternalPayload(Mns_AmsRx_Msg_t *self) +{ + return (SELF_RX->memory_sz > 0U); +} + +/*! \brief Sets payload memory provided by memory management and updates data pointer and size + * \param self The instance + * \param mem_ptr Reference to the provided memory chunk + * \param mem_size Size of the provided memory chunk + * \param info_ptr Optional reference for memory management + */ +void Amsg_RxHandleSetMemory(Mns_AmsRx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *info_ptr) +{ + SELF_RX->memory_ptr = mem_ptr; + SELF_RX->memory_info_ptr = info_ptr; + SELF_RX->memory_sz = mem_size; + + SELF_RX->pb_msg.data_ptr = mem_ptr; + SELF_RX->pb_msg.data_size = 0U; +} + +/*! \brief Queues an Rx message at the tail of a list + * \param self The instance + * \param list_ptr Reference to the list + */ +void Amsg_RxEnqueue(Mns_AmsRx_Msg_t* self, CDlList* list_ptr) +{ + Dl_InsertTail(list_ptr, &SELF_RX->node); +} + +/*! \brief Sets or resets the garbage collector flag + * \param self The instance + * \param value New value of the flag + */ +void Amsg_RxSetGcMarker(Mns_AmsRx_Msg_t* self, bool value) +{ + SELF_RX->gc_marker = value; +} + +/*! \brief Retrieves the value of the garbage collector flag + * \param self The instance + * \return The current value of the flag + */ +bool Amsg_RxGetGcMarker(Mns_AmsRx_Msg_t* self) +{ + return SELF_RX->gc_marker; +} + +/*! \brief Retrieves the next expected telegram count + * \param self The instance + * \return The next expected telegram count as uint8_t + */ +uint8_t Amsg_RxGetExpTelCnt(Mns_AmsRx_Msg_t* self) +{ + return SELF_RX->exp_tel_cnt; +} + +/*! \brief Peeks an Rx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Rx message + */ +Mns_AmsRx_Msg_t* Amsg_RxPeek(CDlList* list_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PeekHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*! \brief Removes an Rx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Rx message + */ +Mns_AmsRx_Msg_t* Amsg_RxDequeue(CDlList* list_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PopHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amsmessage.h b/mnsl/mns_amsmessage.h new file mode 100644 index 0000000..b1faa65 --- /dev/null +++ b/mnsl/mns_amsmessage.h @@ -0,0 +1,217 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of Application Message Classes + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSMSG + * @{ + */ + +#ifndef MNS_AMSMESSAGE_H +#define MNS_AMSMESSAGE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams_pb.h" +#include "mns_message.h" +#include "mns_dl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ +#define AMSG_TX_OBJECT_SZ (sizeof(Amsg_IntMsgTx_t)) +#define AMSG_RX_OBJECT_SZ (sizeof(Amsg_IntMsgRx_t)) + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Internal transmission result of an application message */ +typedef enum Amsg_TxIntStatus_ +{ + AMSG_TX_INTRES_NONE = 0x00U, /*!< \brief The internal transmission is not applicable. */ + AMSG_TX_INTRES_SUCCESS = 0x01U, /*!< \brief The internal transmission succeeded. */ + AMSG_TX_INTRES_ERRBUF = 0x02U /*!< \brief The internal transmission failed. */ + +} Amsg_TxIntStatus_t; + +/*! \brief Assignable function which is invoked as soon as an application message is received + * completely and available in the Rx message queue + * \param self The instance (optional) + * \param msg_ptr Reference to the received message + */ +typedef void (*Amsg_RxCompleteCb_t)(void* self, Mns_AmsRx_Msg_t* msg_ptr); + +/*! \brief Callback function type which is fired as soon as an AMS transmission was finished + * \param self The instance (optional) + * \param msg_ptr Reference to the related message object + * \param result Transmission result + * \param info Detailed INIC transmission result + */ +typedef void (*Amsg_TxCompleteCb_t)(void* self, Mns_AmsTx_Msg_t* msg_ptr, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); + +/*! \brief Single instance API callback function type which is fired as soon as an AMS transmission was finished + * \param msg_ptr Reference to the related message object + * \param result Transmission result + * \param info Detailed INIC transmission result + */ +typedef void (*Amsg_TxCompleteSiaCb_t)(Mns_AmsTx_Msg_t* msg_ptr, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); + +/*! \brief Callback function which is invoked to free a Tx message object to the owning pool + * \param owner_ptr The owning pool of the message object + * \param msg_ptr Reference to the related message object + */ +typedef void (*Amsg_TxFreedCb_t)(void *owner_ptr, Mns_AmsTx_Msg_t* msg_ptr); + +/*! \brief Keeps callback functions to an external memory management for Rx payload */ +typedef struct Ams_MemAllocator_ +{ + void *inst_ptr; /*!< \brief The instance of the (external) memory management */ + Mns_Ams_AllocMemCb_t alloc_fptr; /*!< \brief This function is invoked to allocate Rx user payload */ + Mns_Ams_FreeMemCb_t free_fptr; /*!< \brief This function is invoked to free Rx user payload */ + +} Ams_MemAllocator_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Class */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Internal Tx message structure */ +typedef struct Amsg_IntMsgTx_ +{ + Mns_AmsTx_Msg_t pb_msg; /*!< \brief Public message struct must be the first member */ + void *info_ptr; /*!< \brief Custom object information required by memory management */ + + void *free_inst_ptr; /*!< \brief Reference which is passed to free_ptr */ + Amsg_TxFreedCb_t free_fptr; /*!< \brief Callback function which is called to free the object */ + + uint8_t *memory_ptr; /*!< \brief Reference to payload provided by memory management */ + void *memory_info_ptr; /*!< \brief Custom payload information required by memory management */ + uint16_t memory_sz; /*!< \brief Size of the payload that is provided by memory management */ + + uint16_t next_segment_cnt; /*!< \brief Specifies the next segment count. '0xFF' means size prefixed */ + uint8_t follower_id; /*!< \brief Identifier of segmented messages and corresponding telegrams */ + Mns_MsgTxStatus_t temp_result; /*!< \brief Stores the temporary result that is notified when then transmission + * has completed + */ + uint16_t backup_dest_address; /*!< \brief Backup of replaced target address. */ + Amsg_TxIntStatus_t internal_status; /*!< \brief Stores the internal transmission status */ + bool ignore_wrong_target; /*!< \brief Forces the message to report transmission result "success", although + * the INIC has reported transmission error "wrong target" + */ + CDlNode node; /*!< \brief Node required for message pool */ + + Amsg_TxCompleteSiaCb_t complete_sia_fptr; /*!< \brief Single instance API Callback function which is invoked + * after transmission completed + */ + Amsg_TxCompleteCb_t complete_fptr; /*!< \brief Callback function which is invoked after transmission + * completed + */ + void *complete_inst_ptr; /*!< \brief Instance pointer which is required to invoke complete_fptr */ + +} Amsg_IntMsgTx_t; + +/*! \brief Internal Rx message structure */ +typedef struct Amsg_IntMsgRx_ +{ + Mns_AmsRx_Msg_t pb_msg; /*!< \brief Public message structure must be the first member */ + void *info_ptr; /*!< \brief Custom object information required by memory management */ + + uint8_t *memory_ptr; /*!< \brief Reference to payload provided by memory management */ + void *memory_info_ptr; /*!< \brief Custom payload information required by memory management */ + uint16_t memory_sz; /*!< \brief The size of the allocated user payload in bytes */ + + CDlNode node; /*!< \brief Node required for message pool */ + + uint8_t exp_tel_cnt; /*!< \brief The expected TelCnt used for segmented transfer */ + bool gc_marker; /*!< \brief Identifies message objects that were already + * marked by the garbage collector. + */ +} Amsg_IntMsgRx_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Class methods */ +/*------------------------------------------------------------------------------------------------*/ +/* Tx */ +extern void Amsg_TxCtor(Mns_AmsTx_Msg_t *self, void *info_ptr, Amsg_TxFreedCb_t free_fptr, void *free_inst_ptr); +extern void Amsg_TxSetInternalPayload(Mns_AmsTx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *mem_info_ptr); +extern void Amsg_TxReuse(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxSetCompleteCallback(Mns_AmsTx_Msg_t *self, Amsg_TxCompleteSiaCb_t compl_sia_fptr, + Amsg_TxCompleteCb_t compl_fptr, void* compl_inst_ptr); +extern void Amsg_TxNotifyComplete(Mns_AmsTx_Msg_t *self, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); +extern void Amsg_TxFreeUnused(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxUpdateInternalResult(Mns_AmsTx_Msg_t *self, Amsg_TxIntStatus_t result); +extern void Amsg_TxUpdateResult(Mns_AmsTx_Msg_t *self, Mns_MsgTxStatus_t result); +extern Mns_AmsTx_Result_t Amsg_TxGetResultCode(Mns_AmsTx_Msg_t *self); +extern Mns_AmsTx_Info_t Amsg_TxGetResultInfo(Mns_AmsTx_Msg_t *self); +extern uint16_t Amsg_TxGetNextSegmCnt(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxIncrementNextSegmCnt(Mns_AmsTx_Msg_t *self); +extern uint8_t Amsg_TxGetFollowerId(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxSetFollowerId(Mns_AmsTx_Msg_t *self, uint8_t id); +extern void Amsg_TxReplaceDestinationAddr(Mns_AmsTx_Msg_t *self, uint16_t new_destination); +extern void Amsg_TxRemoveFromQueue(Mns_AmsTx_Msg_t *self, CDlList *list_ptr); +extern void Amsg_TxEnqueue(Mns_AmsTx_Msg_t* self, CDlList* list_ptr); +extern Mns_AmsTx_Msg_t* Amsg_TxPeek(CDlList* list_ptr); +extern Mns_AmsTx_Msg_t* Amsg_TxDequeue(CDlList* list_ptr); + +/* Rx */ +extern void Amsg_RxCtor(Mns_AmsRx_Msg_t *self, void *info_ptr); +extern void Amsg_RxBuildFromTx(Mns_AmsRx_Msg_t *self, Mns_AmsTx_Msg_t *tx_ptr, uint16_t source_address); +extern void Amsg_RxHandleSetup(Mns_AmsRx_Msg_t *self); +extern void Amsg_RxHandleSetMemory(Mns_AmsRx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *info_ptr); +extern bool Amsg_RxHandleIsIdentical(Mns_AmsRx_Msg_t *self, Msg_MostTel_t *tel_ptr); +extern void Amsg_RxCopySignatureFromTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr); +extern void Amsg_RxCopySignatureToTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* target_ptr); +extern void Amsg_RxCopyToPayload(Mns_AmsRx_Msg_t *self, uint8_t data[], uint8_t data_sz); +extern bool Amsg_RxAppendPayload(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr); +extern bool Amsg_RxHasExternalPayload(Mns_AmsRx_Msg_t *self); +extern void Amsg_RxEnqueue(Mns_AmsRx_Msg_t* self, CDlList* list_ptr); +extern void Amsg_RxSetGcMarker(Mns_AmsRx_Msg_t* self, bool value); +extern bool Amsg_RxGetGcMarker(Mns_AmsRx_Msg_t* self); +extern uint8_t Amsg_RxGetExpTelCnt(Mns_AmsRx_Msg_t* self); +/* Rx helpers */ +extern Mns_AmsRx_Msg_t* Amsg_RxPeek(CDlList* list_ptr); +extern Mns_AmsRx_Msg_t* Amsg_RxDequeue(CDlList* list_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_AMSMESSAGE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amspool.c b/mnsl/mns_amspool.c new file mode 100644 index 0000000..3ce89ae --- /dev/null +++ b/mnsl/mns_amspool.c @@ -0,0 +1,336 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Pool + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSPOOL + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_amspool.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal macros */ +/*------------------------------------------------------------------------------------------------*/ +#define INT_RX(ptr) ((Amsg_IntMsgRx_t*)(void*)(ptr)) /* parasoft-suppress MISRA2004-19_7 "common definition of type cast improves code" */ +#define INT_TX(ptr) ((Amsg_IntMsgTx_t*)(void*)(ptr)) /* parasoft-suppress MISRA2004-19_7 "common definition of type cast improves code" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Amsp_FreeTxObj(void *self, Mns_AmsTx_Msg_t* msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of application message pool class + * \param self The instance + * \param mem_allocator_ptr Reference to memory allocator + * \param mns_inst_id MOST NetServices instance ID + */ +void Amsp_Ctor(CAmsMsgPool *self, Ams_MemAllocator_t *mem_allocator_ptr, uint8_t mns_inst_id) +{ + self->mns_inst_id = mns_inst_id; + self->allocator_ptr = mem_allocator_ptr; + self->rx_rsvd_msg_ptr = Amsp_AllocRxObj(self, 45U); + self->rx_rsvd_msg_ref = self->rx_rsvd_msg_ptr; + self->terminated = false; + self->tx_notify_freed = false; + self->rx_notify_freed = false; + Sub_Ctor(&self->tx_freed_subject, self->mns_inst_id); + Sub_Ctor(&self->rx_freed_subject, self->mns_inst_id); + + TR_ASSERT(self->mns_inst_id, "[AMSP]", (self->rx_rsvd_msg_ptr != NULL)); +} + +/*! \brief Frees pre-allocated message memory + * \param self The instance + */ +void Amsp_Cleanup(CAmsMsgPool *self) +{ + Amsg_IntMsgRx_t *msg_ptr = INT_RX(self->rx_rsvd_msg_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Amsp_Cleanup: rx_rsvd_msg_ptr=0x%p", 1U, self->rx_rsvd_msg_ptr)); + + self->terminated = true; + self->tx_notify_freed = false; + self->rx_notify_freed = false; + + if (msg_ptr != NULL) + { + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr->memory_ptr, MNS_AMS_RX_PAYLOAD, msg_ptr->memory_info_ptr); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_RX_OBJECT, msg_ptr->info_ptr); + self->rx_rsvd_msg_ref = NULL; + self->rx_rsvd_msg_ptr = NULL; + } +} + +/*! \brief Assigns an observer which is invoked as soon as memory dedicated to a Tx message is + * freed.The data_ptr of the update callback function is not used (always \c NULL). + * See \ref Obs_UpdateCb_t. + * \param self The instance + * \param observer_ptr The observer + */ +void Amsp_AssignTxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr) +{ + (void)Sub_AddObserver(&self->tx_freed_subject, observer_ptr); +} + +/*! \brief Assigns an observer which is invoked as soon as memory dedicated to a Tx message is + * freed.The data_ptr of the update callback function is not used (always \c NULL). + * See \ref Obs_UpdateCb_t. + * \param self The instance + * \param observer_ptr The observer + */ +void Amsp_AssignRxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr) +{ + (void)Sub_AddObserver(&self->rx_freed_subject, observer_ptr); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx allocations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an internal Tx message object (without payload) + * \param self The instance + * \param payload_sz The required payload size in bytes + * \return Reference to the Tx message object if the allocation succeeds. Otherwise \c NULL. + */ +Mns_AmsTx_Msg_t* Amsp_AllocTxObj(CAmsMsgPool *self, uint16_t payload_sz) +{ + void *payload_info_ptr = NULL; + void *payload_ptr = NULL; + void *obj_info_ptr = NULL; + Mns_AmsTx_Msg_t *msg_ptr = (Mns_AmsTx_Msg_t*)self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, AMSG_TX_OBJECT_SZ, MNS_AMS_TX_OBJECT, &obj_info_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating TxObject: msg_ptr=0x%p, size=%d, info_ptr=0x%p", 3U, msg_ptr, AMSG_TX_OBJECT_SZ, obj_info_ptr)); + + if (msg_ptr != NULL) + { + if (payload_sz > 0U) + { + payload_ptr = self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, payload_sz, MNS_AMS_TX_PAYLOAD, &payload_info_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating TxPayload: msg_ptr=0x%p, mem_ptr=0x%p, size=%d, info_ptr=0x%p", 4U, msg_ptr, payload_ptr, payload_sz, payload_info_ptr)); + + if (payload_ptr == NULL) + { + TR_INFO((self->mns_inst_id, "[AMSP]", "Freeing TxObject: msg_ptr=0x%p, info_ptr=0x%p", 2U, msg_ptr, obj_info_ptr)); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_TX_OBJECT, obj_info_ptr); + msg_ptr = NULL; + } + } + } + + if (msg_ptr != NULL) + { + Amsg_TxCtor(msg_ptr, obj_info_ptr, &Amsp_FreeTxObj, self); + + if (payload_ptr != NULL) + { + Amsg_TxSetInternalPayload(msg_ptr, (uint8_t*)payload_ptr, payload_sz, payload_info_ptr); + } + } + else + { + self->tx_notify_freed = true; + } + + return msg_ptr; +} + +/*! \brief Frees an internal Tx message object including its payload + * \param self The instance + * \param msg_ptr Reference to the internal Tx message object + */ +static void Amsp_FreeTxObj(void *self, Mns_AmsTx_Msg_t* msg_ptr) +{ + CAmsMsgPool *self_ = (CAmsMsgPool*)self; + Amsg_IntMsgTx_t *obj_ptr = INT_TX(msg_ptr); + + if (obj_ptr->memory_ptr != NULL) + { + TR_INFO((self_->mns_inst_id, "[AMSP]", "Freeing TxPayload: msg_ptr=0x%p, mem_ptr=0x%p, info_ptr=0x%p", 3U, msg_ptr, obj_ptr->memory_ptr, obj_ptr->memory_info_ptr)); + self_->allocator_ptr->free_fptr(self_->allocator_ptr->inst_ptr, obj_ptr->memory_ptr, MNS_AMS_TX_PAYLOAD, obj_ptr->memory_info_ptr); + Amsg_TxSetInternalPayload(msg_ptr, NULL, 0U, NULL); + } + + TR_INFO((self_->mns_inst_id, "[AMSP]", "Freeing TxObject: msg_ptr=0x%p, info_ptr=0x%p", 2U, msg_ptr, obj_ptr->info_ptr)); + self_->allocator_ptr->free_fptr(self_->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_TX_OBJECT, obj_ptr->info_ptr); + + if (self_->tx_notify_freed) + { + Sub_Notify(&self_->tx_freed_subject, NULL); + self_->tx_notify_freed = false; + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx allocations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an internal Rx message object (optionally with payload) + * \param self The instance + * \param payload_sz The required payload size that shall be allocated and assigned to the object. + * Value "0" means that no payload memory shall be allocated in the same turn. + * \return Reference to the Rx message object if the allocation succeeds. Otherwise \c NULL. + */ +Mns_AmsRx_Msg_t* Amsp_AllocRxObj(CAmsMsgPool *self, uint16_t payload_sz) +{ + void *info_ptr = NULL; + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, AMSG_RX_OBJECT_SZ, MNS_AMS_RX_OBJECT, &info_ptr); + + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating RxObject: msg_ptr=0x%p, size=%d, info_ptr=0x%p", 3U, msg_ptr, AMSG_RX_OBJECT_SZ, info_ptr)); + + if (msg_ptr != NULL) + { + Amsg_RxCtor(msg_ptr, info_ptr); + Amsg_RxHandleSetup(msg_ptr); + + if (payload_sz != 0U) + { + if (!Amsp_AllocRxPayload(self, payload_sz, msg_ptr)) + { + Amsp_FreeRxObj(self, msg_ptr); /* payload allocation has failed - release message object */ + msg_ptr = NULL; + } + } + } + + return msg_ptr; +} + +/*! \brief Allocates a reserved Rx message object with payload up to 45 bytes payload + * \param self The instance + * \return Reference to the Rx message object if the allocation succeeds. Otherwise \c NULL. + */ +Mns_AmsRx_Msg_t* Amsp_AllocRxRsvd(CAmsMsgPool *self) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + + if (self->rx_rsvd_msg_ptr != NULL) + { + msg_ptr = self->rx_rsvd_msg_ptr; + self->rx_rsvd_msg_ptr = NULL; + Amsg_RxHandleSetup(msg_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Retrieving reserved RxObject: msg_ptr=0x%p", 1U, msg_ptr)); + } + else + { + self->rx_notify_freed = true; + } + + return msg_ptr; +} + +/*! \brief Allocates payload for an internal Rx message object + * \param self The instance + * \param payload_sz Payload size in bytes + * \param msg_ptr Reference to the internal Rx message object + * \return Returns \c true if the allocation succeeds. Otherwise \c NULL. + */ +bool Amsp_AllocRxPayload(CAmsMsgPool *self, uint16_t payload_sz, Mns_AmsRx_Msg_t* msg_ptr) +{ + bool success = false; + void *info_ptr = NULL; + void *mem_ptr = self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, payload_sz, MNS_AMS_RX_PAYLOAD, &info_ptr); + + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating RxPayload: msg_ptr=0x%p, mem_ptr=0x%p, size=%d, info_ptr=0x%p", 4U, msg_ptr, mem_ptr, payload_sz, info_ptr)); + TR_ASSERT(self->mns_inst_id, "[AMSP]", (msg_ptr != NULL)); /* message reference is required */ + TR_ASSERT(self->mns_inst_id, "[AMSP]", (msg_ptr != self->rx_rsvd_msg_ref)); /* forbidden overwrite of pre-allocated message payload */ + + if (mem_ptr != NULL) + { + Amsg_RxHandleSetMemory(msg_ptr, (uint8_t*)mem_ptr, payload_sz, info_ptr); + success = true; + } + + return success; +} + +/*! \brief Frees an internal Rx message object + * \param self The instance + * \param msg_ptr Reference to the internal Rx message object + * \details Payload that is assigned to the message object has to be freed + * separately by using Amsp_FreeRxPayload(). + */ +void Amsp_FreeRxObj(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr) +{ + if (msg_ptr == self->rx_rsvd_msg_ref) + { + TR_ASSERT(self->mns_inst_id, "[AMSP]", (self->rx_rsvd_msg_ptr == NULL)); /* before freeing, message shall be reserved */ + TR_INFO((self->mns_inst_id, "[AMSP]", "Restoring reserved RxObject: msg_ptr=0x%p", 1U, msg_ptr)); + self->rx_rsvd_msg_ptr = self->rx_rsvd_msg_ref; /* restore reserved message */ + + if (self->terminated != false) + { /* also free reserved message if it is freed */ + Amsp_Cleanup(self); /* from any queue after Amsp_Cleanup() */ + } + } + else + { + Amsg_IntMsgRx_t *obj_ptr = INT_RX(msg_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Freeing RxObject: msg_ptr=0x%p, info_ptr=0x%p", 2U, msg_ptr, obj_ptr->info_ptr)); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_RX_OBJECT, obj_ptr->info_ptr); + } + + if (self->rx_notify_freed) + { + Sub_Notify(&self->rx_freed_subject, NULL); + self->rx_notify_freed = false; + } +} + +/*! \brief Frees payload that is associated with an internal Rx message object + * \param self The instance + * \param msg_ptr Reference to the internal Rx message object + */ +void Amsp_FreeRxPayload(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr) +{ + Amsg_IntMsgRx_t *obj_ptr = INT_RX(msg_ptr); + + if (msg_ptr == self->rx_rsvd_msg_ref) + { + TR_ASSERT(self->mns_inst_id, "[AMSP]", (self->rx_rsvd_msg_ptr == NULL)); /* release payload before object */ + TR_INFO((self->mns_inst_id, "[AMSP]", "Restoring reserved RxPayload: msg_ptr=0x%p", 1U, msg_ptr)); + } + else if (obj_ptr->memory_ptr != NULL) + { + TR_INFO((self->mns_inst_id, "[AMSP]", "Freeing RxPayload: msg_ptr=0x%p, mem_ptr=0x%p, info_ptr=0x%p", 3U, msg_ptr, obj_ptr->memory_ptr, obj_ptr->memory_info_ptr)); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, obj_ptr->memory_ptr, MNS_AMS_RX_PAYLOAD, obj_ptr->memory_info_ptr); + Amsg_RxHandleSetMemory(msg_ptr, NULL, 0U, NULL); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amspool.h b/mnsl/mns_amspool.h new file mode 100644 index 0000000..387dc9b --- /dev/null +++ b/mnsl/mns_amspool.h @@ -0,0 +1,100 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of Application Message Pools + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSPOOL + * @{ + */ + +#ifndef MNS_AMSPOOL_H +#define MNS_AMSPOOL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams_pb.h" +#include "mns_amsmessage.h" +#include "mns_obs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Classes */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class members of AMS Pool */ +typedef struct CAmsMsgPool_ +{ + Ams_MemAllocator_t *allocator_ptr; /*!< \brief Interface to memory allocator */ + Mns_AmsRx_Msg_t *rx_rsvd_msg_ptr; /*!< \brief Pre-allocated Rx message or NULL if no + * reserved message is available */ + Mns_AmsRx_Msg_t *rx_rsvd_msg_ref; /*!< \brief Stores the reference of the reserved message + * to identify it and restore the + * \c rx_rsvd_msg_ptr. */ + CSubject tx_freed_subject; /*!< \brief Allows to observe freed Tx message event */ + CSubject rx_freed_subject; /*!< \brief Allows to observe freed Rx message event */ + bool tx_notify_freed; /*!< \brief Is \c true when to notify the next Tx freed object */ + bool rx_notify_freed; /*!< \brief Is \c true when to notify the next Rx freed object */ + bool terminated; /*!< \brief Is \c true if a cleanup was done. Helps to release the + * pre-allocated message after the first cleanup attempt. */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CAmsMsgPool; + +/*------------------------------------------------------------------------------------------------*/ +/* Class methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Amsp_Ctor(CAmsMsgPool *self, Ams_MemAllocator_t *mem_allocator_ptr, uint8_t mns_inst_id); +extern void Amsp_Cleanup(CAmsMsgPool *self); +/* Tx */ +extern void Amsp_AssignTxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr); +extern Mns_AmsTx_Msg_t* Amsp_AllocTxObj(CAmsMsgPool *self, uint16_t payload_sz); +/* Rx */ +extern void Amsp_AssignRxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr); +extern Mns_AmsRx_Msg_t* Amsp_AllocRxObj(CAmsMsgPool *self, uint16_t payload_sz); +extern Mns_AmsRx_Msg_t* Amsp_AllocRxRsvd(CAmsMsgPool *self); +extern bool Amsp_AllocRxPayload(CAmsMsgPool *self, uint16_t payload_sz, Mns_AmsRx_Msg_t* msg_ptr); +extern void Amsp_FreeRxObj(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr); +extern void Amsp_FreeRxPayload(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_AMSPOOL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_base.c b/mnsl/mns_base.c new file mode 100644 index 0000000..542e485 --- /dev/null +++ b/mnsl/mns_base.c @@ -0,0 +1,70 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the Base class. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_BASE + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_base.h" +#include "mns_misc.h" +#include "mns_message.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CBase */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the Base class. + * \param self Instance pointer + * \param init_ptr Reference to the initialization data + */ +void Base_Ctor(CBase *self, Base_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + /* Save MOST NetServices instance ID */ + self->mns_inst_id = init_ptr->mns_inst_id; + /* Create the scheduler instance */ + Scd_Ctor(&self->scd, &init_ptr->scd); + /* Create the timer management instance */ + Tm_Ctor(&self->tm, &self->scd, &init_ptr->tm); + /* Create the event handler instance */ + Eh_Ctor(&self->eh, self->mns_inst_id); + /* Create the API locking manager instance */ + Alm_Ctor(&self->alm, &self->tm, &self->eh, self->mns_inst_id); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_base.h b/mnsl/mns_base.h new file mode 100644 index 0000000..132243d --- /dev/null +++ b/mnsl/mns_base.h @@ -0,0 +1,93 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the Base class. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_BASE + * @{ + */ + +#ifndef MNS_BASE_H +#define MNS_BASE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_timer.h" +#include "mns_scheduler.h" +#include "mns_trace.h" +#include "mns_eh.h" +#include "mns_alm.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of the Base class. */ +typedef struct Base_InitData_ +{ + Scd_InitData_t scd; /*!< \brief Initialization data of the scheduler */ + Tm_InitData_t tm; /*!< \brief Initialization data of the timer management */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} Base_InitData_t; + +/*! \brief Class structure of the Base class. */ +typedef struct CBase_ +{ + CScheduler scd; /*!< \brief Scheduler instance */ + CTimerManagement tm; /*!< \brief Timer management instance */ + CEventHandler eh; /*!< \brief Event handler instance */ + CApiLockingManager alm; /*!< \brief API locking manager instance */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CBase; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +void Base_Ctor(CBase *self, Base_InitData_t *init_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_BASE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_cfg.h b/mnsl/mns_cfg.h new file mode 100644 index 0000000..dcf6e2b --- /dev/null +++ b/mnsl/mns_cfg.h @@ -0,0 +1,67 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Configuration header file of MOST NetServices Light + */ + +#ifndef MNSL_CFG_H +#define MNSL_CFG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* File only needed for other includes. */ +#include "mns_types_cfg.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Message Pool */ +/*------------------------------------------------------------------------------------------------*/ +/* Sets the number of pre-allocated Rx messages which are shared by all FIFOs. Default value: 35*/ +/* #define MNSL_CHANNEL_POOL_SIZE_RX 35 */ + +/*------------------------------------------------------------------------------------------------*/ +/* Tracing & Debugging */ +/*------------------------------------------------------------------------------------------------*/ +/* Define the following macros to map info and error trace output to user defined functions. + * The purpose of these functions is debugging. It is not recommended to define these functions + * in a production system. + */ +#define MNS_TR_ERROR My_TraceError +// #define MNS_TR_INFO My_TraceInfo + +extern void My_TraceError(uint8_t mns_inst_id, const char module_str[], const char entry_str[], uint16_t vargs_cnt, ...); +extern void My_TraceInfo(uint8_t mns_inst_id, const char module_str[], const char entry_str[], uint16_t vargs_cnt, ...); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNSL_CFG_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ diff --git a/mnsl/mns_dl.c b/mnsl/mns_dl.c new file mode 100644 index 0000000..7a7a322 --- /dev/null +++ b/mnsl/mns_dl.c @@ -0,0 +1,392 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the doubly linked list. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_DL + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_dl.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CDlList */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the doubly linked list class. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Dl_Ctor(CDlList *self, uint8_t mns_inst_id) +{ + self->head = NULL; + self->tail = NULL; + self->size = 0U; + self->mns_inst_id = mns_inst_id; +} + +/*! \brief Inserts a new node after an arbitrary node. + * \param self Instance pointer + * \param node Reference of the initial node + * \param new_node Reference of the new node are to be inserted + */ +void Dl_InsertAfter(CDlList *self, CDlNode *node, CDlNode *new_node) +{ + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size <= 0xFFFFU)); + new_node->prev = node; + new_node->next = node->next; + if(node->next == NULL) /* Is initial node last node in list? */ + { + self->tail = new_node; /* Set new node as tail of list */ + } + else + { + node->next->prev = new_node; /* Adjust follower node */ + } + node->next = new_node; /* Adjust parent node */ + new_node->in_use = true; /* Signals that node is part of a list */ + self->size++; /* Increment number of nodes */ +} + +/*! \brief Inserts a new node before an arbitrary node. + * \param self Instance pointer + * \param node Reference of the initial node + * \param new_node Reference of the new node are to be inserted + */ +void Dl_InsertBefore(CDlList *self, CDlNode *node, CDlNode *new_node) +{ + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size <= 0xFFFFU)); + new_node->prev = node->prev; + new_node->next = node; + if(node->prev == NULL) /* Is initial node first node in list? */ + { + self->head = new_node; /* Set new node as head of list */ + } + else + { + node->prev->next = new_node; /* Adjust parent node */ + } + node->prev = new_node; /* Adjust follower node */ + new_node->in_use = true; /* Signals that node is part of a list */ + self->size++; /* Increment number of nodes */ +} + +/*! \brief Sets the new node as head of a doubly linked list. + * \param self Instance pointer + * \param new_node Reference of the new node are to be placed as head of the list + */ +void Dl_InsertHead(CDlList *self, CDlNode *new_node) +{ + if(self->head == NULL) /* Is list empty? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size <= 0xFFFFU)); + self->head = new_node; + self->tail = new_node; + new_node->prev = NULL; + new_node->next = NULL; + new_node->in_use = true; /* Signals that node is part of a list */ + self->size++; /* Increment number of nodes */ + } + else + { + Dl_InsertBefore(self, self->head, new_node); + } +} + +/*! \brief Inserts the new node at the end of a doubly linked list. + * \param self Instance pointer + * \param new_node Reference of the new node are to be placed at the end of the list + */ +void Dl_InsertTail(CDlList *self, CDlNode *new_node) +{ + if(self->tail == NULL) /* Is list empty? */ + { + Dl_InsertHead(self, new_node); + } + else + { + Dl_InsertAfter(self, self->tail, new_node); + } +} + +/*! \brief Removes an arbitrary node from a doubly linked list. + * \param self Instance pointer + * \param node Reference of the node are to be removed from the list + * \return \c DL_OK: No error + * \return \c DL_UNKNOWN_NODE: Given node is not part of this list + */ +Dl_Ret_t Dl_Remove(CDlList *self, CDlNode *node) +{ + Dl_Ret_t ret_val = DL_UNKNOWN_NODE; + + if(Dl_IsNodeInList(self, node) != false) /* Is node part of list? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size > 0U)); + if(node->prev == NULL) /* First node in list? */ + { + self->head = node->next; /* Replace head node with next node in list */ + } + else /* -> Not first node in list */ + { + node->prev->next = node->next; /* Set next pointer of previous node to next node */ + } + if(node->next == NULL) /* Last node in list? */ + { + self->tail = node->prev; /* Replace tail node with previous node in list */ + } + else /* -> Not last node in list */ + { + node->next->prev = node->prev; /* Set previous ptr of next node to previous node */ + } + node->prev = NULL; + node->next = NULL; + node->in_use = false; /* Signals that node is not part of a list */ + ret_val = DL_OK; + self->size--; /* Decrement number of nodes */ + } + + return ret_val; +} + +/*! \brief Removes the first node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the removed head node or \c NULL if the list is empty. + */ +CDlNode * Dl_PopHead(CDlList *self) +{ + CDlNode *node = self->head; + + if(NULL != node) /* Is list not empty? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size > 0U)); + self->head = node->next; /* Replace head node with next node in list */ + if(node->next == NULL) /* Last node in list? */ + { + self->tail = NULL; /* Replace tail node and set list's tail pointer + * to NULL + */ + } + else /* -> Not last node in list */ + { + node->next->prev = NULL; /* Set previous pointer of next node to NULL */ + } + node->prev = NULL; + node->next = NULL; + node->in_use = false; /* Signals that node is not part of a list */ + self->size--; /* Decrement number of nodes */ + } + + return node; +} + +/*! \brief Removes the last node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the removed tail node or \c NULL if the list is empty. + */ +CDlNode * Dl_PopTail(CDlList *self) +{ + CDlNode *node = self->tail; + + if(NULL != node) /* Is list not empty? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size > 0U)); + if(node->prev == NULL) /* First node in list? */ + { + self->head = NULL; /* Replace head node and set list's head pointer + * to NULL + */ + } + else /* -> Not first node in list */ + { + node->prev->next = NULL; /* Set next pointer of previous node to NULL */ + } + self->tail = node->prev; /* Replace tail node with previous node in list */ + node->prev = NULL; + node->next = NULL; + node->in_use = false; /* Signals that node is not part of a list */ + self->size--; /* Decrement number of nodes */ + } + + return node; +} + +/*! \brief Returns the reference of the first node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the head node or \c NULL if the list is empty. + */ +CDlNode * Dl_PeekHead(CDlList *self) +{ + return self->head; +} + +/*! \brief Returns the reference of the last node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the tail node or NULL if the list is empty. + */ +CDlNode * Dl_PeekTail(CDlList *self) +{ + return self->tail; +} + +/*! \brief Calls the given function for each node in the doubly linked list. If the func_ptr + * returns true the loop is stopped and the current node will be returned. + * \param self Instance pointer + * \param func_ptr Reference of the callback function which is called for each node + * \param user_data_ptr Reference of optional user data given to func_ptr + * \return Returns the current node or \c NULL if the whole list is processed. + */ +CDlNode * Dl_Foreach(CDlList *self, Dl_ForeachFunc_t func_ptr, void *user_data_ptr) +{ + CDlNode *ret_val = NULL; + CDlNode *node = self->head; + + while(NULL != node) /* End of list reached? */ + { + if(func_ptr(node->data_ptr, user_data_ptr) != false) /* Data found? */ + { + ret_val = node; + break; + } + node = node->next; + } + return ret_val; +} + +/*! \brief Checks if a node is part of the given doubly linked list. + * \param self Instance pointer + * \param node Reference of the searched node + * \return \c true: Node is part of the given list + * \return \c false: Node is not part of the given list + */ +bool Dl_IsNodeInList(CDlList *self, const CDlNode *node) +{ + bool ret_val = false; + CDlNode *current_node = self->head; + + while(NULL != current_node) /* End of list reached? */ + { + if(current_node == node) /* Is current node the searched one */ + { + ret_val = true; + break; + } + current_node = current_node->next; + } + return ret_val; +} + +/*! \brief Appends one doubly linked list to another doubly linked list. + * \param self Instance pointer + * \param list_ptr Reference to the doubly linked list + */ +void Dl_AppendList(CDlList *self, CDlList *list_ptr) +{ + TR_ASSERT(self->mns_inst_id, "[DL]", (list_ptr != NULL)); + if(list_ptr->head != NULL) + { + if(self->tail == NULL) /* Is list empty? */ + { + self->head = list_ptr->head; + self->tail = list_ptr->tail; + self->size = list_ptr->size; + } + else + { + list_ptr->head->prev = self->tail; + self->tail->next = list_ptr->head; + self->tail = list_ptr->tail; + self->size += list_ptr->size; + } + list_ptr->head = NULL; + list_ptr->tail = NULL; + list_ptr->size = 0U; + } +} + +/*! \brief Interface function to retrieve the list size. + * \param self Instance pointer + * \return Size of the list + */ +uint16_t Dl_GetSize(CDlList *self) +{ + return self->size; +} + + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CDlNode */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of doubly linked list nodes. + * \param self Instance pointer + * \param data_ptr Optional reference to data + */ +void Dln_Ctor(CDlNode *self, void *data_ptr) +{ + self->next = NULL; + self->prev = NULL; + self->in_use = false; + self->data_ptr = data_ptr; +} + +/*! \brief Interface function to set the data pointer of the given node. + * \param self Instance pointer + * \param data_ptr Reference of the new data + */ +void Dln_SetData(CDlNode *self, void *data_ptr) +{ + self->data_ptr = data_ptr; +} + +/*! \brief Interface function to request the data pointer of the given node. + * \param self Instance pointer + */ +void * Dln_GetData(CDlNode *self) +{ + return self->data_ptr; +} + +/*! \brief Checks if a node is part of a doubly linked list. + * \param self Instance pointer of the searched node + * \return \c true: Node is part of a list + * \return \c false: Node is not part of a list + */ +bool Dln_IsNodePartOfAList(CDlNode *self) +{ + return self->in_use; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_dl.h b/mnsl/mns_dl.h new file mode 100644 index 0000000..96e89e3 --- /dev/null +++ b/mnsl/mns_dl.h @@ -0,0 +1,132 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the doubly linked list. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_DL + * @{ + */ + +#ifndef MNS_DL_H +#define MNS_DL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback signature used by foreach-function + * \param d_ptr Reference to the data of the current node + * \param up_ptr Reference to the user data + * \return true: Stop the for-each-loop + * \return false: Continue the for-each-loop + */ +typedef bool(*Dl_ForeachFunc_t)(void *d_ptr, void *ud_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerators */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return values of the list module. */ +typedef enum Dl_Ret_ +{ + DL_OK, /*!< \brief No error */ + DL_UNKNOWN_NODE, /*!< \brief Unknown node */ + DL_STOPPED /*!< \brief Search process stopped */ + +} Dl_Ret_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of doubly linked list node. */ +typedef struct DlNode_ +{ + struct DlNode_ *prev; /*!< \brief Reference to previous node in list */ + struct DlNode_ *next; /*!< \brief Reference to next node in list */ + void *data_ptr; /*!< \brief Reference to optional data */ + bool in_use; /*!< \brief Flag which signals that the node is in use */ + +} CDlNode; + +/*! \brief Class structure of the doubly linked list. */ +typedef struct CDlList_ +{ + struct DlNode_ *head; /*!< \brief Reference to head of the list */ + struct DlNode_ *tail; /*!< \brief Reference to tail of the list */ + uint16_t size; /*!< \brief Number of nodes in the list */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CDlList; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CDlList */ +/*------------------------------------------------------------------------------------------------*/ +extern void Dl_Ctor(CDlList *self, uint8_t mns_inst_id); +extern void Dl_InsertAfter(CDlList *self, CDlNode *node, CDlNode *new_node); +extern void Dl_InsertBefore(CDlList *self, CDlNode *node, CDlNode *new_node); +extern void Dl_InsertHead(CDlList *self, CDlNode *new_node); +extern void Dl_InsertTail(CDlList *self, CDlNode *new_node); +extern Dl_Ret_t Dl_Remove(CDlList *self, CDlNode *node); +extern CDlNode * Dl_PopHead(CDlList *self); +extern CDlNode * Dl_PopTail(CDlList *self); +extern CDlNode * Dl_PeekHead(CDlList *self); +extern CDlNode * Dl_PeekTail(CDlList *self); +extern CDlNode * Dl_Foreach(CDlList *self, Dl_ForeachFunc_t func_ptr, void *user_data_ptr); +extern bool Dl_IsNodeInList(CDlList *self, const CDlNode *node); +extern void Dl_AppendList(CDlList *self, CDlList *list_ptr); +extern uint16_t Dl_GetSize(CDlList *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CDlNode */ +/*------------------------------------------------------------------------------------------------*/ +extern void Dln_Ctor(CDlNode *self, void *data_ptr); +extern void Dln_SetData(CDlNode *self, void *data_ptr); +extern void * Dln_GetData(CDlNode *self); +extern bool Dln_IsNodePartOfAList(CDlNode *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_DL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_eh.c b/mnsl/mns_eh.c new file mode 100644 index 0000000..36e0168 --- /dev/null +++ b/mnsl/mns_eh.c @@ -0,0 +1,155 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the event handler. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_EH + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_eh.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static bool Eh_EncodeEvent(uint32_t event_code, Mns_Error_t *public_error_code_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CEventHandler */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the event handler class. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Eh_Ctor(CEventHandler *self, uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + /* Save MOST NetServices instance ID */ + self->mns_inst_id = mns_inst_id; + /* Initialize subject for internal events */ + Sub_Ctor(&self->internal_event_subject, self->mns_inst_id); + /* Initialize subject for public error reporting */ + Ssub_Ctor(&self->public_error_subject, self->mns_inst_id); +} + +/*! \brief Adds an observer which reports public errors + * \param self Instance pointer + * \param obs_ptr Reference to an observer + */ +void Eh_AddObsrvPublicError(CEventHandler *self, CSingleObserver *obs_ptr) +{ + (void)Ssub_AddObserver(&self->public_error_subject, obs_ptr); +} + +/*! \brief Removes an observer registered by Eh_AddObsrvPublicError(). + * \param self Instance pointer + */ +void Eh_DelObsrvPublicError(CEventHandler *self) +{ + Ssub_RemoveObserver(&self->public_error_subject); +} + +/*! \brief Reports an event to the event handler. + * \param self Instance pointer + * \param event_code Event code to report + */ +void Eh_ReportEvent(CEventHandler *self, uint32_t event_code) +{ + Mns_Error_t public_error_code; + /* Check if event code exists */ + if((event_code & EH_M_ALL_EVENTS) != 0U) + { + /* Encode internal event code */ + bool result = Eh_EncodeEvent(event_code, &public_error_code); + /* Notify all registered observers */ + Msub_Notify(&self->internal_event_subject, &event_code, event_code); + /* Report error to application? */ + if(result != false) + { + Ssub_Notify(&self->public_error_subject, &public_error_code, false); + } + } +} + +/*! \brief Encodes an internal event code. Some internal event codes are mapped to public + * error codes. + * \param event_code Internal event code to report + * \param public_error_code_ptr Returned public error code + * \return true if error must be reported to the application, otherwise false + */ +static bool Eh_EncodeEvent(uint32_t event_code, Mns_Error_t *public_error_code_ptr) +{ + bool ret_val = true; + + /* Translate internal event code into public error code */ + switch(event_code) + { + case EH_E_BIST_FAILED: + *public_error_code_ptr = MNS_GEN_ERR_INIC; + break; + case EH_E_UNSYNC_COMPLETE: + case EH_E_UNSYNC_FAILED: + *public_error_code_ptr = MNS_GEN_ERR_COMMUNICATION; + break; + default: + ret_val = false; /* Do not report this event to application. */ + break; + } + + return ret_val; +} + +/*! \brief Registers an observer on the given event code. + * \param self Instance pointer + * \param obs_ptr Reference to the masked-observer object + */ +void Eh_AddObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr) +{ + (void)Sub_AddObserver(&self->internal_event_subject, &obs_ptr->parent); +} + +/*! \brief Unregisters the given observer from the given event code. + * \param self Instance pointer + * \param obs_ptr Reference to the masked-observer object + */ +void Eh_DelObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr) +{ + (void)Sub_RemoveObserver(&self->internal_event_subject, &obs_ptr->parent); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_eh.h b/mnsl/mns_eh.h new file mode 100644 index 0000000..2186a2f --- /dev/null +++ b/mnsl/mns_eh.h @@ -0,0 +1,130 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the event handler. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_EH + * @{ + */ + +#ifndef MNS_EH_H +#define MNS_EH_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_eh_pb.h" +#include "mns_obs.h" +#include "mns_trace.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief A control FIFO synchronization is lost. When this event occurs the PMS still waits + * until all FIFOs are unsynchronized. So this event is no termination event. + */ +#define EH_E_SYNC_LOST 0x0001U +/*! \brief INIC Build-In-Self-Test failed + */ +#define EH_E_BIST_FAILED 0x0002U +/*! \brief Notifies completed un-synchronization of Port Message FIFOs + */ +#define EH_E_UNSYNC_COMPLETE 0x0004U +/*! \brief Notifies that the Port Message Channel was not able to un-synchronize its FIFOs + * within a definite time + */ +#define EH_E_UNSYNC_FAILED 0x0008U +/*! \brief MOST NetServices initialization succeeded + */ +#define EH_E_INIT_SUCCEEDED 0x0010U +/*! \brief MOST NetServices initialization failed + */ +#define EH_E_INIT_FAILED 0x0020U + +/*! \brief Mask including all events that lead to the termination of the MNS + */ +#define EH_M_TERMINATION_EVENTS (EH_E_UNSYNC_COMPLETE | EH_E_UNSYNC_FAILED | \ + EH_E_BIST_FAILED | EH_E_INIT_FAILED) + +/*! \brief Bitmask to identify all internal event codes + */ +#define EH_M_ALL_EVENTS (EH_M_TERMINATION_EVENTS | EH_E_INIT_SUCCEEDED | EH_E_SYNC_LOST) + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for callback functions which notifies the event handler + * observers. + * \param self Instance pointer + * \param event_code Reported event code + */ +typedef void (*Ehobs_UpdateCb_t)(void *self, uint32_t event_code); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of the event handler. */ +typedef struct CEventHandler_ +{ + /*! \brief Subject used for internal events */ + CSubject internal_event_subject; + /*! \brief Single subject to report error to application */ + CSingleSubject public_error_subject; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} CEventHandler; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CEventHandler */ +/*------------------------------------------------------------------------------------------------*/ +extern void Eh_Ctor(CEventHandler *self, uint8_t mns_inst_id); +extern void Eh_AddObsrvPublicError(CEventHandler *self, CSingleObserver *obs_ptr); +extern void Eh_DelObsrvPublicError(CEventHandler *self); +extern void Eh_ReportEvent(CEventHandler *self, uint32_t event_code); +extern void Eh_AddObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr); +extern void Eh_DelObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_EH_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_eh_pb.h b/mnsl/mns_eh_pb.h new file mode 100644 index 0000000..422ae15 --- /dev/null +++ b/mnsl/mns_eh_pb.h @@ -0,0 +1,68 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Public header file of the event handler. + */ +/*! + * \addtogroup G_MNS_INIT_AND_SRV_TYPES + * @{ + */ + +#ifndef MNS_EH_PB_H +#define MNS_EH_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MOST NetServices general error codes */ +typedef enum Mns_Error_ +{ + MNS_GEN_ERR_COMMUNICATION = 1, /*!< \brief Fatal communication error between EHC and INIC */ + MNS_GEN_ERR_INIC = 2 /*!< \brief INIC internal error */ + +} Mns_Error_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_EH_PB_H */ + +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_encoder.c b/mnsl/mns_encoder.c new file mode 100644 index 0000000..5153e64 --- /dev/null +++ b/mnsl/mns_encoder.c @@ -0,0 +1,254 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 message encoder + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ENCODER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_encoder.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Constants */ +/*------------------------------------------------------------------------------------------------*/ +#define ENC_LLR_TIME_DEFAULT 11U /*! \brief Default LLR time required to transmit valid messages + * with ContentType 0x81 + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Enc_Encode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]); +static void Enc_Decode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +static void Enc_Encode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]); +static void Enc_Decode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +static void Enc_Encode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]); +static void Enc_Decode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the interface of a specific encoder + * \details Creates all encoder interfaces as singletons + * \param type Specifies the type of encoder to retrieve + * \return The desired interface to the specified encoder + */ +IEncoder *Enc_GetEncoder(Enc_MsgContent_t type) +{ + static IEncoder enc_content_00 = {ENC_CONTENT_00, 8U, 12U, &Enc_Encode_00, &Enc_Decode_00}; + static IEncoder enc_content_80 = {ENC_CONTENT_80, 6U, 11U, &Enc_Encode_80, &Enc_Decode_80}; + static IEncoder enc_content_81 = {ENC_CONTENT_81, 6U, 13U, &Enc_Encode_81, &Enc_Decode_81}; + IEncoder *encoder_ptr = NULL; + + switch (type) + { + case ENC_CONTENT_00: + encoder_ptr = &enc_content_00; + break; + case ENC_CONTENT_80: + encoder_ptr = &enc_content_80; + break; + case ENC_CONTENT_81: + encoder_ptr = &enc_content_81; + break; + default: + encoder_ptr = NULL; + break; + } + + return encoder_ptr; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Content type "00" */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Encodes a message telegram to the "ContentType 0x00" MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Encode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + header[0] = MISC_HB(tel_ptr->source_addr); + header[1] = MISC_LB(tel_ptr->source_addr); + header[2] = MISC_HB(tel_ptr->destination_addr); + header[3] = MISC_LB(tel_ptr->destination_addr); + + header[4] = tel_ptr->id.fblock_id; + header[5] = tel_ptr->id.instance_id; + + header[6] = MISC_HB(tel_ptr->id.function_id); + header[7] = MISC_LB(tel_ptr->id.function_id); + + header[8] = (uint8_t)(tel_ptr->tel.tel_id << 4) | (uint8_t)((uint8_t)tel_ptr->id.op_type & 0xFU); + header[9] = tel_ptr->opts.llrbc; + + header[10] = tel_ptr->tel.tel_cnt; + header[11] = tel_ptr->tel.tel_len; +} + +/*! \brief Decodes a "ContentType 0x00" MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Decode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + tel_ptr->source_addr = (uint16_t)((uint16_t)header[0] << 8) | (uint16_t)header[1]; + tel_ptr->destination_addr = (uint16_t)((uint16_t)header[2] << 8) | (uint16_t)header[3]; + + tel_ptr->id.fblock_id = header[4]; + tel_ptr->id.instance_id = header[5]; + + tel_ptr->id.function_id = (uint16_t)((uint16_t)header[6] << 8) | (uint16_t)header[7]; + + tel_ptr->tel.tel_id = header[8] >> 4; /* high nibble: TelId */ + tel_ptr->id.op_type = (Mns_OpType_t)(header[8] & 0x0FU); /* low nibble: OPType */ + + tel_ptr->opts.llrbc = header[9]; + tel_ptr->tel.tel_cnt = header[10]; + tel_ptr->tel.tel_len = header[11]; + + tel_ptr->tel.tel_data_ptr = &header[12]; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Content type "0x80" */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Encodes a message telegram to the "ContentType 0x80" MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Encode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ /* high nibble: TelId low nibble: OPType */ + header[0] = (uint8_t)(tel_ptr->tel.tel_id << 4) | (uint8_t)((uint8_t)tel_ptr->id.op_type & 0xFU); + header[1] = tel_ptr->tel.tel_cnt; + header[2] = tel_ptr->tel.tel_len; + + header[3] = MISC_HB(tel_ptr->id.function_id); + header[4] = MISC_LB(tel_ptr->id.function_id); + + header[5] = MISC_HB(tel_ptr->source_addr); + header[6] = MISC_LB(tel_ptr->source_addr); + + header[7] = MISC_HB(tel_ptr->destination_addr); + header[8] = MISC_LB(tel_ptr->destination_addr); + + header[9] = tel_ptr->id.fblock_id; + header[10] = tel_ptr->id.instance_id; +} + +/*! \brief Decodes a "ContentType 0x80" MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Decode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + tel_ptr->tel.tel_id = header[0] >> 4; /* high nibble: TelId */ + tel_ptr->id.op_type = (Mns_OpType_t)(header[0] & 0x0FU); /* low nibble: OPType */ + + tel_ptr->tel.tel_cnt = header[1]; + tel_ptr->tel.tel_len = header[2]; + + tel_ptr->id.function_id = (uint16_t)((uint16_t)header[3] << 8) | (uint16_t)header[4]; + + tel_ptr->source_addr = (uint16_t)((uint16_t)header[5] << 8) | (uint16_t)header[6]; + tel_ptr->destination_addr = (uint16_t)((uint16_t)header[7] << 8) | (uint16_t)header[8]; + + tel_ptr->id.fblock_id = header[9]; + tel_ptr->id.instance_id = header[10]; + + tel_ptr->tel.tel_data_ptr = &header[11]; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Content type "0x81" */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Encodes a message telegram to the "ContentType 0x81" MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Encode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + header[0] = tel_ptr->opts.llrbc; + header[1] = ENC_LLR_TIME_DEFAULT; + /* high nibble: TelId low nibble: OPType */ + header[2] = (uint8_t)(tel_ptr->tel.tel_id << 4) | (uint8_t)((uint8_t)tel_ptr->id.op_type & 0xFU); + header[3] = tel_ptr->tel.tel_cnt; + header[4] = tel_ptr->tel.tel_len; + + header[5] = MISC_HB(tel_ptr->id.function_id); + header[6] = MISC_LB(tel_ptr->id.function_id); + + header[7] = MISC_HB(tel_ptr->source_addr); + header[8] = MISC_LB(tel_ptr->source_addr); + + header[9] = MISC_HB(tel_ptr->destination_addr); + header[10] = MISC_LB(tel_ptr->destination_addr); + + header[11] = tel_ptr->id.fblock_id; + header[12] = tel_ptr->id.instance_id; +} + +/*! \brief Decodes a "ContentType 0x81" MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Decode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + tel_ptr->opts.llrbc = header[0]; + + tel_ptr->tel.tel_id = header[2] >> 4; /* high nibble: TelId */ + tel_ptr->id.op_type = (Mns_OpType_t)(header[2] & 0x0FU); /* low nibble: OPType */ + + tel_ptr->tel.tel_cnt = header[3]; + tel_ptr->tel.tel_len = header[4]; + + tel_ptr->id.function_id = (uint16_t)((uint16_t)header[5] << 8) | (uint16_t)header[6]; + + tel_ptr->source_addr = (uint16_t)((uint16_t)header[7] << 8) | (uint16_t)header[8]; + tel_ptr->destination_addr = (uint16_t)((uint16_t)header[9] << 8) | (uint16_t)header[10]; + + tel_ptr->id.fblock_id = header[11]; + tel_ptr->id.instance_id = header[12]; + + tel_ptr->tel.tel_data_ptr = &header[13]; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_encoder.h b/mnsl/mns_encoder.h new file mode 100644 index 0000000..439a622 --- /dev/null +++ b/mnsl/mns_encoder.h @@ -0,0 +1,118 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of message encoder + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ENCODER + * @{ + */ + +#ifndef MNS_ENCODER_H +#define MNS_ENCODER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Defines */ +/*------------------------------------------------------------------------------------------------*/ +#define ENC_MAX_SIZE_CONTENT 16U /*!< \brief Maximum content size in bytes, quadlet aligned */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the size of a MOST message header + * \return The size of the MOST message header in bytes. + */ +typedef uint8_t (*Enc_GetSize_t)(void); + +/*! \brief Retrieves the content type of a MOST message header + * \return The content type of the MOST message header in bytes. + */ +typedef uint8_t (*Enc_GetContType_t)(void); + +/*! \brief Encodes a message telegram to the MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +typedef void (*Enc_Encode_t)(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +/*! \brief Decodes a MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +typedef void (*Enc_Decode_t)(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Identifier for a MOST Message Content */ +typedef enum Enc_MsgContent_ +{ + ENC_CONTENT_00 = 0x00, /*!< \brief Content Type "0x00": Uncompressed, excluding retry values */ + ENC_CONTENT_80 = 0x80, /*!< \brief Content Type "0x80": Compressed, excluding retry values */ + ENC_CONTENT_81 = 0x81 /*!< \brief Content Type "0x81": Compressed, including retry values */ + +} Enc_MsgContent_t; + +/*! \brief Interface for message encoder */ +typedef struct IEncoder_ +{ + Enc_MsgContent_t content_type; /*!< \brief Retrieves the content type of the MOST message header */ + uint8_t pm_hdr_sz; /*!< \brief Retrieves the size of the Port Message header */ + uint8_t msg_hdr_sz; /*!< \brief Retrieves the size of the MOST message header */ + Enc_Encode_t encode_fptr; /*!< \brief Function required to encode a MOST message header */ + Enc_Decode_t decode_fptr; /*!< \brief Function required to decode a MOST message header */ + +} IEncoder; + +/*------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern IEncoder *Enc_GetEncoder(Enc_MsgContent_t type); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_ENCODER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_lld_pb.h b/mnsl/mns_lld_pb.h new file mode 100644 index 0000000..d1a087b --- /dev/null +++ b/mnsl/mns_lld_pb.h @@ -0,0 +1,221 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of the low-level driver interface + * \defgroup G_LLD Low-Level Driver + * \brief API functions to be used by the low-level driver. + * \details The MOST NetServices provides a certain set of functions which are only dedicated to the low-level driver. + * The low-level driver \em API is a set of functions which shall be used by the low-level driver. + * The low-level driver \em callbacks is a set of function that shall be implemented by the low-level driver. + * The low-level driver \em callbacks shall be assigned to the MOST NetServices initialization structure. + * During initialization MOST NetServices invokes the callback \ref Mns_Lld_Callbacks_t "start_fptr" and + * passes the low-level driver \em API as pointer to \ref Mns_Lld_Api_t. + * \mns_ic_started{ See also Getting Started with \ref P_UM_STARTED_LLD. } + * \mns_ic_examples{ See also <i>Examples</i>, section \ref P_UM_EXAMPLE_LLD_01, \ref P_UM_EXAMPLE_LLD_02 and \ref P_UM_EXAMPLE_LLD_03. } + * \ingroup G_MNS_API + * \defgroup G_LLD_TYPES Referred Types + * \brief Referred types used by the low-level driver interface + * \ingroup G_LLD + * \defgroup G_LLD_API Low-Level Driver API + * \brief Function pointers to be used by the low-level driver + * \ingroup G_LLD + */ + +#ifndef MNS_LLD_PB_H +#define MNS_LLD_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mns_memory_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * \addtogroup G_LLD_TYPES + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Tx message object providing the raw port message byte stream */ +typedef struct Mns_Lld_TxMsg_ +{ + struct Mns_Lld_TxMsg_ *custom_next_msg_ptr;/*!< \brief Shall be used by the LLD implementation to queue messages for + * asynchronous transmission + * \details MOST NetServices will set this value to \c NULL since only + * single messages are forwarded to the LLD. Within the transmit function + * it is recommended that the LLD queues the message for asynchronous + * transmission. Despite a driver's transmit function might signal busy for + * a short term MOST NetServices might forward multiple messages for + * transmission. If a driver works asynchronously (interrupt driven) it + * can easily use this pointer build a queue of waiting messages. + * Nonetheless, it is important that \ref Mns_Lld_Api_t::tx_release_fptr + * "tx_release_fptr" is invoked for every message separately. The Interface + * between MOST NetServices and the LLD does only support single messages. + */ + Mns_Mem_Buffer_t *memory_ptr; /*!< \brief Points to the data buffer */ + +} Mns_Lld_TxMsg_t; + +/*! \brief Rx message object pointing to the raw port message byte stream. */ +typedef struct Mns_Lld_RxMsg_ +{ + uint8_t* data_ptr; /*!< \brief Points to a MOST NetServices allocated memory chunk. */ + uint16_t data_size; /*!< \brief Size of the memory chunk in bytes. Valid values: 6..72. */ + +} Mns_Lld_RxMsg_t; + +/*! + * @} + * \addtogroup G_LLD_API + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Low-level driver API */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an Rx message object + * \param inst_ptr Reference to internal MOST NetServices handler + * \param buffer_size The size in bytes of the received Rx message. + * Valid values: 6..72. + * \return The Rx message object or \c NULL if no message object is available. In the latter + * case the low-level driver can wait until Mns_Lld_RxMsgAvailableCb_t() is invoked. + * The low-level driver is allowed to pre-allocate Rx messages with the maximum size + * of 72 bytes. After writing received data into Mns_Lld_RxMsg_t::data_ptr the + * low-level driver must set Mns_Lld_RxMsg_t::data_size to the actual message size. + * \warning + * The function will also return \c NULL if the requested \c buffer_size exceeds the valid range. + * In such a case the MOST NetServices cannot guarantee that Mns_Lld_RxMsgAvailableCb_t() is + * called as expected. Received messages exceeding the valid range must be discarded by the LLD. + */ +typedef Mns_Lld_RxMsg_t* (*Mns_Lld_RxAllocateCb_t)(void *inst_ptr, uint16_t buffer_size); + +/*! \brief Frees an unused Rx message object + * \param inst_ptr Reference to internal MOST NetServices handler + * \param msg_ptr Reference to the unused Rx message object + */ +typedef void (*Mns_Lld_RxFreeUnusedCb_t)(void *inst_ptr, Mns_Lld_RxMsg_t *msg_ptr); + +/*! \brief Pass an Rx message to MOST NetServices + * \param inst_ptr Reference to internal MOST NetServices handler + * \param msg_ptr Reference to the Rx message object containing the received + * message. + */ +typedef void (*Mns_Lld_RxReceiveCb_t)(void *inst_ptr, Mns_Lld_RxMsg_t *msg_ptr); + +/*! \brief Notifies that the LLD no longer needs to access the Tx message object + * \param inst_ptr Reference to internal MOST NetServices handler + * \param msg_ptr Reference to the Tx message object which is no longer accessed + * by the low-level driver + */ +typedef void (*Mns_Lld_TxReleaseCb_t)(void *inst_ptr, Mns_Lld_TxMsg_t *msg_ptr); + +/*! \brief Initialization required for one communication channel (control or packet) + */ +typedef struct Mns_Lld_Api_ +{ + Mns_Lld_RxAllocateCb_t rx_allocate_fptr; /*!< \brief Allocates an Rx message object */ + Mns_Lld_RxFreeUnusedCb_t rx_free_unused_fptr; /*!< \brief Frees an unused Rx message object */ + Mns_Lld_RxReceiveCb_t rx_receive_fptr; /*!< \brief Pass an Rx message to MOST NetServices */ + Mns_Lld_TxReleaseCb_t tx_release_fptr; /*!< \brief Notifies that the LLD no longer needs to access the Tx message object */ + +} Mns_Lld_Api_t; + +/*! + * @} + * \addtogroup G_LLD + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* LLD interface functions */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Notifies the LLD to start transmitting and receiving messages + * \param api_ptr Reference to MOST NetServices LLD interface + * \param ns_ptr Reference to internal MOST NetServices handler + */ +typedef void (*Mns_Lld_StartCb_t)(Mns_Lld_Api_t* api_ptr, void *ns_ptr, void *inst_ptr); + +/*! \brief Notifies the LLD to stop/abort transmitting and receiving messages + * \details As soon as this function is called the low-level driver is not allowed + * to call any MOST NetServices function. + */ +typedef void (*Mns_Lld_StopCb_t)(void *inst_ptr); + +/*! \brief Notifies the LLD to reset the INIC + * \details If this function is called the low-level driver is responsible to + * perform an INIC hardware reset. + */ +typedef void (*Mns_Lld_ResetInicCb_t)(void *inst_ptr); + +/*! \brief Callback function which is invoked as soon as port message objects are available again. + * \details By implementing this callback function the low-level driver can avoid polling for + * Rx message objects. The low-level driver should wait for the function call as soon + * as Mns_Lld_RxAllocateCb_t() returns NULL. Only then it shall call those functions again. + */ +typedef void (*Mns_Lld_RxMsgAvailableCb_t)(void *inst_ptr); + +/*! \brief Callback function which is invoked to transmit a single message to the INIC + * \param msg_ptr Reference to a single Tx message. + */ +typedef void (*Mns_Lld_TxTransmitCb_t)(Mns_Lld_TxMsg_t *msg_ptr, void *inst_ptr); + +/*! + * @} + * \addtogroup G_LLD_TYPES + * @{ + */ + +/*! \brief Set of functions implemented by the low-level driver + */ +typedef struct Mns_Lld_Callbacks_ +{ + Mns_Lld_StartCb_t start_fptr; /*!< \brief Callback function to initialize the low-level driver and + * start the transmission and reception of messages */ + Mns_Lld_StopCb_t stop_fptr; /*!< \brief Callback function to stop/abort the transmission and reception of messages */ + Mns_Lld_RxMsgAvailableCb_t rx_available_fptr; /*!< \brief Callback function which is invoked as soon as Rx message objects are available again */ + Mns_Lld_TxTransmitCb_t tx_transmit_fptr; /*!< \brief Callback function to transmit one or multiple messages to the INIC */ + +} Mns_Lld_Callbacks_t; + +/*! @} */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_LLD_PB_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_lldpool.c b/mnsl/mns_lldpool.c new file mode 100644 index 0000000..ae9f33d --- /dev/null +++ b/mnsl/mns_lldpool.c @@ -0,0 +1,101 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 LLD Message Pool + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_lldpool.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Returns an unused LLD Tx message object back to the pool + * \param self The instance + * \param owner_ptr Assigns messages to the respective FIFO + * \param mns_inst_id MOST NetServices instance ID + */ +void Lldp_Ctor(CLldPool *self, void *owner_ptr, uint8_t mns_inst_id) +{ + uint8_t cnt; + MISC_MEM_SET(self, 0, sizeof(*self)); + + Dl_Ctor(&self->list, mns_inst_id); + + for (cnt = 0U; cnt < LLDP_NUM_HANDLES; cnt++) /* setup LLD Tx handles */ + { + TR_ASSERT(mns_inst_id, "[FIFO]", (NULL == self->messages[cnt].msg_ptr)); + Dln_Ctor(&self->messages[cnt].node, &self->messages[cnt]); + self->messages[cnt].owner_ptr = owner_ptr; + Dl_InsertTail(&self->list, &self->messages[cnt].node); + } +} + +/*! \brief Returns an unused LLD Tx message object back to the pool + * \param self The instance + * \param msg_ptr The unused LLD Tx message object + */ +void Lldp_ReturnTxToPool(CLldPool *self, Lld_IntTxMsg_t *msg_ptr) +{ + Dl_InsertTail(&self->list, &msg_ptr->node); +} + +/*! \brief Allocates an unused LLD Tx message object from the pool + * \param self The instance + * \return An internal LLD Tx message object or \c NULL if no message object is + * available. + */ +Lld_IntTxMsg_t* Lldp_GetTxFromPool(CLldPool *self) +{ + CDlNode *node_ptr = NULL; + Lld_IntTxMsg_t *handle_ptr = NULL; + + node_ptr = Dl_PopHead(&self->list); + + if (node_ptr != NULL) + { + handle_ptr = (Lld_IntTxMsg_t*)Dln_GetData(node_ptr); + } + + return handle_ptr; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_lldpool.h b/mnsl/mns_lldpool.h new file mode 100644 index 0000000..fdf4419 --- /dev/null +++ b/mnsl/mns_lldpool.h @@ -0,0 +1,112 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of LLD Message Pool + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +#ifndef MNS_LLDPOOL_H +#define MNS_LLDPOOL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_base.h" +#include "mns_lld_pb.h" +#include "mns_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Number of LLD Tx handles dedicated to each FIFO */ +#define LLDP_NUM_HANDLES 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Internal LLD Tx message */ +typedef struct Lld_IntTxMsg_ +{ + Mns_Lld_TxMsg_t lld_msg; /*!< \brief Contains the public LLD Tx message + * \details This attribute needs to be the first one in this structure + */ + CDlNode node; /*!< \brief Node required for queuing */ + CMessage *msg_ptr; /*!< \brief Reference to the associated common message object, or + * \c NULL if the object is a command */ + void *owner_ptr; /*!< \brief Points to the FIFO which owns the message object + * or NULL if the object is a command */ + +} Lld_IntTxMsg_t; + +/*! \brief Internal LLD Rx message */ +typedef struct Lld_IntRxMsg_ +{ + Mns_Lld_RxMsg_t lld_msg; /*!< \brief Contains the public LLD Rx message + * \details This attribute needs to be the first one in this structure + */ + CMessage *msg_ptr; /*!< \brief Reference to the associated common message object*/ + +} Lld_IntRxMsg_t; + +/*! \brief The class CLldPool*/ +typedef struct CLldPool_ +{ + CDlList list; /*!< \brief Points to the first available message in Tx pool */ + Lld_IntTxMsg_t messages[LLDP_NUM_HANDLES];/*!< \brief Available messages in Tx pool */ + +} CLldPool; + +/*------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Lldp_Ctor(CLldPool *self, void *owner_ptr, uint8_t mns_inst_id); +extern void Lldp_ReturnTxToPool(CLldPool *self, Lld_IntTxMsg_t *msg_ptr); +extern Lld_IntTxMsg_t* Lldp_GetTxFromPool(CLldPool *self); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_LLDPOOL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_memory.h b/mnsl/mns_memory.h new file mode 100644 index 0000000..5b80bf1 --- /dev/null +++ b/mnsl/mns_memory.h @@ -0,0 +1,112 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of internal memory buffer + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MEMORY + * @{ + */ + +#ifndef MNS_MEMORY_H +#define MNS_MEMORY_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_memory_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* IAllocator Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function which frees memory + * \param allocator Reference to the Mem_Allocator_t object + * \param mem_ptr Reference to memory chunk + * \param mem_info_ptr Customer specific information needed to free + * the related memory chunk + */ +typedef void (*Mem_Free_t)(void *allocator, void* mem_ptr, void* mem_info_ptr); + +/*! \brief Callback function which allocated memory + * \param allocator Reference to the Mem_Allocator_t object + * \param size Size of the demanded memory chunk + * \param mem_info_ptr Customer specific information needed to free + * the related memory chunk + * \return Reference to a memory chunk with a minimum size of \c size. + * Otherwise NULL. + */ +typedef void* (*Mem_Allocate_t)(void *allocator, uint16_t size, void** mem_info_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Interface IAllocator */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Interface which is needed to be implemented by a memory allocator */ +typedef struct IAllocator_ +{ + void* base; /*!< Reference to the base class */ + Mem_Allocate_t allocate_fptr; /*!< Callback function required to allocate memory */ + Mem_Free_t free_fptr; /*!< Callback function required to free memory */ + +} IAllocator; + + +/*------------------------------------------------------------------------------------------------*/ +/* Memory buffer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Memory chunk comprising non public fields */ +typedef struct Mem_IntBuffer_ +{ + Mns_Mem_Buffer_t public_buffer; /*!< \brief Public attributes of memory buffer + * \details This has to be the first member in this + * struct + */ + IAllocator *allocator_ptr; /*!< \brief Reference to the allocator which is + * required to free the memory chunk + */ + void *mem_info_ptr; /*!< \brief Customer specific information needed to + * free the related memory chunk + */ +} Mem_IntBuffer_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MEMORY_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_memory_pb.h b/mnsl/mns_memory_pb.h new file mode 100644 index 0000000..2165e73 --- /dev/null +++ b/mnsl/mns_memory_pb.h @@ -0,0 +1,72 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of memory buffer and memory allocator + */ +/*! + * \addtogroup G_LLD_TYPES + * @{ + */ + +#ifndef MNS_MEMORY_PB_H +#define MNS_MEMORY_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Memory buffer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Memory chunk representing a message or part of a message. */ +typedef struct Mns_Mem_Buffer_ +{ + struct Mns_Mem_Buffer_ *next_buffer_ptr; /*!< \brief Points to an additional memory buffer + * that belongs to the same message. + */ + uint8_t *data_ptr; /*!< \brief Points to the data buffer */ + uint16_t data_size; /*!< \brief Size of the data buffer */ + uint16_t total_size; /*!< \brief Reserved for future use. Size of this and all concatenated data buffers */ + +} Mns_Mem_Buffer_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MEMORY_PB_H */ + +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_message.c b/mnsl/mns_message.c new file mode 100644 index 0000000..33a0673 --- /dev/null +++ b/mnsl/mns_message.c @@ -0,0 +1,331 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 class message + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MESSAGE + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of common MOST message class + * \param self The instance + */ +void Msg_Ctor(CMessage *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + Dln_Ctor(&self->node, self); + + self->rsvd_memory.allocator_ptr = NULL; + self->rsvd_memory.mem_info_ptr = NULL; + self->rsvd_memory.public_buffer.next_buffer_ptr = NULL; + self->rsvd_memory.public_buffer.data_ptr = &self->rsvd_buffer[0]; + self->rsvd_memory.public_buffer.data_size = MSG_SIZE_RSVD_BUFFER; + self->rsvd_memory.public_buffer.total_size = MSG_SIZE_RSVD_BUFFER; + + self->start_ptr = &self->rsvd_buffer[0]; + self->pb_msg.tel.tel_data_ptr = &self->rsvd_buffer[0]; +/* self->pb_msg.tel.tel_id = 0U; + self->pb_msg.tel.tel_cnt = 0U; + self->pb_msg.tel.tel_len = 0U; */ + + self->pb_msg.opts.llrbc = MSG_LLRBC_DEFAULT; + +/* self->header_rsvd_sz = 0U; + self->header_curr_idx = 0U; + self->header_curr_sz = 0U; + self->ref_ptr = NULL; */ +} + +/*! \brief Prepares the message for re-usage + * \details In future this function has to take care that external memory + * has to be reinitialize properly. + * \param self The instance + */ +void Msg_Cleanup(CMessage *self) +{ + void *handle = self->lld_handle_ptr; /* restore associated LLD message object */ + void *pool_ptr = self->pool_ptr; /* restore associated pool reference */ + + Msg_Ctor(self); /* simply call constructor now */ + + self->lld_handle_ptr = handle; + self->pool_ptr = pool_ptr; +} + +/*! \brief Adds external message payload to the message + * \details The internally reserved message payload is no longer in in use. + * \param self The instance + * \param payload_ptr Pointer to externally allocated payload + * \param payload_sz Size of externally allocated payload + * \param mem_info_ptr Reference to additional memory information + */ +void Msg_SetExtPayload(CMessage *self, uint8_t *payload_ptr, uint8_t payload_sz, void* mem_info_ptr) +{ + self->pb_msg.tel.tel_data_ptr = payload_ptr; + self->pb_msg.tel.tel_len = payload_sz; + + self->ext_memory.allocator_ptr = NULL; + self->ext_memory.mem_info_ptr = mem_info_ptr; + self->ext_memory.public_buffer.data_ptr = payload_ptr; + self->ext_memory.public_buffer.data_size = payload_sz; + self->ext_memory.public_buffer.total_size = payload_sz; + self->ext_memory.public_buffer.next_buffer_ptr = NULL; +} + +/*! \brief Initially defines a header space in front of the data body + * \details Ensure that \c start_ptr is assigned correctly before calling + * this functions. + * \param self The instance + * \param header_sz Size of the header + */ +void Msg_ReserveHeader(CMessage *self, uint8_t header_sz) +{ + /* self->start_ptr stays */ + self->header_rsvd_sz = header_sz; + self->header_curr_idx = header_sz; + self->header_curr_sz = 0U; + + self->pb_msg.tel.tel_data_ptr = &self->start_ptr[header_sz]; +} + +/*! \brief Adds a defined header space in front of the current header + * \param self The instance + * \param header_sz Size of the header + */ +void Msg_PullHeader(CMessage *self, uint8_t header_sz) +{ +/* MNS_ASSERT(header_sz <= self->curr_header_sz); */ + +/* self->pb_msg.tel.tel_data_ptr = &self->rsvd_buffer[MSG_SIZE_RSVD_HEADER];*/ + self->header_curr_idx -= header_sz; + self->header_curr_sz += header_sz; +} + +/*! \brief Undoes a message header of a defined size + * \param self The instance + * \param header_sz Size of the header + */ +void Msg_PushHeader(CMessage *self, uint8_t header_sz) +{ + self->header_curr_idx += header_sz; + self->header_curr_sz -= header_sz; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Class Properties (get/set) */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the reference to the containing MOST Telegrams structure + * \param self The instance + * \return Pointer to the internal MOST Telegram structure + */ +Msg_MostTel_t* Msg_GetMostTel(CMessage *self) +{ + return &self->pb_msg; +} + +/*! \brief Retrieves the start of the current message header + * \param self The instance + * \return Pointer to the current header start + */ +uint8_t* Msg_GetHeader(CMessage *self) +{ + return &(self->rsvd_buffer[self->header_curr_idx]); +} + +/*! \brief Retrieves the size of the current message header + * \param self The instance + * \return Size of the current header in bytes + */ +uint8_t Msg_GetHeaderSize(CMessage * self) +{ + return (self->header_curr_sz); +} + +/*! \brief Retrieves the message buffer as memory structure + * \param self The instance + * \return Reference to the message memory structure + */ +Mns_Mem_Buffer_t* Msg_GetMemTx(CMessage *self) +{ + self->rsvd_memory.public_buffer.data_ptr = &(self->rsvd_buffer[self->header_curr_idx]); + + if (self->ext_memory.public_buffer.data_size == 0U) + { + self->rsvd_memory.public_buffer.next_buffer_ptr = NULL; + self->rsvd_memory.public_buffer.data_size = (uint16_t)self->header_curr_sz + (uint16_t)self->pb_msg.tel.tel_len; + self->rsvd_memory.public_buffer.total_size = (uint16_t)self->header_curr_sz + (uint16_t)self->pb_msg.tel.tel_len; + } + else + { + self->rsvd_memory.public_buffer.next_buffer_ptr = &self->ext_memory.public_buffer; + self->rsvd_memory.public_buffer.data_size = (uint16_t)self->header_curr_sz; /* only header is enclosed */ + self->rsvd_memory.public_buffer.total_size = self->rsvd_memory.public_buffer.data_size + + self->ext_memory.public_buffer.data_size; + } + + return &self->rsvd_memory.public_buffer; +} + +/*! \brief Assigns a message status handler which is called as soon as the message is processed + * \param self The instance + * \param callback_fptr Reference to the status callback function + * \param inst_ptr The instance which implements the status callback + */ +void Msg_SetTxStatusHandler(CMessage *self, Msg_TxStatusCb_t callback_fptr, void *inst_ptr) +{ + self->tx_status_inst = inst_ptr; + self->tx_status_fptr = callback_fptr; +} + +/*! \brief Marks the message as occupied by the LLD + * \param self The instance + * \param active Set to \c true if the message is occupied by the LLD, otherwise \c false. + */ +void Msg_SetTxActive(CMessage *self, bool active) +{ + self->tx_active = active; +} + +/*! \brief Checks if the message as occupied by the LLD + * \param self The instance + * \return Returns \c true if the message is occupied by the LLD, otherwise \c false. + */ +bool Msg_IsTxActive(CMessage *self) +{ + return self->tx_active; +} + +/*! \brief Marks the message as bypass message + * \param self The instance + * \param bypass Set to \c true if the message is supposed to be a bypass message, otherwise \c false. + */ +void Msg_SetTxBypass(CMessage *self, bool bypass) +{ + self->tx_bypass = bypass; +} + +/*! \brief Checks if the message is marked as bypass message + * \param self The instance + * \return Returns \c true if the message is marked as bypass message, otherwise \c false. + */ +bool Msg_IsTxBypass(CMessage *self) +{ + return self->tx_bypass; +} + +/*! \brief Fires a status notification for the message object + * \param self The instance + * \param status The transmission status + */ +void Msg_NotifyTxStatus(CMessage *self, Mns_MsgTxStatus_t status) +{ + if (self->tx_status_fptr != NULL) + { + self->tx_status_fptr(self->tx_status_inst, &self->pb_msg, status); + } +} + +/*! \brief Assigns a low-level driver message + * \param self The instance + * \param handle The reference to a low-level driver message object (Tx or Rx) + */ +void Msg_SetLldHandle(CMessage *self, void *handle) +{ + self->lld_handle_ptr = handle; +} + +/*! \brief Retrieves the reference to a low-level driver message + * \param self The instance + * \return The reference to a low-level driver message object or \c NULL + * if no message is assigned. + */ +void *Msg_GetLldHandle(CMessage *self) +{ + return self->lld_handle_ptr; +} + +/*! \brief Assigns a reference for the owning pool + * \param self The instance + * \param pool_ptr The reference to the owning pool + */ +void Msg_SetPoolReference(CMessage *self, void *pool_ptr) +{ + self->pool_ptr = pool_ptr; +} + +/*! \brief Retrieves a reference for the owning pool + * \param self The instance + * \return The reference to the owning pool or \c NULL + * if no pool is assigned. + */ +void *Msg_GetPoolReference(CMessage *self) +{ + return self->pool_ptr; +} + +/*! \brief Retrieves the reference to the internal node member + * \param self The instance + * \return The reference the internal list node + */ +CDlNode *Msg_GetNode(CMessage *self) +{ + return &self->node; +} + +/*! \brief Performs checks on length payload length + * \param self The instance + * \return Returns \c true if the verification succeeded. Otherwise \c false. + */ +bool Msg_VerifyContent(CMessage *self) +{ + bool success = (self->pb_msg.tel.tel_len <= MSG_MAX_SIZE_PAYLOAD) ? true : false; + + return success; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_message.h b/mnsl/mns_message.h new file mode 100644 index 0000000..69d82ea --- /dev/null +++ b/mnsl/mns_message.h @@ -0,0 +1,238 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of class message + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MESSAGE + * @{ + */ + +#ifndef MNS_MESSAGE_H +#define MNS_MESSAGE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_memory.h" +#include "mns_dl.h" +#include "mns_message_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Common macros */ +/*------------------------------------------------------------------------------------------------*/ +#define MSG_ADDR_INVALID 0U /*!< \brief The (source) address the INIC uses to declare an invalid source address. + * \details Invalid source addresses can be: + * - invalid messages from MOST: source_address = [0x0000..0x000F] + * - invalid messages from EHC: source_address != [0x0002, 0x0003] + * . + */ +#define MSG_ADDR_INIC 1U /*!< \brief The address of the local INIC */ +#define MSG_ADDR_EHC_CFG 2U /*!< \brief The address of the EHC configuration interface (ICM and RCM FIFO) */ +#define MSG_ADDR_EHC_APP 3U /*!< \brief The address of the EHC application interface (MCM FIFO) */ + +#define MSG_LLRBC_DEFAULT 10U /*!< \brief The default LowLevelRetry BlockCount */ +#define MSG_LLRBC_MAX 100U/*!< \brief The maximum LowLevelRetry BlockCount */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MOST message id "FBlockID.InstID.FktID.OPType" */ +typedef struct Msg_MsgId_ +{ + uint8_t fblock_id; /*!< \brief FBlockID */ + uint8_t instance_id; /*!< \brief InstID */ + uint16_t function_id; /*!< \brief FktID */ + Mns_OpType_t op_type; /*!< \brief Operation type */ + +} Msg_MsgId_t; + +/*! \brief Retry options */ +typedef struct Msg_TxOptions_ +{ + uint8_t llrbc; /*!< \brief Low-level retry block count performed by the INIC. + * \details The LLRBC are applicable for MCMs. ICMs don't care. + * Values exceeding the maximum value are be corrected + * by the INIC silently to the maximum value. + * Valid range: 0..100 + */ + uint8_t cancel_id; /*!< \brief Either "0" or label for a group of dependent telegrams. + * \details The value determines the required action if the transmission + * has failed. + * Valid range: + * - 0: Only the failed telegram will is removed from the FIFO. + * - 1..255: All telegrams with the same cancel_id as a failed telegram + * will be removed from the FIFO queue. + */ + +} Msg_TxOptions_t; + +/*! \brief Most telegram data */ +typedef struct Msg_TelData_ +{ + uint8_t tel_id; /*!< \brief Telegram id which indicates the telegram as part of + * segmented message or as single transfer. */ + uint8_t tel_len; /*!< \brief The telegram length. + * I.e. the number of telegram bytes starting at address + * which is referred in \c tel_data_ptr. The INIC will add + * \em one in case of \"tel_id = 1..3\". + */ + uint8_t tel_cnt; /*!< \brief The message count indexing the telegram within a segmented + * message. + * The respective tel_cnt is moved by the INIC to \"DATA[0]\" + * in case of \"tel_id = 1..3\". Otherwise it is ignored. + */ + uint8_t *tel_data_ptr; /*!< \brief Points to telegram data. */ + +} Msg_TelData_t; + +/*! \brief Common MOST message */ +typedef struct Msg_MostTel_ +{ + uint16_t destination_addr; /*!< \brief MOST destination address */ + uint16_t source_addr; /*!< \brief MOST source address */ + + Msg_MsgId_t id; /*!< \brief MOST message id "FBlockID.InstID.FktID.OPType" */ + Msg_TxOptions_t opts; /*!< \brief Message transmission options */ + Msg_TelData_t tel; /*!< \brief MOST telegram data */ + void *info_ptr; /*!< \brief Possible reference to additional data */ + +} Msg_MostTel_t; + +/* necessary forward declaration */ +struct CMessage_; +/*! \brief Common message class which provides MOST style message addressing */ +typedef struct CMessage_ CMessage; + +/*! \brief Assignable function which is invoked as soon as transmission + * of the message object is finished. + * \param self The instance + * \param msg_ptr Reference to the message object + * \param status Transmission status + */ +typedef void (*Msg_TxStatusCb_t)(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Size in bytes of reserved message header */ +#define MSG_SIZE_RSVD_HEADER 24U +/*! \brief Size in bytes of message payload */ +#define MSG_MAX_SIZE_PAYLOAD 45U +/*! \brief Size in bytes of pre-allocated message buffer + * \details Size = 24(header) + 45(payload) + 3(stuffing) = 72 */ +#define MSG_SIZE_RSVD_BUFFER 72U + +/*------------------------------------------------------------------------------------------------*/ +/* Class CMessage */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CMessage + * \details Common internal message class which embeds the public message attributes + */ +struct CMessage_ +{ + Msg_MostTel_t pb_msg; /*!< \brief Public part which defines the MOST telegram + * structure. This attribute must be the first + * element inside the message structure. + */ + uint8_t rsvd_buffer[MSG_SIZE_RSVD_BUFFER]; /*!< \brief Reserved memory space */ + Mem_IntBuffer_t rsvd_memory; /*!< \brief Reserved memory which is needed at least for the + * Port message header (24 bytes) */ + Mem_IntBuffer_t ext_memory; /*!< \brief Possible user memory */ + + uint8_t *start_ptr; /*!< \brief Points to the start of the message buffer */ + uint8_t header_curr_idx; /*!< \brief Index of the end of the current header */ + uint8_t header_curr_sz; /*!< \brief Current size of header in bytes */ + uint8_t header_rsvd_sz; /*!< \brief Reserved size of header in bytes */ + + void *pool_ptr; /*!< \brief Point to the pool the message is allocated from and released to */ + void *lld_handle_ptr; /*!< \brief Possible reference to another message object */ + CDlNode node; /*!< \brief Node for usage in a doubly linked list */ + + Msg_TxStatusCb_t tx_status_fptr; /*!< \brief Pointer to Tx status callback */ + void *tx_status_inst; /*!< \brief Reference to instance which needs Tx status notification */ + + bool tx_active; /*!< \brief Is \c true if the object is occupied by the LLD, otherwise \c false */ + bool tx_bypass; /*!< \brief Is \c true if a message was queued as bypass message */ +}; + + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Msg_Ctor(CMessage *self); +extern void Msg_Cleanup(CMessage *self); + +extern void Msg_ReserveHeader(CMessage *self, uint8_t header_sz); +extern void Msg_PullHeader(CMessage *self, uint8_t header_sz); +extern void Msg_PushHeader(CMessage *self, uint8_t header_sz); + +extern void Msg_NotifyTxStatus(CMessage *self, Mns_MsgTxStatus_t status); + +/*------------------------------------------------------------------------------------------------*/ +/* Properties */ +/*------------------------------------------------------------------------------------------------*/ +extern Msg_MostTel_t* Msg_GetMostTel(CMessage *self); + +extern uint8_t* Msg_GetHeader(CMessage *self); +extern uint8_t Msg_GetHeaderSize(CMessage *self); +extern Mns_Mem_Buffer_t* Msg_GetMemTx(CMessage *self); + +extern void Msg_SetLldHandle(CMessage *self, void *handle); +extern void *Msg_GetLldHandle(CMessage *self); +extern void Msg_SetPoolReference(CMessage *self, void *pool_ptr); +extern void *Msg_GetPoolReference(CMessage *self); + +extern CDlNode *Msg_GetNode(CMessage *self); + +extern void Msg_SetTxStatusHandler(CMessage *self, Msg_TxStatusCb_t callback_fptr, void *inst_ptr); +extern void Msg_SetExtPayload(CMessage *self, uint8_t *payload_ptr, uint8_t payload_sz, void* mem_info_ptr); +extern void Msg_SetTxActive(CMessage *self, bool active); +extern bool Msg_IsTxActive(CMessage *self); +extern void Msg_SetTxBypass(CMessage *self, bool bypass); +extern bool Msg_IsTxBypass(CMessage *self); + +extern bool Msg_VerifyContent(CMessage *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MESSAGE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_message_pb.h b/mnsl/mns_message_pb.h new file mode 100644 index 0000000..8656519 --- /dev/null +++ b/mnsl/mns_message_pb.h @@ -0,0 +1,121 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of public message types + */ + +#ifndef MNS_MESSAGE_PB_H +#define MNS_MESSAGE_PB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * \addtogroup G_MNS_AMS + * @{ + */ +/*------------------------------------------------------------------------------------------------*/ +/* Defines */ +/*------------------------------------------------------------------------------------------------*/ +#define MNS_ADDR_INTERNAL 0x0000U /*!< \brief Internal transmission destination address + * \details Can be used for internal message transmission + * to avoid possible race conditions during + * recalculation of the own node address. + */ +#define MNS_ADDR_BROADCAST_BLOCKING 0x03C8U /*!< \brief Blocking broadcast destination address */ +#define MNS_ADDR_BROADCAST_UNBLOCKING 0x03FFU /*!< \brief Unblocking broadcast destination address */ +#define MNS_ADDR_DEBUG 0x0FF0U /*!< \brief Optional debug destination address */ +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Message transmission status for internal/debug use + * \ingroup G_MNS_MISC_RET_RES + */ +typedef enum Mns_MsgTxStatus_ +{ + MNS_MSG_STAT_OK = 0x00U, /*!< \brief Transmission succeeded */ + MNS_MSG_STAT_ERROR_CFG_NO_RCVR = 0x01U, /*!< \brief No internal receiver exists */ + MNS_MSG_STAT_ERROR_BF = 0x08U, /*!< \brief Buffer full */ + MNS_MSG_STAT_ERROR_CRC = 0x09U, /*!< \brief CRC */ + MNS_MSG_STAT_ERROR_ID = 0x0AU, /*!< \brief Corrupted identifiers */ + MNS_MSG_STAT_ERROR_ACK = 0x0BU, /*!< \brief Corrupted PACK or CACK */ + MNS_MSG_STAT_ERROR_TIMEOUT = 0x0CU, /*!< \brief TX timeout */ + MNS_MSG_STAT_ERROR_FATAL_WT = 0x10U, /*!< \brief Wrong target */ + MNS_MSG_STAT_ERROR_FATAL_OA = 0x11U, /*!< \brief Own node address */ + MNS_MSG_STAT_ERROR_NA_TRANS = 0x18U, /*!< \brief Control channel was switched off and + * a pending transmission was canceled */ + MNS_MSG_STAT_ERROR_NA_OFF = 0x19U, /*!< \brief Control channel not available */ + MNS_MSG_STAT_ERROR_UNKNOWN = 0xFEU, /*!< \brief Unknown error status */ + MNS_MSG_STAT_ERROR_SYNC = 0xFFU /*!< \brief Internal error which is notified if + * communication link with INIC is lost + */ +} Mns_MsgTxStatus_t; + +/*! \brief Operation Types + * \ingroup G_MNS_AMS_TYPES + */ +typedef enum Mns_OpType_ +{ + MNS_OP_SET = 0x0, /*!< \brief Operation Set (Property) */ + MNS_OP_GET = 0x1, /*!< \brief Operation Get (Property) */ + MNS_OP_SETGET = 0x2, /*!< \brief Operation SetGet (Property) */ + MNS_OP_INC = 0x3, /*!< \brief Operation Increment (Property) */ + MNS_OP_DEC = 0x4, /*!< \brief Operation Decrement (Property) */ + MNS_OP_STATUS = 0xC, /*!< \brief Operation Status (Property) */ + + MNS_OP_START = 0x0, /*!< \brief Operation Start (Method) */ + MNS_OP_ABORT = 0x1, /*!< \brief Operation Abort (Method) */ + MNS_OP_STARTRESULT = 0x2, /*!< \brief Operation StartResult (Method) */ + MNS_OP_PROCESSING = 0xB, /*!< \brief Operation Processing (Method) */ + MNS_OP_RESULT = 0xC, /*!< \brief Operation Result (Method) */ + + MNS_OP_STARTACK = 0x8, /*!< \brief Operation StartAck (Method) */ + MNS_OP_ABORTACK = 0x7, /*!< \brief Operation AbortAck (Method) */ + MNS_OP_STARTRESULTACK = 0x6, /*!< \brief Operation StartResultAck (Method) */ + MNS_OP_PROCESSINGACK = 0xA, /*!< \brief Operation ProcessingAck (Method) */ + MNS_OP_RESULTACK = 0xD, /*!< \brief Operation ResultAck (Method) */ + + MNS_OP_GETINTERFACE = 0x5, /*!< \brief Operation GetInterface (Property/Method) */ + MNS_OP_INTERFACE = 0xE, /*!< \brief Operation Interface (Property/Method) */ + MNS_OP_ERROR = 0xF, /*!< \brief Operation Error (Property/Method) */ + MNS_OP_ERRORACK = 0x9 /*!< \brief Operation ErrorAck (Property/Method) */ + +} Mns_OpType_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MESSAGE_PB_H */ + + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_misc.c b/mnsl/mns_misc.c new file mode 100644 index 0000000..c020c11 --- /dev/null +++ b/mnsl/mns_misc.c @@ -0,0 +1,82 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the library module which contains miscellaneous helper functions. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MISC + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MOST NetServices internal memset-function. + * \param dst_ptr Pointer to the block of memory to fill + * \param value Value to be set + * \param size Number of bytes to be set to the value + */ +void Misc_MemSet(void *dst_ptr, int32_t value, uint32_t size) +{ + uint8_t *dst_ptr_ = (uint8_t *)dst_ptr; + uint32_t i; + + for(i=0U; i<size; i++) + { + dst_ptr_[i] = (uint8_t)value; /* parasoft-suppress MISRA2004-17_4 "void pointer required for memset-function signature (stdlib)" */ + } +} + +/*! \brief MOST NetServices internal memcpy-function. + * \param dst_ptr Pointer to the destination array where the content is to be copied + * \param src_ptr Pointer to the source of data to be copied + * \param size Number of bytes to copy + */ +void Misc_MemCpy(void *dst_ptr, void *src_ptr, uint32_t size) +{ + uint8_t *dst_ptr_ = (uint8_t *)dst_ptr; + uint8_t *src_ptr_ = (uint8_t *)src_ptr; + uint32_t i; + + for(i=0U; i<size; i++) + { + dst_ptr_[i] = src_ptr_[i]; /* parasoft-suppress MISRA2004-17_4 "void pointers required for memcpy-function signature (stdlib)" */ + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_misc.h b/mnsl/mns_misc.h new file mode 100644 index 0000000..76d005f --- /dev/null +++ b/mnsl/mns_misc.h @@ -0,0 +1,157 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the library module which contains miscellaneous helper functions. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MISC + * @{ + */ + +#ifndef MNS_MISC_H +#define MNS_MISC_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Standard library functions */ +/*------------------------------------------------------------------------------------------------*/ +/* parasoft suppress item MISRA2004-19_7 reason "function-like macros allowed for stdlib and helper functions" */ + +/*! \def MISC_MEM_SET + * \brief Macro to encapsulate memset function + * \details By defining the macro MNS_MEM_SET the application is able to specify its own memset + * function. If the macro is not defined MOST NetServices internal memset function + * Misc_MemSet() is used. + * \param dest Pointer to the block of memory to fill + * \param value Value to be set + * \param size Number of bytes to be set to the value. + */ +#ifdef MNS_MEM_SET +#define MISC_MEM_SET(dest, value, size) (MNS_MEM_SET((dest), (value), (size))) +#else +#define MISC_MEM_SET(dest, value, size) (Misc_MemSet((dest), (value), (size))) +#endif + +/*! \def MISC_MEM_CPY + * \brief Macro to encapsulate memcpy function + * \details By defining the macro MNS_MEM_CPY the application is able to specify its own memcpy + * function. If the macro is not defined MOST NetServices internal memcpy function + * Misc_MemCpy() is used. + * \param dest Pointer to the destination array where the content is to be copied + * \param src Pointer to the source of data to be copied + * \param size Number of bytes to copy + */ +#ifdef MNS_MEM_CPY +#define MISC_MEM_CPY(dest, src, size) (MNS_MEM_CPY((dest), (src), (size))) +#else +#define MISC_MEM_CPY(dest, src, size) (Misc_MemCpy((dest), (src), (size))) +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Helper Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Macro to avoid compiler warning "Unused Parameter" */ +#define MISC_UNUSED(p) ((p) = (p)) + +/*! \brief High Byte of 16-bit value */ +#define MISC_HB(value) ((uint8_t)((uint16_t)(value) >> 8)) + +/*! \brief Low Byte of 16-bit value */ +#define MISC_LB(value) ((uint8_t)((uint16_t)(value) & (uint16_t)0xFF)) + +/*! \brief Big-Endian to target 16 bit */ +#define MISC_DECODE_WORD(w_ptr, msb_ptr) (*(w_ptr) = \ + (uint16_t)((uint16_t)((uint16_t)(msb_ptr)[0] << 8) | (uint16_t)(msb_ptr)[1])) + +/*! \brief Big-Endian to target 32 bit */ +#define MISC_DECODE_DWORD(dw_ptr, msb_ptr) (*(dw_ptr) = \ + (uint32_t)((uint32_t)((uint32_t)(msb_ptr)[0] << 24) | \ + (uint32_t)((uint32_t)(msb_ptr)[1] << 16) | \ + (uint32_t)((uint32_t)(msb_ptr)[2] << 8) | (uint32_t)(msb_ptr)[3])) + +/*! \brief Checks if a value is inside a certain range */ +#define MISC_IS_VALUE_IN_RANGE(val, min, max) ((((val) >= (min)) && ((val) <= (max))) ? true : false) + +/*! \brief Checks if the given size is a multiple of 4. If not, the given size is corrected + * by that macro. + */ +#define MISC_QUADLET_ALGINED_SIZE(size) (((((size)+4U)-1U)/4U)*4U) + +/* parasoft unsuppress item MISRA2004-19_7 reason "function-like macros allowed for stdlib and helper functions" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Misc_MemSet(void *dst_ptr, int32_t value, uint32_t size); +extern void Misc_MemCpy(void *dst_ptr, void *src_ptr, uint32_t size); + +/*! + * @} + * \endcond + */ + +/*! + * \def MNS_MEM_SET + * \brief Customer assignment of memset function + * \details By defining the macro MNS_MEM_SET the application is able to specify its own memset + * function to be used by the MOST NetServices. If the macro is not set the MOST + * NetServices will use byte wise write operations. + * \ingroup G_MNS_MISC + */ +#ifndef MNS_MEM_SET +#define MNS_MEM_SET +#endif + +/*! + * \def MNS_MEM_CPY + * \brief Customer assignment of memcpy function + * \details By defining the macro MNS_MEM_CPY the application is able to specify its own memcpy + * function to be used by the MOST NetServices. If the macro is not set the MOST + * NetServices will use byte wise copy operations. + * \ingroup G_MNS_MISC + */ +#ifndef MNS_MEM_CPY +#define MNS_MEM_CPY +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MISC_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_obs.c b/mnsl/mns_obs.c new file mode 100644 index 0000000..7183fb2 --- /dev/null +++ b/mnsl/mns_obs.c @@ -0,0 +1,453 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the observer library module. The module consists of the two classes + * CSubject and CObserver. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_OBS + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_obs.h" +#include "mns_misc.h" +#include "mns_trace.h" + +#include <assert.h> //TKU + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Sub_UpdateList(CSubject *self); +static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CSubject */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the subject class. Initializes a subject which distributes its data to + * a list of observers. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Sub_Ctor(CSubject *self, uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = mns_inst_id; + Dl_Ctor(&self->list, self->mns_inst_id); + Dl_Ctor(&self->add_list, self->mns_inst_id); +} + +/*! \brief Adds an observer to a subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_ALREADY_ADDED: Observer is already added + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr) +{ + Sub_Ret_t ret_val; + if(obs_ptr == NULL) + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + else if(obs_ptr->valid != false) + { + ret_val = SUB_ALREADY_ADDED; + } + else if((self->notify != false) && + (false == Dl_IsNodeInList(&self->list, &obs_ptr->node)) && + (false == Dl_IsNodeInList(&self->add_list, &obs_ptr->node))) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers < 0xFFU)); + Dl_InsertTail(&self->add_list, &obs_ptr->node); + obs_ptr->valid = true; + self->changed = true; + ret_val = SUB_DELAYED; + } + else if((self->notify == false) && (false == Dl_IsNodeInList(&self->list, &obs_ptr->node))) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers < 0xFFU)); + ret_val = SUB_OK; + Dl_InsertTail(&self->list, &obs_ptr->node); + obs_ptr->valid = true; + self->num_observers++; + } + else + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + return ret_val; +} + +/*! \brief Removes an observer from a subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr) +{ + Sub_Ret_t ret_val; + if(obs_ptr == NULL) + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + else if(obs_ptr->valid == false) + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + else if((self->notify != false) && + (Dl_IsNodeInList(&self->list, &obs_ptr->node) != false)) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers > 0U)); + obs_ptr->valid = false; + self->changed = true; + self->num_observers--; + ret_val = SUB_DELAYED; + } + else if((self->notify == false) && + (DL_OK == Dl_Remove(&self->list, &obs_ptr->node))) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers > 0U)); + self->num_observers--; + ret_val = SUB_OK; + } + else + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + return ret_val; +} + +/*! \brief Notifies all registered observers of a subject. + * \param self Instance pointer + * \param data_ptr Reference to value to distribute (optional) + */ +void Sub_Notify(CSubject *self, void *data_ptr) +{ + if(NULL != self) + { + CDlNode *n_tmp = self->list.head; + self->notify = true; + self->changed = false; + while(NULL != n_tmp) + { + CObserver *o_tmp = (CObserver *)n_tmp->data_ptr; + if((NULL != o_tmp->update_fptr) && (o_tmp->valid != false)) + { + (o_tmp->update_fptr)(o_tmp->inst_ptr, data_ptr); + } + n_tmp = n_tmp->next; + } + if(self->changed != false) + { + Sub_UpdateList(self); + } + self->notify = false; + } +} + +/*! \brief Updates the list of observers. Delayed remove- and add-operations are processed. + * \param self Instance pointer + */ +static void Sub_UpdateList(CSubject *self) +{ + (void)Dl_Foreach(&self->list, &Sub_CheckObserver, self); + Dl_AppendList(&self->list, &self->add_list); +} + +/*! \brief Checks if the given observer is still valid. If the observer is invalid it will be + * removed from the list. This function is used by the foreach loop in Sub_UpdateList(). + * \param current_obs_ptr Reference to the current observer object + * \param subject_ptr Reference to the subject object + * \return Returns always \c false. Force to process the whole list. + */ +static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr) +{ + CObserver *current_obs_ptr_ = (CObserver *)current_obs_ptr; + CSubject *subject_ptr_ = (CSubject *)subject_ptr; + + if(false == current_obs_ptr_->valid) + { + (void)Dl_Remove(&subject_ptr_->list, ¤t_obs_ptr_->node); + } + return false; +} + +/*! \brief Returns the number of registered observers of a subject. + * \param self Instance pointer + * \return The number of registered observers + */ +uint8_t Sub_GetNumObservers(CSubject *self) +{ + return self->num_observers; +} + +/*! \brief Switches all observers of the source-subject to the target-subject. + * \param sub_target Target subject + * \param sub_source Source subject + * \return \c SUB_OK: No error + * \return \c SUB_INVALID_OPERATION: Target and source must be different objects + */ +Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source) +{ + Sub_Ret_t ret_val; + + if(sub_target == sub_source) + { + ret_val = SUB_INVALID_OPERATION; + } + else + { + Dl_AppendList(&sub_target->list, &sub_source->list); + sub_target->num_observers += sub_source->num_observers; + sub_source->num_observers = 0U; + ret_val = SUB_OK; + } + return ret_val; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the observer class. Initializes an observer which is notified + * by a corresponding subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param update_fptr Callback function to update the observer + */ +void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr) +{ + assert(NULL != inst_ptr); + MISC_MEM_SET(self, 0, sizeof(*self)); + self->inst_ptr = inst_ptr; + self->update_fptr = update_fptr; + Dln_Ctor(&self->node, self); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CSingleSubject */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the single-subject class. Initializes a single-subject which distributes + * its data to the registered single-observer. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Ssub_Ctor(CSingleSubject *self, uint8_t mns_inst_id) +{ + self->observer_ptr = NULL; + self->mns_inst_id = mns_inst_id; +} + +/*! \brief Adds a single-observer to a single-subject. + * \param self Instance pointer + * \param obs_ptr Pointer to single-observer instance + * \return \c SSUB_OK: No error + * \return \c SSUB_ALREADY_ADDED: Observer is already added + * \return \c SSUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr) +{ + Ssub_Ret_t ret_val; + if(obs_ptr == NULL) + { + ret_val = SSUB_UNKNOWN_OBSERVER; + } + else if(self->observer_ptr != obs_ptr) + { +#ifdef MNS_TR_INFO + if(self->observer_ptr != NULL) + { + TR_INFO((self->mns_inst_id, "[SSUB]", "Observer callback has been overwritten", 0U)); + } +#endif + ret_val = SSUB_OK; + self->observer_ptr = obs_ptr; + } + else + { + ret_val = SSUB_ALREADY_ADDED; + } + + return ret_val; +} + +/*! \brief Removes an single-observer from a single-subject. + * \param self Instance pointer + */ +void Ssub_RemoveObserver(CSingleSubject *self) +{ + self->observer_ptr = NULL; +} + +/*! \brief Notifies the registered single-observer of the given single-subject. + * \param self Instance pointer + * \param data_ptr Reference to value to distribute (optional) + * \param auto_remove If true the observer will be removed + */ +void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove) +{ + void *inst_ptr = NULL; + Obs_UpdateCb_t update_fptr = NULL; + if(self->observer_ptr != NULL) + { + inst_ptr = self->observer_ptr->inst_ptr; + update_fptr = self->observer_ptr->update_fptr; + if(auto_remove != false) + { + self->observer_ptr = NULL; + } + } + if(update_fptr != NULL) + { + update_fptr(inst_ptr, data_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CSingleObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the single-observer class. Initializes an single-observer which is + * notified by a corresponding single-subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param update_fptr Callback function to update the observer + */ +void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr) +{ + self->inst_ptr = inst_ptr; + self->update_fptr = update_fptr; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the masked-observer class. Initializes an observer which is notified + * by a corresponding subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param notification_mask Notification bitmask + * \param update_fptr Callback function to update the observer + */ +void Mobs_Ctor(CMaskedObserver *self, + void *inst_ptr, + uint32_t notification_mask, + Obs_UpdateCb_t update_fptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + Obs_Ctor(&self->parent, inst_ptr, update_fptr); + self->notification_mask = notification_mask; +} + +/*! \brief Sets the notification mask of a masked-observer. + * \param self Instance pointer + * \param mask Bitmask to set + */ +void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask) +{ + self->notification_mask = mask; +} + +/*! \brief Retrieves the notification mask of a masked-observer. + * \param self Instance pointer + * \return Returns the current notification bitmask of the given observer + */ +uint32_t Mobs_GetNotificationMask(CMaskedObserver *self) +{ + return self->notification_mask; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Additional methods of class CSubject used in combination with CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Adds an masked-observer to a masked-subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_ALREADY_ADDED: Observer is already added + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr) +{ + return Sub_AddObserver(self, &obs_ptr->parent); +} + +/*! \brief Removes an masked-observer from a subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr) +{ + return Sub_RemoveObserver(self, &obs_ptr->parent); +} + +/*! \brief Notifies all registered masked-observers of a masked-subject. + * \param self Instance pointer + * \param data_ptr Reference to value to distribute (optional) + * \param notification_mask Bitmask indicates notified observers + */ +void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask) +{ + if(NULL != self) + { + CDlNode *n_tmp = self->list.head; + self->notify = true; + self->changed = false; + while(NULL != n_tmp) + { + CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr; + if( (NULL != o_tmp->parent.update_fptr) && + (o_tmp->parent.valid != false) && + ((o_tmp->notification_mask & notification_mask) != 0U) ) + { + (o_tmp->parent.update_fptr)(o_tmp->parent.inst_ptr, data_ptr); + } + n_tmp = n_tmp->next; + } + if(self->changed != false) + { + Sub_UpdateList(self); + } + self->notify = false; + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_obs.h b/mnsl/mns_obs.h new file mode 100644 index 0000000..088c6c8 --- /dev/null +++ b/mnsl/mns_obs.h @@ -0,0 +1,196 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the observer library module. The module consists of the two + * classes CSubject and CObserver. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_OBS + * @{ + */ + +#ifndef MNS_OBS_H +#define MNS_OBS_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mns_dl.h" +#include "mns_ret.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for callback functions which notifies the observers. + * \param self Instance pointer + * \param data_ptr Reference to optional data + */ +typedef void (*Obs_UpdateCb_t)(void *self, void *data_ptr); + +/*! \brief Function signature used for callback functions which notifies the single-observers. + * \param self Instance pointer + * \param data_ptr Reference to optional data + */ +typedef void (*Sobs_UpdateCb_t)(void *self, void *data_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerators */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return values of the subject class. */ +typedef enum Sub_Ret_ +{ + SUB_OK, /*!< \brief No error */ + SUB_DELAYED, /*!< \brief Operation is queued since notification is still active */ + SUB_ALREADY_ADDED, /*!< \brief Observer already added */ + SUB_UNKNOWN_OBSERVER, /*!< \brief Unknown observer */ + SUB_INVALID_OPERATION /*!< \brief Invalid operation */ + +} Sub_Ret_t; + +/*! \brief Standard return values of the single-subject class. */ +typedef enum Ssub_Ret_ +{ + SSUB_OK, /*!< \brief No error */ + SSUB_ALREADY_ADDED, /*!< \brief Observer already added */ + SSUB_UNKNOWN_OBSERVER /*!< \brief Unknown observer */ + +} Ssub_Ret_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of observers which are notified by subjects. */ +typedef struct CObserver_ +{ + CDlNode node; /*!< \brief Node element to be able to add observer to list */ + void *inst_ptr; /*!< \brief Reference to instance used by update_fptr() */ + Obs_UpdateCb_t update_fptr; /*!< \brief Callback function to update the observer */ + bool valid; /*!< \brief Used for queued remove operation */ + +} CObserver; + +/*! \brief Class structure of subjects. */ +typedef struct CSubject_ +{ + CDlList list; /*!< \brief Doubly linked list to manage observers */ + CDlList add_list; /*!< \brief List to manage delayed add operations */ + uint8_t num_observers; /*!< \brief Number of added observers */ + bool notify; /*!< \brief Signals that the notification is in progress */ + bool changed; /*!< \brief Signals that an add- or a remove-operation + has been queued */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CSubject; + +/*! \brief Class structure of a single-observer which is notified by a single-subject. */ +typedef struct CSingleObserver_ +{ + void *inst_ptr; /*!< \brief Reference to instance used by update_fptr() */ + Obs_UpdateCb_t update_fptr; /*!< \brief Callback function to update the observer */ + +} CSingleObserver; + +/*! \brief Class structure of a single-subject. */ +typedef struct CSingleSubject_ +{ + CSingleObserver *observer_ptr; /*!< \brief Reference to the assigned single-observer */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CSingleSubject; + +/*! \brief Class structure of masked observers which are notified by subjects. */ +typedef struct CMaskedObserver_ +{ + CObserver parent; /*!< \brief Parent class instance */ + uint32_t notification_mask; /*!< \brief Notification bitmask */ + +} CMaskedObserver; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CSubject */ +/*------------------------------------------------------------------------------------------------*/ +extern void Sub_Ctor(CSubject *self, uint8_t mns_inst_id); +extern Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr); +extern Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr); +extern void Sub_Notify(CSubject *self, void *data_ptr); +extern uint8_t Sub_GetNumObservers(CSubject *self); +extern Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CSingleSubject */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ssub_Ctor(CSingleSubject *self, uint8_t mns_inst_id); +extern Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr); +extern void Ssub_RemoveObserver(CSingleSubject *self); +extern void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CSingleObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern void Mobs_Ctor(CMaskedObserver *self, + void *inst_ptr, + uint32_t notification_mask, + Obs_UpdateCb_t update_fptr); +extern void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask); +extern uint32_t Mobs_GetNotificationMask(CMaskedObserver *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Additional prototypes of class CSubject used in combination with CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr); +extern Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr); +extern void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_OBS_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmchannel.c b/mnsl/mns_pmchannel.c new file mode 100644 index 0000000..e98a2b2 --- /dev/null +++ b/mnsl/mns_pmchannel.c @@ -0,0 +1,311 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Port Message Channel + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMC + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmchannel.h" +#include "mns_pmp.h" +#include "mns_pmcmd.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +/* LLD related interface functions */ +static Mns_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size); +static void Pmch_RxUnused(void *self, Mns_Lld_RxMsg_t *msg_ptr); +static void Pmch_RxReceive(void *self, Mns_Lld_RxMsg_t *msg_ptr); +static void Pmch_TxRelease(void *self, Mns_Lld_TxMsg_t *msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Constructor of class CPmChannel + * \param self The instance + * \param init_ptr Reference to initialization data structure + * \param inst_ptr TKU: MultiInstance param + */ +void Pmch_Ctor(CPmChannel *self, const Pmch_InitData_t *init_ptr, void *inst_ptr) +{ + uint16_t cnt; + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->init_data = *init_ptr; + self->inst_ptr = inst_ptr; + self->lld_active = false; + + self->mns_iface.rx_allocate_fptr = &Pmch_RxAllocate; + self->mns_iface.rx_receive_fptr = &Pmch_RxReceive; + self->mns_iface.rx_free_unused_fptr = &Pmch_RxUnused; + self->mns_iface.tx_release_fptr = &Pmch_TxRelease; + + Pool_Ctor(&self->rx_msgs_pool, self->rx_msgs, /* initialize Rx message pool */ + PMCH_POOL_SIZE_RX, self->init_data.mns_inst_id); + for (cnt = 0U; cnt < PMCH_POOL_SIZE_RX; cnt++) /* and assign LLD Rx handles */ + { + Msg_SetLldHandle(&self->rx_msgs[cnt], &self->lld_rx_msgs[cnt]); + self->lld_rx_msgs[cnt].msg_ptr = &self->rx_msgs[cnt]; + } +} + +/*! \brief Registers an Rx callback function dedicated to one FIFO + * \param self The instance + * \param fifo_id The FIFO identifier + * \param rx_fptr The Rx callback function + * \param inst_ptr Reference to the instance required to invoke the callback + */ +void Pmch_RegisterReceiver(CPmChannel *self, Pmp_FifoId_t fifo_id, Pmch_OnRxMsg_t rx_fptr, void *inst_ptr) +{ + TR_ASSERT(self->init_data.mns_inst_id, "[PMCH]", (((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_ICM)||((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_MCM)||((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_RCM))); + + self->receivers[fifo_id].rx_fptr = rx_fptr; + self->receivers[fifo_id].inst_ptr = inst_ptr; +} + +/*! \brief Un-initializes the LLD interface of the channel + * \param self The instance + */ +void Pmch_Initialize(CPmChannel *self) +{ + if (self->lld_active == false) + { + self->lld_active = true; + TR_INFO((self->init_data.mns_inst_id, "[PMCH]", "Pmch_Initialize(): LLD_START()", 0U)); + self->init_data.lld_iface.start_fptr(&self->mns_iface, self, self->inst_ptr); + } +} + +/*! \brief Un-initializes the LLD interface of the channel + * \param self The instance + */ +extern void Pmch_Uninitialize(CPmChannel *self) +{ + TR_INFO((self->init_data.mns_inst_id, "[PMCH]", "Pmch_Uninitialize(): Channel un-synchronization started", 0U)); + + if (self->lld_active != false) + { + self->lld_active = false; + TR_INFO((self->init_data.mns_inst_id, "[PMCH]", "Pmch_Uninitialize(): LLD_STOP()", 0U)); + self->init_data.lld_iface.stop_fptr(self->inst_ptr); + } +} + +/*! \brief Wrapper for LLD transmit + * \details This function which shall be used by all internal classes. No class shall + * invoke the LLD transmit function directly. Thus, it might be possible + * in future to handle transmission failures and retries. + * \param self The instance + * \param msg_ptr Reference to the public LLD message structure + */ +void Pmch_Transmit(CPmChannel *self, Mns_Lld_TxMsg_t *msg_ptr) +{ + if (self->lld_active != false) + { + self->init_data.lld_iface.tx_transmit_fptr(msg_ptr, self->inst_ptr); + } + else + { + Pmch_TxRelease(self, msg_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* The exposed low-level driver interface */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an Rx message object + * \param self The instance + * \param buffer_size Size of the memory chunk in bytes which is needed to + * copy the Rx message. + * \return Reference to an allocated Rx message object or \c NULL if no message object is available. + */ +static Mns_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size) +{ + CMessage *msg_ptr = NULL; + Mns_Lld_RxMsg_t *handle = NULL; + CPmChannel *self_ = (CPmChannel*)self; + + if (buffer_size <= MSG_SIZE_RSVD_BUFFER) + { + msg_ptr = Pool_GetMsg(&self_->rx_msgs_pool); + + if (msg_ptr != NULL) + { + Msg_Cleanup(msg_ptr); + handle = &((Lld_IntRxMsg_t*)Msg_GetLldHandle(msg_ptr))->lld_msg; + + TR_ASSERT(self_->init_data.mns_inst_id, "[PMCH]", (handle != NULL)); + + handle->data_size = buffer_size; + handle->data_ptr = Msg_GetHeader(msg_ptr); + } + else + { + self_->rx_trigger_available = true; + TR_INFO((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxAllocate(): Allocation failed, size=%u", 1U, buffer_size)); + } + } + else + { + self_->rx_trigger_available = true; + TR_FAILED_ASSERT(self_->init_data.mns_inst_id, "[PMCH]"); + } + + return handle; +} + +/*! \brief Frees an unused Rx message object + * \param self The instance + * \param msg_ptr Reference to the unused Rx message object + */ +static void Pmch_RxUnused(void *self, Mns_Lld_RxMsg_t *msg_ptr) +{ + CPmChannel *self_ = (CPmChannel*)self; + CMessage *pb_handle = ((Lld_IntRxMsg_t*)(void*)msg_ptr)->msg_ptr; + + TR_ASSERT(self_->init_data.mns_inst_id, "[PMCH]", (pb_handle != NULL)); + Pmch_ReturnRxToPool(self_, pb_handle); +} + +/*! \brief Pass an Rx message to MOST NetServices + * \param self The instance + * \param msg_ptr Reference to the Rx message object containing the received + * message. + */ +static void Pmch_RxReceive(void *self, Mns_Lld_RxMsg_t *msg_ptr) +{ + bool found = false; + CPmChannel *self_ = (CPmChannel*)self; + + if (msg_ptr->data_ptr != NULL) + { + if (msg_ptr->data_size >= PMP_PM_MIN_SIZE_HEADER) /* ignore incomplete messages */ + { + uint8_t fifo_no = (uint8_t)Pmp_GetFifoId(msg_ptr->data_ptr); /* get channel id (FIFO number) */ + + if ((fifo_no < PMP_MAX_NUM_FIFOS) && (self_->receivers[fifo_no].inst_ptr != NULL)) + { + CMessage *handle = ((Lld_IntRxMsg_t*)(void*)msg_ptr)->msg_ptr; + /* forward message to the respective FIFO/channel */ + self_->receivers[fifo_no].rx_fptr(self_->receivers[fifo_no].inst_ptr, handle); + found = true; + } + else + { + TR_ERROR((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxReceive(): received message for unregistered FIFO no=%u", 1U, fifo_no)); + } + } + else + { + TR_ERROR((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxReceive(): received incomplete message of size=%u", 1U, msg_ptr->data_size)); + } + } + else + { + TR_ERROR((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxReceive(): message data is not valid", 0U)); + } + + if (false == found) + { + Pmch_RxUnused(self_, msg_ptr); /* Just return message to pool until PMC is implemented */ + } +} + +/*! \brief Notifies that the LLD no longer needs to access the Tx message object + * \param self The instance + * \param msg_ptr Reference to the Tx message object which is no longer accessed + * by the low-level driver + */ +static void Pmch_TxRelease(void *self, Mns_Lld_TxMsg_t *msg_ptr) +{ + CPmChannel *self_ = (CPmChannel*)self; + Lld_IntTxMsg_t *tx_ptr = (Lld_IntTxMsg_t*)(void*)msg_ptr; + + if ((tx_ptr->owner_ptr == NULL) && (tx_ptr->msg_ptr == NULL)) /* tx_ptr is command */ + { + Pmcmd_Release((CPmCommand*)(void*)tx_ptr); + } + else if (tx_ptr->owner_ptr != NULL) /* release message to FIFO */ + { + self_->init_data.tx_release_fptr(tx_ptr->owner_ptr, msg_ptr); + } + else + { + TR_FAILED_ASSERT(self_->init_data.mns_inst_id, "[PMCH]"); /* unknown FIFO - invalid message object */ + } + + TR_ASSERT(self_->init_data.mns_inst_id, "[PMCH]", (NULL == msg_ptr->custom_next_msg_ptr)); /* concatenation destroyed by the LLD */ + +} + +/*------------------------------------------------------------------------------------------------*/ +/* FIFO Related Callback Functions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Returns an unused Rx message object back to the pool + * \param self The instance + * \param msg_ptr The unused Rx message object + */ +void Pmch_ReturnRxToPool(void *self, CMessage *msg_ptr) +{ + CPmChannel *self_ = (CPmChannel*)self; + + Pool_ReturnMsg(msg_ptr); + + if (self_->rx_trigger_available == true) + { + self_->rx_trigger_available = false; + + if (self_->init_data.lld_iface.rx_available_fptr != NULL) + { + self_->init_data.lld_iface.rx_available_fptr(self_->inst_ptr); + } + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmchannel.h b/mnsl/mns_pmchannel.h new file mode 100644 index 0000000..8b069ad --- /dev/null +++ b/mnsl/mns_pmchannel.h @@ -0,0 +1,179 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of Port Message Channel + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMC + * @{ + */ + +#ifndef MNS_PMCHANNEL_H +#define MNS_PMCHANNEL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_lld_pb.h" +#include "mns_lldpool.h" +#include "mns_pool.h" +#include "mns_base.h" +#include "mns_message.h" +#include "mns_pmp.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define PMCH_POOL_SIZE_RX_MIN 10U /*!< \brief Minimal size of Rx pool which is shared by all FIFOs */ +#define PMCH_POOL_SIZE_RX_OPT 35U /*!< \brief Optimal size of Rx pool which is shared by all FIFOs */ + +#define PMCH_MCM_CREDITS_OPT 21U /*!< \brief Optimal number of credits configured for MCM FIFO */ +#define PMCH_MCM_THRESHOLD_OPT 8U /*!< \brief Optimal threshold configured for MCM FIFO */ + +#define PMCH_FIFO_CREDITS_OPT 5U /*!< \brief Optimal number of credits configured for conventional FIFOs */ +#define PMCH_FIFO_THRESHOLD_OPT 4U /*!< \brief Optimal threshold configured for conventional FIFO */ + +#define PMCH_FIFO_CREDITS_MIN 3U /*!< \brief Minimal number of credits configured for conventional FIFOs */ +#define PMCH_FIFO_THRESHOLD_MIN 2U /*!< \brief Minimal threshold configured for conventional FIFO */ + +/* required rules */ +#if defined(MNS_FOOTPRINT_TINY) && defined(MNSL_CHANNEL_POOL_SIZE_RX) +# error Forbidden combination of macros MNS_FOOTPRINT_TINY and MNSL_CHANNEL_POOL_SIZE_RX +#endif + +#ifdef MNSL_CHANNEL_POOL_SIZE_RX +# if (MNSL_CHANNEL_POOL_SIZE_RX < PMCH_POOL_SIZE_RX_MIN) +# error MNSL_CHANNEL_POOL_SIZE_RX must be at least 10 +# endif +#endif + +/*! \def MNSL_CHANNEL_POOL_SIZE_RX + * \brief MNSL configuration that defines the number of pre-allocated Rx messages which are shared by all FIFOs. + * Valid values: 35...65535. Default value: 35. + * + * \def PMCH_POOL_SIZE_RX + * \brief Defines the number of pre-allocated Rx messages which are shared by all FIFOs. + */ +#ifdef MNS_FOOTPRINT_TINY +# define PMCH_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_MIN) +# define PMCH_MCM_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_FIFO_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_MCM_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +# define PMCH_FIFO_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +# define MNSL_CHANNEL_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_MIN) +#elif defined MNSL_CHANNEL_POOL_SIZE_RX +# define PMCH_POOL_SIZE_RX ((uint16_t)MNSL_CHANNEL_POOL_SIZE_RX) +# define PMCH_MCM_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_FIFO_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_MCM_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +# define PMCH_FIFO_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +#else +# define PMCH_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_OPT) +# define PMCH_MCM_CREDITS (PMCH_MCM_CREDITS_OPT) +# define PMCH_FIFO_CREDITS (PMCH_FIFO_CREDITS_OPT) +# define PMCH_MCM_THRESHOLD (PMCH_MCM_THRESHOLD_OPT) +# define PMCH_FIFO_THRESHOLD (PMCH_FIFO_THRESHOLD_OPT) +# define MNSL_CHANNEL_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_OPT) +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +typedef void (*Pmch_OnRxMsg_t)(void *fifo_ptr, CMessage *msg_ptr); +typedef void (*Pmch_OnTxRelease_t)(void *fifo_ptr, Mns_Lld_TxMsg_t *handle_ptr); + +/*! \brief Initialization structure of the Base Module. */ +typedef struct Pmch_InitData_ +{ + uint8_t mns_inst_id; /*!< \brief Initialization data of the Trace Module */ + Mns_Lld_Callbacks_t lld_iface; /*!< \brief LLD callback functions */ + Pmch_OnTxRelease_t tx_release_fptr; /*!< \brief Callback which releases a FIFO dedicated LLD buffer */ + +} Pmch_InitData_t; + +/*! \brief Combination of callback and instance for a receiving FIFO */ +typedef struct Pmch_Receiver_ +{ + Pmch_OnRxMsg_t rx_fptr; /*!< \brief Reference to an Rx callback function */ + void *inst_ptr; /*!< \brief Reference to the instance which shall be + * passed to the callback function */ +} Pmch_Receiver_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Class attributes */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Structure of a PMS object */ +typedef struct CPmChannel_ +{ + Pmch_InitData_t init_data; /*!< \brief Copy of initialization data */ + + Lld_IntRxMsg_t lld_rx_msgs[PMCH_POOL_SIZE_RX]; /*!< \brief Pre-allocated LLD Rx message objects */ + CMessage rx_msgs[PMCH_POOL_SIZE_RX]; /*!< \brief Pre-allocated Rx message objects */ + CPool rx_msgs_pool; /*!< \brief Pre-allocated Rx message pool */ + bool rx_trigger_available; /*!< \brief Triggers LLD callback function if a buffer + * is available again. + */ + bool lld_active; /*!< \brief Determines whether the LLD is running */ + Mns_Lld_Api_t mns_iface; /*!< \brief PMS function pointers */ + + Pmch_Receiver_t receivers[PMP_MAX_NUM_FIFOS]; /*!< \brief Registered FIFOs for Rx */ + + void *inst_ptr; /*< TKU: tag for LLD */ + +} CPmChannel; + + +/*------------------------------------------------------------------------------------------------*/ +/* Class methods */ +/*------------------------------------------------------------------------------------------------*/ +/* component creation */ +extern void Pmch_Ctor(CPmChannel *self, const Pmch_InitData_t *init_ptr, void *inst_ptr); +extern void Pmch_Initialize(CPmChannel *self); +extern void Pmch_Uninitialize(CPmChannel *self); +extern void Pmch_RegisterReceiver(CPmChannel *self, Pmp_FifoId_t fifo_id, Pmch_OnRxMsg_t rx_fptr, void *inst_ptr); +extern void Pmch_Transmit(CPmChannel *self, Mns_Lld_TxMsg_t *msg_ptr); +extern void Pmch_ReturnRxToPool(void *self, CMessage *msg_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMCHANNEL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmcmd.c b/mnsl/mns_pmcmd.c new file mode 100644 index 0000000..21d8913 --- /dev/null +++ b/mnsl/mns_pmcmd.c @@ -0,0 +1,157 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 class CPmCommand + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PM_CMD + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmcmd.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of CPmCommand class + * \param self The instance + * \param fifo The dedicated FIFO + * \param type The port message type + */ +void Pmcmd_Ctor(CPmCommand *self, Pmp_FifoId_t fifo, Pmp_MsgType_t type) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); /* setup attributes */ + self->memory.data_ptr = &self->data[0]; + self->tx_obj.lld_msg.memory_ptr = &self->memory; + self->tx_obj.msg_ptr = NULL; /* label message as command by setting */ + self->tx_obj.owner_ptr = NULL; /* msg_ptr and owner_ptr to NULL */ + self->trigger = false; + + Pmp_SetPmhl(self->data, 3U); /* PMHL is always "3" for control/status messages */ + Pmp_SetFph(self->data, fifo, type); +} + +/*! \brief Retrieves reference to the LLD Tx message object required to call Pmch_Transmit() + * \param self The instance + * \return Returns a reference to the LLD Tx message object + */ +Mns_Lld_TxMsg_t* Pmcmd_GetLldTxObject(CPmCommand *self) +{ + return (Mns_Lld_TxMsg_t*)(void*)self; +} + +/*! \brief Sets the content of a command/status message + * \param self The instance + * \param sid The sequence id + * \param ext_type The ExtType type + * \param ext_code The ExtType code + * \param add_data_ptr Additional payload data + * \param add_data_sz The size of additional payload data, valid values: 0..4 + */ +void Pmcmd_SetContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, uint8_t ext_code, uint8_t add_data_ptr[], uint8_t add_data_sz) +{ + if ((add_data_ptr != NULL) && (add_data_sz != 0U)) + { + MISC_MEM_CPY(&self->data[6U], add_data_ptr, (size_t)add_data_sz); + } + + self->memory.data_size = 6U + (uint16_t)add_data_sz; + self->memory.total_size = 6U + (uint16_t)add_data_sz; + + Pmp_SetPml(self->data, 4U + add_data_sz); + Pmp_SetSid(self->data, sid); + Pmp_SetExtType(self->data, ext_type, ext_code); +} + +/*! \brief Updates the content of a command/status message + * \details The length and the content of the payload is not modified. + * It is important to call Pmcmd_SetContent() before. + * \param self The instance + * \param sid The sequence id + * \param ext_type The ExtType type + * \param ext_code The ExtType code + */ +void Pmcmd_UpdateContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, uint8_t ext_code) +{ + Pmp_SetSid(self->data, sid); + Pmp_SetExtType(self->data, ext_type, ext_code); +} + +/*! \brief Reserves the command object if it is available + * \param self The instance + * \return \c true if the command object is available, \c false + * if the command object is (still) in usage + */ +bool Pmcmd_Reserve(CPmCommand *self) +{ + bool succ = false; + + if (self->reserved == false) + { + self->reserved = true; + succ = true; + } + return succ; +} + +/*! \brief Releases the command object after usage + * \param self The instance + */ +void Pmcmd_Release(CPmCommand *self) +{ + self->reserved = false; +} + +/*! \brief Sets or resets the trigger attribute + * \param self The instance + * \param trigger The trigger value + */ +void Pmcmd_SetTrigger(CPmCommand *self, bool trigger) +{ + self->trigger = trigger; +} + +/*! \brief Returns the trigger value + * \param self The instance + * \return Returns \c true if the trigger attribute is set, otherwise \c false. + */ +bool Pmcmd_IsTriggered(CPmCommand *self) +{ + return self->trigger; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmcmd.h b/mnsl/mns_pmcmd.h new file mode 100644 index 0000000..5905beb --- /dev/null +++ b/mnsl/mns_pmcmd.h @@ -0,0 +1,92 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of class CPmCommand + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PM_CMD + * @{ + */ + +#ifndef MNS_PMCMD_H +#define MNS_PMCMD_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mns_memory.h" +#include "mns_lldpool.h" +#include "mns_pmp.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Class CPmCommand */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CPmCommand */ +typedef struct CPmCommand_ +{ + Lld_IntTxMsg_t tx_obj; /*!< \brief Required LLD Tx structure, must be first attribute */ + uint8_t data[10]; /*!< \brief Reserved memory space */ + Mns_Mem_Buffer_t memory; /*!< \brief Public memory structure */ + bool reserved; /*!< \brief \c true if the command is in use, otherwise \c false. */ + bool trigger; /*!< \brief \c true if the command is triggered, otherwise \c false. */ + +} CPmCommand; + + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pmcmd_Ctor(CPmCommand *self, Pmp_FifoId_t fifo, Pmp_MsgType_t type); +extern Mns_Lld_TxMsg_t* Pmcmd_GetLldTxObject(CPmCommand *self); +extern bool Pmcmd_Reserve(CPmCommand *self); +extern void Pmcmd_Release(CPmCommand *self); +extern void Pmcmd_SetContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, + uint8_t ext_code, uint8_t add_data_ptr[], uint8_t add_data_sz); +extern void Pmcmd_UpdateContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, uint8_t ext_code); +extern void Pmcmd_SetTrigger(CPmCommand *self, bool trigger); +extern bool Pmcmd_IsTriggered(CPmCommand *self); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_PMCMD_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifo.c b/mnsl/mns_pmfifo.c new file mode 100644 index 0000000..a1cafbf --- /dev/null +++ b/mnsl/mns_pmfifo.c @@ -0,0 +1,1368 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Port Message FIFO + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmfifo.h" +#include "mns_pmp.h" +#include "mns_pmcmd.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal macros */ +/*------------------------------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ +static const uint8_t FIFO_SRV_PRIO = 252U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ +static const Srv_Event_t FIFO_SE_RX_SERVICE = 1U; /*!< \brief Event which triggers the Rx service */ +static const Srv_Event_t FIFO_SE_TX_SERVICE = 2U; /*!< \brief Event which triggers the Rx service */ +static const Srv_Event_t FIFO_SE_TX_APPLY_STATUS = 4U; /*!< \brief Event which triggers to apply the current INIC status */ +static const Srv_Event_t FIFO_SE_ALL = 7U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Fifo_InitCounters(CPmFifo *self, uint8_t tx_sid_complete, uint8_t tx_credits); +static void Fifo_Service(void *self); + +static void Fifo_RxService(CPmFifo *self); +static void Fifo_RxCheckStatusTrigger(CPmFifo *self); +static void Fifo_RxGetCredit(CPmFifo *self); +static void Fifo_RxReleaseCredit(CPmFifo *self); +static bool Fifo_RxProcessData(CPmFifo *self, CMessage *msg_ptr); +static void Fifo_RxProcessStatus(CPmFifo *self, CMessage *msg_ptr); +static void Fifo_RxProcessCommand(CPmFifo *self, CMessage *msg_ptr); +static void Fifo_RxProcessSyncStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code, uint8_t *header_ptr); +static uint8_t Fifo_RxCheckFailureCode(CPmFifo *self, uint8_t failure_code); +static void Fifo_OnRx(void *self, CMessage *msg_ptr); + +static void Fifo_TxService(CPmFifo *self); +static void Fifo_TxProcessData(CPmFifo *self); +static void Fifo_TxProcessStatus(CPmFifo *self); +static void Fifo_TxProcessCommand(CPmFifo *self); + +static void Fifo_TxEnqueueBypassMsg(CPmFifo *self, CDlList *q_ptr, CMessage *msg_ptr); +static bool Fifo_FindFirstRegularMsg(void *d_ptr, void *ud_ptr); + +static void Fifo_TxExecuteCancel(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code); +static void Fifo_TxExecuteCancelAll(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code); +static void Fifo_TxFinishedCancelAll(CPmFifo *self); +static uint8_t Fifo_TxPendingGetFollowerId(CPmFifo *self); +static void Fifo_TxCancelFollowers(CPmFifo *self, uint8_t follower_id, Mns_MsgTxStatus_t status); + +static bool Fifo_TxHasAccessPending(CPmFifo *self); +static void Fifo_TxRestorePending(CPmFifo *self); + +static void Fifo_TxOnWatchdogTimer(void *self); +static void Fifo_TxStartWatchdog(CPmFifo *self); + +static uint8_t Fifo_TxGetValidAcknowledges(CPmFifo *self, uint8_t sid); +static bool Fifo_TxNotifyStatus(CPmFifo *self, uint8_t sid, Mns_MsgTxStatus_t status); +static void Fifo_TxApplyCurrentStatus(CPmFifo *self); +static void Fifo_TxUpdateCurrentStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code); +static bool Fifo_TxIsIncomingSidValid(CPmFifo *self, uint8_t sid); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of message FIFO + * \param self The instance + * \param init_ptr Reference to initialization data + * \param config_ptr Reference to configuration + */ +void Fifo_Ctor(CPmFifo *self, const Fifo_InitData_t *init_ptr, const Fifo_Config_t *config_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->init = *init_ptr; + self->config = *config_ptr; + + self->sync_state = FIFO_S_UNSYNCED_INIT; /* initialize members */ + Sub_Ctor(&self->sync_state_subject, self->init.base_ptr->mns_inst_id); + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Ctor(): state: %u", 1U, self->sync_state)); + + Srv_Ctor(&self->service, FIFO_SRV_PRIO, self, &Fifo_Service); /* registration of service */ + (void)Scd_AddService(&self->init.base_ptr->scd, &self->service); + + T_Ctor(&self->wd.timer); /* setup watchdog */ + self->wd.timer_value = self->config.tx_wd_timer_value; + Pmcmd_Ctor(&self->wd.wd_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD); + Pmcmd_SetContent(&self->wd.wd_cmd, 0U, PMP_CMD_TYPE_REQ_STATUS, PMP_CMD_CODE_REQ_STATUS, NULL, 0U); + + /* init Rx part */ + Dl_Ctor(&self->rx.queue, self->init.base_ptr->mns_inst_id); + self->rx.encoder_ptr = self->init.rx_encoder_ptr; + self->rx.on_complete_fptr = self->init.rx_cb_fptr; + self->rx.on_complete_inst = self->init.rx_cb_inst; + + self->rx.ack_threshold = self->config.rx_threshold; + + if (self->config.rx_threshold > self->config.rx_credits)/* configuration error - use single acknowledge */ + { + self->rx.ack_threshold = 1U; + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + + self->rx.wait_processing = false; + Pmcmd_Ctor(&self->rx.status, self->config.fifo_id, PMP_MSG_TYPE_STATUS); + Pmcmd_SetContent(&self->rx.status, 0U, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_SUCCESS, NULL, 0U); + + /* init Tx part */ + Dl_Ctor(&self->tx.waiting_queue, self->init.base_ptr->mns_inst_id); + Dl_Ctor(&self->tx.pending_q, self->init.base_ptr->mns_inst_id); + + Pmcmd_Ctor(&self->tx.cancel_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD); + Pmcmd_SetContent(&self->tx.cancel_cmd, 0U, PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL, NULL, 0U); + + Fifo_InitCounters(self, 0U, 0U); /* values are incremented on each sync attempt */ + self->tx.encoder_ptr = init_ptr->tx_encoder_ptr; + + /* FIFO synchronization command */ + self->sync_cnt = 0xFFU; + self->sync_params[0] = config_ptr->rx_credits; + self->sync_params[1] = config_ptr->rx_busy_allowed; + self->sync_params[2] = config_ptr->rx_ack_timeout; + self->sync_params[3] = config_ptr->tx_wd_timeout; + Pmcmd_Ctor(&self->tx.sync_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD); + Pmcmd_SetContent(&self->tx.sync_cmd, 0U, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_SYNC, self->sync_params, 4U); + + /* default PM header for Tx */ + self->tx.pm_header.pml = 6U; + self->tx.pm_header.pmhl = self->tx.encoder_ptr->pm_hdr_sz - 3U; + Pmh_SetFph(&self->tx.pm_header, self->config.fifo_id, PMP_MSG_TYPE_DATA); + self->tx.pm_header.sid = 0U; + self->tx.pm_header.ext_type = (uint8_t)self->tx.encoder_ptr->content_type; + + Lldp_Ctor(&self->tx.lld_pool, self, self->init.base_ptr->mns_inst_id); + + Pmch_RegisterReceiver(self->init.channel_ptr, self->config.fifo_id, &Fifo_OnRx, self); +} + +/*! \brief Initializes flow control and related counters + * \param self The instance + * \param tx_sid_complete Reference to initialization data + * \param tx_credits Number of credits for Tx + */ +static void Fifo_InitCounters(CPmFifo *self, uint8_t tx_sid_complete, uint8_t tx_credits) +{ + self->rx.busy_num = 0U; + self->rx.expected_sid = tx_sid_complete + 1U; + self->rx.ack_last_ok_sid = tx_sid_complete; + + self->tx.credits = tx_credits; + self->tx.sid_next_to_use = tx_sid_complete +1U; + self->tx.sid_last_completed = tx_sid_complete; + + self->tx.failure_status = 0U; + self->tx.failure_sid = 0U; + + self->tx.current_sid = tx_sid_complete; + self->tx.current_type = PMP_STATUS_TYPE_FLOW; + self->tx.current_code = (uint8_t)PMP_STATUS_CODE_SUCCESS; +} + +/*! \brief Adds an observer of synchronization state changes + * \param self The instance + * \param obs_ptr The observer. The notification result type is \ref Pmp_FifoId_t. + */ +void Fifo_AddStateObserver(CPmFifo *self, CObserver *obs_ptr) +{ + (void)Sub_AddObserver(&self->sync_state_subject, obs_ptr); +} + +/*! \brief Removes an observer of synchronization state changes + * \param self The instance + * \param obs_ptr The observer. + */ +void Fifo_RemoveStateObserver(CPmFifo *self, CObserver *obs_ptr) +{ + (void)Sub_RemoveObserver(&self->sync_state_subject, obs_ptr); +} + +/*! \brief Stops execution of a FIFO and notifies sync lost if necessary + * \param self The instance + * \param new_state The new synchronization state + * \param allow_notification Set to \c false in order to avoid recursion + */ +void Fifo_Stop(CPmFifo *self, Fifo_SyncState_t new_state, bool allow_notification) +{ + bool notify = false; + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Stop(): FIFO: %u, state: %u, new_state: %u", 3U, self->config.fifo_id, self->sync_state, new_state)); + + if (self->sync_state != new_state) + { + notify = true; + } + + self->sync_state = new_state; + self->tx.credits = 0U; + + if (self->wd.timer_value != 0U) + { + Tm_ClearTimer(&self->init.base_ptr->tm, &self->wd.timer); + } + + if ((notify != false) && (allow_notification != false)) + { + Sub_Notify(&self->sync_state_subject, &self->config.fifo_id); + } +} + +/*! \brief Releases all external references + * \details It is important to call Fifo_Stop() prior to this functions. The low-level driver + * must be stopped as well to avoid concurrent access to message objects. + * \param self The instance + */ +void Fifo_Cleanup(CPmFifo *self) +{ + CMessage *msg_ptr = NULL; + CDlNode *node_ptr = NULL; + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (self->sync_state == FIFO_S_UNSYNCED_INIT)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Cleanup(): FIFO: %u", 1U, self->config.fifo_id)); + + /* cleanup pending queue */ + for (node_ptr = Dl_PopHead(&self->tx.pending_q); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.pending_q)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + Msg_NotifyTxStatus(msg_ptr, MNS_MSG_STAT_ERROR_SYNC); + Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(msg_ptr)); + Msg_SetLldHandle(msg_ptr, NULL); /* remove link to LLD message object */ + } + + /* cleanup waiting queue */ + for (node_ptr = Dl_PopHead(&self->tx.waiting_queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.waiting_queue)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + Msg_NotifyTxStatus(msg_ptr, MNS_MSG_STAT_ERROR_SYNC); + } + + /* cleanup Rx queue */ + for (node_ptr = Dl_PopHead(&self->rx.queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->rx.queue)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr); + } + + Srv_ClearEvent(&self->service, FIFO_SE_ALL); +} + + +/*! \brief Service function of FIFO + * \details The processing order of Rx followed by Tx is important for Fifo_RxProcessCommand() + * \param self The instance + */ +static void Fifo_Service(void *self) +{ + CPmFifo *self_ = (CPmFifo*)self; + Srv_Event_t event_mask; + + Srv_GetEvent(&self_->service, &event_mask); + + if(FIFO_SE_RX_SERVICE == (event_mask & FIFO_SE_RX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, FIFO_SE_RX_SERVICE); + Fifo_RxService(self_); + } + + if(FIFO_SE_TX_APPLY_STATUS == (event_mask & FIFO_SE_TX_APPLY_STATUS)) + { + Srv_ClearEvent(&self_->service, FIFO_SE_TX_APPLY_STATUS); + Fifo_TxApplyCurrentStatus(self_); + } + + if(FIFO_SE_TX_SERVICE == (event_mask & FIFO_SE_TX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, FIFO_SE_TX_SERVICE); + Fifo_TxService(self_); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Enqueues a message for transmission + * \param self The instance + * \param msg_ptr The Tx message object + * \param bypass Use \c true if the message shall bypass all other messages + * in the FIFO. Otherwise \c false. + */ +void Fifo_Tx(CPmFifo *self, CMessage *msg_ptr, bool bypass) +{ + uint8_t *msg_hdr_ptr = NULL; + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (msg_ptr != NULL)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Tx(): FIFO: %u, msg_ptr: 0x%p, FuncId: 0x%X, queued Tx message", 3U, self->config.fifo_id, msg_ptr, msg_ptr->pb_msg.id.function_id)); + + Msg_PullHeader(msg_ptr, self->tx.encoder_ptr->msg_hdr_sz); + msg_hdr_ptr = Msg_GetHeader(msg_ptr); + self->tx.encoder_ptr->encode_fptr(Msg_GetMostTel(msg_ptr), msg_hdr_ptr); + + if (bypass == false) + { + Dl_InsertTail(&self->tx.waiting_queue, Msg_GetNode(msg_ptr)); /* enqueue message for asynchronous transmission */ + } + else + { + Fifo_TxEnqueueBypassMsg(self, &self->tx.waiting_queue, msg_ptr); /* queue before first non-bypass message */ + } + + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); +} + +/*! \brief Enqueues a bypass message between the last bypass and the first regular message in a queue + * \param self The instance + * \param q_ptr The message queue + * \param msg_ptr The Tx message object + */ +static void Fifo_TxEnqueueBypassMsg(CPmFifo *self, CDlList *q_ptr, CMessage *msg_ptr) +{ + CDlNode *node_ptr = Dl_Foreach(q_ptr, &Fifo_FindFirstRegularMsg, NULL); /* find first "non-bypass" message */ + Msg_SetTxBypass(msg_ptr, true); /* mark new message as bypass message */ + + if (node_ptr == NULL) /* no message or only bypass messages found */ + { + Dl_InsertTail(&self->tx.waiting_queue, Msg_GetNode(msg_ptr)); /* enqueue message to tail */ + } + else /* first "non-bypass" message is found */ + { /* insert the bypass message before the first regular message found */ + Dl_InsertBefore(&self->tx.waiting_queue, node_ptr, Msg_GetNode(msg_ptr)); + } +} + +/*! \brief Required as "for-each" function to find the first "regular message" + * \param d_ptr Points to a message object in the queue + * \param ud_ptr Unused data reference, always \c NULL + * \return Returns \c true if a regular (non-bypass) message is found. + */ +static bool Fifo_FindFirstRegularMsg(void *d_ptr, void *ud_ptr) +{ + bool ret = true; + MISC_UNUSED(ud_ptr); + + if (Msg_IsTxBypass((CMessage*)d_ptr)) + { + ret = false; + } + + return ret; +} + +/*! \brief Processing of data, status and command messages + * \param self The instance + */ +static void Fifo_TxService(CPmFifo *self) +{ + Fifo_TxProcessCommand(self); + Fifo_TxProcessStatus(self); + Fifo_TxProcessData(self); +} + +/*! \brief Processing of status messages + * \param self The instance + */ +static void Fifo_TxProcessStatus(CPmFifo *self) +{ + if (Pmcmd_IsTriggered(&self->rx.status) != false) + { + if (Pmcmd_Reserve(&self->rx.status) != false) + { + Pmcmd_SetTrigger(&self->rx.status, false); + self->rx.ack_last_ok_sid = (self->rx.expected_sid - self->rx.busy_num) - 1U; + self->rx.wait_processing = false; + + if (self->rx.busy_num == 0U) /* currently no processing of data messages active */ + { /* notify the latest with SUCCESS */ + Pmcmd_UpdateContent(&self->rx.status, self->rx.expected_sid - 1U, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_SUCCESS); + } + else /* message processing is active */ + { /* notify code busy according to remaining credits */ + Pmcmd_UpdateContent(&self->rx.status, self->rx.expected_sid - self->rx.busy_num, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_BUSY); + } + + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->rx.status)); + } + } +} + +/*! \brief Processing of queued data messages + * \param self The instance + */ +static void Fifo_TxProcessData(CPmFifo *self) +{ + /* process all queued messages as long as credits are available, + * process all queued messages if FIFO is not synced + */ + while ((self->tx.cancel_all_running == false) && (self->tx.credits > 0U)) + { + CMessage *msg_ptr = NULL; + CDlNode *node_ptr = NULL; + uint8_t *msg_hdr_ptr = NULL; + Lld_IntTxMsg_t *lld_tx_ptr = NULL; + + node_ptr = Dl_PopHead(&self->tx.waiting_queue); /* get message node */ + if (NULL == node_ptr) + { + msg_ptr = NULL; /* stop processing - no further messages in queue */ + break; + } + + msg_ptr = (CMessage*)Dln_GetData(node_ptr); /* get message object */ + + if (self->sync_state != FIFO_S_SYNCED) + { + Msg_NotifyTxStatus(msg_ptr, MNS_MSG_STAT_ERROR_SYNC); /* notify sync error while not synced */ + } + else + { + lld_tx_ptr = Lldp_GetTxFromPool(&self->tx.lld_pool); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (msg_ptr != NULL)); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (lld_tx_ptr != NULL)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxProcessData(): FIFO: %u, msg_ptr: 0x%p, FuncId: 0x%X, SID: 0x%02X, queued Tx message", 4U, self->config.fifo_id, msg_ptr, msg_ptr->pb_msg.id.function_id, self->tx.sid_next_to_use)); + + Msg_SetLldHandle(msg_ptr, lld_tx_ptr); /* link message objects */ + lld_tx_ptr->msg_ptr = msg_ptr; + + Msg_PullHeader(msg_ptr, self->tx.encoder_ptr->pm_hdr_sz); /* get PM header pointer */ + msg_hdr_ptr = Msg_GetHeader(msg_ptr); + + { + uint8_t tel_length = Msg_GetMostTel(msg_ptr)->tel.tel_len; + self->tx.pm_header.pml = (Msg_GetHeaderSize(msg_ptr) + tel_length) - 2U; + } + + self->tx.pm_header.sid = self->tx.sid_next_to_use; /* assign SeqID */ + self->tx.sid_next_to_use++; + + Pmh_BuildHeader(&self->tx.pm_header, msg_hdr_ptr); /* build PM header */ + lld_tx_ptr->lld_msg.memory_ptr = Msg_GetMemTx(msg_ptr); + + Msg_SetTxActive(msg_ptr, true); + Dl_InsertTail(&self->tx.pending_q, Msg_GetNode(msg_ptr)); + + Pmch_Transmit(self->init.channel_ptr, (Mns_Lld_TxMsg_t*)(void*)lld_tx_ptr); + + self->tx.credits--; + } + } +} + +/*! \brief Processing of status messages + * \param self The instance + */ +static void Fifo_TxProcessCommand(CPmFifo *self) +{ + if (Pmcmd_IsTriggered(&self->tx.sync_cmd) != false) + { + if (Pmcmd_Reserve(&self->tx.sync_cmd) != false) + { + Pmcmd_SetTrigger(&self->tx.sync_cmd, false); + + if (self->sync_state == FIFO_S_SYNCING) + { + self->sync_cnt++; + Pmcmd_SetContent(&self->tx.sync_cmd, self->sync_cnt, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_SYNC, self->sync_params, 4U); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.sync_cmd)); + } + else if (self->sync_state == FIFO_S_UNSYNCING) + { + Pmcmd_SetContent(&self->tx.sync_cmd, 0U, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_UNSYNC, NULL, 0U); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.sync_cmd)); + } + else + { + Pmcmd_Release(&self->tx.sync_cmd); + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + } +} + +/*! \brief Releases a LLD Tx message object + * \param self The instance + * \param handle_ptr The unused LLD Tx message object + * \details If Fifo_TxApplyStatus() is waiting for a message object + * being released + */ +void Fifo_TxOnRelease(void *self, Mns_Lld_TxMsg_t *handle_ptr) +{ + CPmFifo *self_ = (CPmFifo*)self; + Lld_IntTxMsg_t *tx_ptr = (Lld_IntTxMsg_t*)(void*)handle_ptr; + + if (tx_ptr->msg_ptr != NULL) + { + Msg_SetTxActive(tx_ptr->msg_ptr, false); + } + else + { + TR_FAILED_ASSERT(self_->init.base_ptr->mns_inst_id, "[FIFO]"); + } + + if (self_->tx.status_waiting_release != false) + { + self_->tx.status_waiting_release = false; + Srv_SetEvent(&self_->service, (FIFO_SE_TX_APPLY_STATUS | FIFO_SE_TX_SERVICE)); + } +} + +/*! \brief Triggers a command CANCEL_ALL and stops further Tx processing + * \details CANCEL_ALL shall be called only, if the front-most pending message + * has followers (is segmented, i.e. \c cancel_id > 0). Use command CANCEL + * if the front-most message has no followers (\c cancel_id == NULL). + * \param self The instance + * \param failure_sid The failure sid + * \param failure_code The failure code reported by the INIC + */ +static void Fifo_TxExecuteCancelAll(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxExecuteCancelAll(): FIFO: %u, SID: %u, Code: %u", 3U, self->config.fifo_id, failure_sid, failure_code)); + + if (Pmcmd_Reserve(&self->tx.cancel_cmd) != false) /* prepare cancel command */ + { + Pmcmd_UpdateContent(&self->tx.cancel_cmd, self->tx.current_sid, + PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL_ALL); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.cancel_cmd)); + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* Unable to reserve cancel command */ + } + + self->tx.cancel_all_running = true; + self->tx.failure_sid = failure_sid; + self->tx.failure_status = failure_code; +} + +/*! \brief Shall be called if the command CANCEL_ALL was processed completely + * \param self The instance + * \details Since the CANCEL_ALL is used to cancel the front-most message and + * all of its followers (same cancel_id) + + for mid-level retries, the canceled messages + * are moved from the processing_q to the waiting_q again. The MLR timer is + * started. As soon as the timer elapses, Tx processing is continued again. + * If the front-most message has a follower id, all pending messages are + * moved to the waiting queue and all messages with the same follower id + * are notified as failed. + */ +static void Fifo_TxFinishedCancelAll(CPmFifo *self) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxFinishedCancelAll(): FIFO: %u, FailureStatus: %u,", 2U, self->config.fifo_id, self->tx.failure_status)); + + if (self->tx.failure_status != 0U) /* avoid multiple execution of the same CANCELED status */ + { /* and all of its followers */ + uint8_t follower_id = Fifo_TxPendingGetFollowerId(self); + Fifo_TxRestorePending(self); /* move remaining messages to waiting_q */ + Fifo_TxCancelFollowers(self, follower_id, (Mns_MsgTxStatus_t)self->tx.failure_status); + /* notify front-most and message and all of its followers */ + self->tx.cancel_all_running = false; /* continue with Tx processing */ + self->tx.failure_sid = 0U; + self->tx.failure_status = 0U; + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); + } +} + +/*! \brief Triggers a command CANCEL while Tx processing continues + * \param self The instance + * \param failure_sid The failure sid + * \param failure_code The failure code reported by the INIC + */ +static void Fifo_TxExecuteCancel(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxExecuteCancel(): FIFO: %u, SID: %u, Code: %u", 3U, self->config.fifo_id, failure_sid, failure_code)); + + if (Pmcmd_Reserve(&self->tx.cancel_cmd) != false) + { + Pmcmd_UpdateContent(&self->tx.cancel_cmd, self->tx.current_sid, + PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.cancel_cmd)); + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* Unable to reserve cancel command */ + } + + self->tx.cancel_all_running = false; + self->tx.failure_sid = failure_sid; + self->tx.failure_status = failure_code; +} + +/*! \brief Checks if the LLD has released all messages in the pending_q + * \param self The instance + * \return Returns \c true if all messages are released by the LLD, otherwise \c false. + */ +static bool Fifo_TxHasAccessPending(CPmFifo *self) +{ + bool ret = true; + CDlNode *node_ptr = Dl_PeekTail(&self->tx.pending_q); /* if the tail is not active, then all */ + /* pending message are not active */ + if (node_ptr != NULL) + { + CMessage *msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + if (Msg_IsTxActive(msg_ptr) != false) + { + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxHasAccessPending(): FIFO: %u, msg_ptr: 0x%p, still in use", 2U, self->config.fifo_id, msg_ptr)); + self->tx.status_waiting_release = true; + ret = false; + } + } + + return ret; +} + +/*! \brief Moves all pending messages to the waiting_q + * \details All messages from pending_q will be moved to the waiting_g and + * all consumed credits are restored. The message objects are restored + * to the queue in the same order as they have been forwarded to the LLD. + * This method is typically called to restore the waiting_q in the correct + * order before notifying a + * \param self The instance + */ +static void Fifo_TxRestorePending(CPmFifo *self) +{ + /* take tail from pending_q to the head of waiting_q */ + CMessage *msg_ptr = NULL; + CDlNode *node_ptr = NULL; + + /* cleanup pending queue */ + for (node_ptr = Dl_PopTail(&self->tx.pending_q); node_ptr != NULL; node_ptr = Dl_PopTail(&self->tx.pending_q)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxRestorePending(): FIFO: %u, msg_ptr: 0x%p", 2U, self->config.fifo_id, msg_ptr)); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (Msg_IsTxActive(msg_ptr) == false)); + + self->tx.sid_last_completed++; + self->tx.credits++; + Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(msg_ptr)); + Msg_SetLldHandle(msg_ptr, NULL); /* remove link to LLD message object */ + Msg_PushHeader(msg_ptr, self->tx.encoder_ptr->pm_hdr_sz); /* set index to position of message header */ + Dl_InsertHead(&self->tx.waiting_queue, node_ptr); /* enqueue message to waiting_q */ + } +} + +/*! \brief Retrieves the follower id of the front-most pending message + * \param self The instance + * \return Returns the follower id of the front-most pending message. + */ +static uint8_t Fifo_TxPendingGetFollowerId(CPmFifo *self) +{ + CDlNode *node_ptr; + CMessage *tx_ptr; + uint8_t ret = 0U; + + node_ptr = Dl_PeekHead(&self->tx.pending_q); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (node_ptr != NULL)); + + if (node_ptr != NULL) + { + tx_ptr = (CMessage*)Dln_GetData(node_ptr); + ret = tx_ptr->pb_msg.opts.cancel_id; + } + + return ret; +} + +/*! \brief Aborts the transmission of all messages in the waiting_q with a given follower id + * \param self The instance + * \param follower_id The follower id a message needs to have to be canceled + * \param status The transmission status that shall be notified + */ +static void Fifo_TxCancelFollowers(CPmFifo *self, uint8_t follower_id, Mns_MsgTxStatus_t status) +{ + CDlNode *node_ptr; + CDlList temp_queue; + + Dl_Ctor(&temp_queue, self->init.base_ptr->mns_inst_id); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxCancelFollowers(): FIFO: %u: FollowerId: %u", 2U, self->config.fifo_id, follower_id)); + + for (node_ptr = Dl_PopHead(&self->tx.waiting_queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.waiting_queue)) + { + CMessage *tx_ptr = (CMessage*)Dln_GetData(node_ptr); + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (Msg_GetLldHandle(tx_ptr) == NULL)); + + if (tx_ptr->pb_msg.opts.cancel_id == follower_id) + { + Msg_NotifyTxStatus(tx_ptr, status); /* notify failed transmission of message and all followers */ + } + else + { + Dl_InsertTail(&temp_queue, node_ptr); /* add to temporary queue and keep order of messages */ + } + } + + if (Dl_GetSize(&temp_queue) > 0U) /* restore temp_queue to waiting_q */ + { + Dl_AppendList(&self->tx.waiting_queue, &temp_queue);/* temp_queue will be empty now */ + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx Message Processing */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the number of (implicit) acknowledges that are related to one SID + * \param self The instance + * \param sid The sequence ID + * \return The number of implicit acknowledges that are related to the SID + */ +static uint8_t Fifo_TxGetValidAcknowledges(CPmFifo *self, uint8_t sid) +{ + uint8_t diff_s = (uint8_t)(sid - self->tx.sid_last_completed); /* number of implicit acknowledged data */ + uint8_t diff_b = (uint8_t)(self->tx.sid_next_to_use - self->tx.sid_last_completed); /* number of "sent but un-acknowledged data" + 1 */ + + if (diff_b <= diff_s) /* check valid acknowledges */ + { + diff_s = 0U; + } + + return diff_s; +} + + +/*! \brief Checks id an incoming SID of a status message is valid. + * \param self The instance + * \param sid The sequence ID + * \return Returns \c true if the SID is valid, otherwise \c false. + */ +static bool Fifo_TxIsIncomingSidValid(CPmFifo *self, uint8_t sid) +{ + bool ret = false; + uint8_t diff_s = (uint8_t)(sid - self->tx.sid_last_completed); /* number of implicit acknowledged data */ + uint8_t diff_b = (uint8_t)(self->tx.sid_next_to_use - self->tx.sid_last_completed); /* number of "sent but un-acknowledged data" + 1 */ + uint8_t diff_p = (uint8_t)(self->tx.current_sid - self->tx.sid_last_completed); /* pending/known acknowledges */ + + if (diff_b > diff_s) /* check if SID fits in valid range */ + { + if (diff_s >= diff_p) /* avoid overwriting with smaller values */ + { + ret = true; + } + } + + return ret; +} + +/*! \brief Implicitly notifies transmission status to calling classes + * \param self The instance + * \param sid The sequence ID until the status shall be notified + * \param status The status which is notified + * \return Returns \c true if all desired messages had been notified, + * otherwise \c false. + */ +static bool Fifo_TxNotifyStatus(CPmFifo *self, uint8_t sid, Mns_MsgTxStatus_t status) +{ + bool ret = true; + uint8_t acks = Fifo_TxGetValidAcknowledges(self, sid); + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, calculated_acks: %u", 2U, self->config.fifo_id, acks)); + + while (acks > 0U) + { + CDlNode *node_ptr = Dl_PopHead(&self->tx.pending_q); + + if (node_ptr != NULL) + { + CMessage *tx_ptr = (CMessage*)node_ptr->data_ptr; + + if (!Msg_IsTxActive(tx_ptr)) + { + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (tx_ptr != NULL)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, FuncId: 0x%X, notified status: %u", 3U, self->config.fifo_id, tx_ptr->pb_msg.id.function_id, status)); + Msg_NotifyTxStatus(tx_ptr, status); + Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(tx_ptr)); + Msg_SetLldHandle(tx_ptr, NULL); /* remove link to LLD message object */ + + self->tx.credits++; /* increment credits */ + self->tx.sid_last_completed++; /* update last acknowledge SID */ + } + else + { + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, LLD objects still occupied", 1U, self->config.fifo_id)); + Dl_InsertHead(&self->tx.pending_q, node_ptr); + self->tx.status_waiting_release = true; + ret = false; + break; + } + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* not yet handled */ + /* trigger sync again */ + } + + acks--; + } + + return ret; +} + +/*! \brief Updates the current Tx status with the content of a received FIFO status + * \param self The instance + * \param sid The sequence id of the FIFO status + * \param type The type of the FIFO status. Valid types are only: + * - PMP_STATUS_TYPE_FLOW + * - PMP_STATUS_TYPE_FAILURE + * \param code The code of the FIFO status + */ +static void Fifo_TxUpdateCurrentStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code) +{ + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (type == (uint8_t)PMP_STATUS_TYPE_FAILURE) || (type == (uint8_t)PMP_STATUS_TYPE_FLOW)); + if (Fifo_TxIsIncomingSidValid(self, sid)) /* is new or updating status */ + { + self->tx.current_sid = sid; /* update current status */ + self->tx.current_type = (Pmp_StatusType_t)type; + self->tx.current_code = code; + } + else + { + TR_ERROR((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxUpdateCurrentStatus(): FIFO: %u, sid: %u, type: %u, code: %u, INVALID SID", 4U, self->config.fifo_id, sid, type, code)); + } +} + +/*! \brief Analyses the current Tx status, tries to notify statuses to the transmitter and triggers + * retry/cancel actions. + * \param self The instance + */ +static void Fifo_TxApplyCurrentStatus(CPmFifo *self) +{ + if ((self->tx.cancel_all_running == false) && (self->tx.failure_status != 0U)) /* Command(CANCEL) is pending */ + { + if (Fifo_TxGetValidAcknowledges(self, self->tx.current_sid) > 1U) /* ?>=1? "single cancel" is valid and implicit */ + { + if (Fifo_TxNotifyStatus(self, self->tx.failure_sid, (Mns_MsgTxStatus_t)self->tx.failure_status)) + { + self->tx.failure_status = 0U; /* implicit canceled stops retries */ + self->tx.failure_sid = 0U; + } + } + } + + if ((self->tx.current_type == PMP_STATUS_TYPE_FAILURE) && (self->tx.status_waiting_release == false)) + { + if (self->tx.cancel_all_running == false) + { + if (Fifo_TxNotifyStatus(self, self->tx.current_sid - 1U, MNS_MSG_STAT_OK) != false) + { + /* important: failed message now is front-most message in the tx.pending_q, */ + /* any implicit acknowledge was done before */ + if (self->tx.failure_status == 0U) /* failure not yet handled - avoid multiple calls */ + { + if (Fifo_TxPendingGetFollowerId(self) == 0U) + { + Fifo_TxExecuteCancel(self, self->tx.current_sid, self->tx.current_code); /* execute simple cancel */ + } + else + { + Fifo_TxExecuteCancelAll(self, self->tx.current_sid, self->tx.current_code); /* execute cancel all */ + /* self->tx.cancel_all_running now is 'true' and Tx is stopped */ + } + } + } + } + } + + if ((self->tx.current_type == PMP_STATUS_TYPE_FLOW) && (self->tx.status_waiting_release == false)) + { + if ((uint8_t)PMP_STATUS_CODE_SUCCESS == self->tx.current_code) /* acknowledge pending messages */ + { + /* no further retries possible */ + (void)Fifo_TxNotifyStatus(self, self->tx.current_sid, MNS_MSG_STAT_OK); + } + else if ((uint8_t)PMP_STATUS_CODE_CANCELED == self->tx.current_code) + { + if (self->tx.cancel_all_running != false) + { + /* wait until the last SID is notified */ + if (self->tx.current_sid == (uint8_t)(self->tx.sid_next_to_use - (uint8_t)1U)) + { + /* cancel done if none of pending messages is active */ + if (Fifo_TxHasAccessPending(self) != false) + { + Fifo_TxFinishedCancelAll(self); + } + } + } + else if (Fifo_TxNotifyStatus(self, self->tx.current_sid, (Mns_MsgTxStatus_t)self->tx.failure_status)) + { + self->tx.failure_status = 0U; + self->tx.failure_sid = 0U; + } + } + else + { + if (Fifo_TxNotifyStatus(self, self->tx.current_sid - 1U, MNS_MSG_STAT_OK)) /* just implicitly acknowledge preceding message */ + { + if ((uint8_t)PMP_STATUS_CODE_NACK == self->tx.current_code) + { + Fifo_Stop(self, FIFO_S_UNSYNCED_INIT, true); + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + } + } + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Receives a message on the respective FIFO + * \param self The instance + * \param msg_ptr Reference to the Rx message + */ +static void Fifo_OnRx(void *self, CMessage *msg_ptr) +{ + CPmFifo *self_ = (CPmFifo*)self; + Dl_InsertTail(&self_->rx.queue, Msg_GetNode(msg_ptr)); /* enqueue in rx_queue */ + Srv_SetEvent(&self_->service, (FIFO_SE_RX_SERVICE | FIFO_SE_TX_APPLY_STATUS | FIFO_SE_TX_SERVICE)); +} + +/*! \brief Processes the Rx queue completely and triggers possible Tx events + * \param self The instance + */ +static void Fifo_RxService(CPmFifo *self) +{ + while (false == self->rx.wait_processing) /* process all Rx messages if possible */ + { + CMessage *msg_ptr; + uint8_t *header_ptr; + Pmp_MsgType_t type; + bool ok; + + bool free_msg = true; /* default: free every status or command message */ + CDlNode *node_ptr = Dl_PopHead(&self->rx.queue); + + if (NULL == node_ptr) + { + msg_ptr = NULL; /* stop processing - no further messages in queue */ + break; + } + + msg_ptr = (CMessage*)node_ptr->data_ptr; + header_ptr = Msg_GetHeader(msg_ptr); + type = Pmp_GetMsgType(header_ptr); + ok = Pmp_VerifyHeader(header_ptr, MSG_SIZE_RSVD_BUFFER); + + if (ok != false) + { + switch (type) + { + case PMP_MSG_TYPE_CMD: + Fifo_RxProcessCommand(self, msg_ptr); + break; + case PMP_MSG_TYPE_STATUS: + Fifo_RxProcessStatus(self, msg_ptr); + break; + case PMP_MSG_TYPE_DATA: + free_msg = Fifo_RxProcessData(self, msg_ptr); /* important: message can be freed */ + break; /* synchronously */ + default: + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* unknown FIFO message type */ + break; + } + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* invalid message header */ + } + + if (free_msg != false) + { + Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr); + } + } +} + +/*! \brief Evaluates the trigger condition to transmit a Rx status + * \details Needs to be called before and after processing Rx data messages + * \param self The instance + */ +static void Fifo_RxCheckStatusTrigger(CPmFifo *self) +{ + /* calculate the number of credits the INIC has consumed */ + /* if less messages are processing, the freed can be acknowledged */ + uint8_t consumed_inic_credits = (self->rx.expected_sid - self->rx.ack_last_ok_sid) - 1U; + uint8_t possible_acks = consumed_inic_credits - self->rx.busy_num; + + if ((consumed_inic_credits >= self->rx.ack_threshold) && (possible_acks > 0U)) + { + if (Pmcmd_IsTriggered(&self->rx.status) == false) + { + Pmcmd_SetTrigger(&self->rx.status, true); /* INIC might run out of credits */ + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); + } + } +} + +/*! \brief This function shall be called before processing a valid FIFO data message + * \param self The instance + */ +static void Fifo_RxGetCredit(CPmFifo *self) +{ + self->rx.busy_num++; + Fifo_RxCheckStatusTrigger(self); +} + +/*! \brief This function shall be called after processing a valid FIFO data message + * \details It is important to call this function after the message object is freed, + * so that the flow control can be updated. + * \param self The instance + */ +static void Fifo_RxReleaseCredit(CPmFifo *self) +{ + self->rx.busy_num--; + Fifo_RxCheckStatusTrigger(self); +} + +/*! \brief Releases a FIFO data message which was received and forwarded by the FIFO + * \details The function returns the message to the channel's Rx message pool and + * has to update the number of credits (processing handles). + * A FIFO data message is initially allocated from the channel's Rx message pool. + * When processing the handle the determined FIFO need to calculate the amount of + * credits. When freeing the message the handle needs to be returned to the channel's + * Rx pool again and the FIFO needs to refresh the status and credits calculation. + * Therefore the message has to be freed to the respective FIFO again. + * \param self The instance + * \param msg_ptr The Rx data message + */ +void Fifo_RxReleaseMsg(CPmFifo *self, CMessage *msg_ptr) +{ + Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr); + Fifo_RxReleaseCredit(self); +} + +/*! \brief Processes an Rx data message + * \param self The instance + * \param msg_ptr The Rx data message + * \return \c true if the message object is no longer needed. + * Otherwise \c false. + */ +static bool Fifo_RxProcessData(CPmFifo *self, CMessage *msg_ptr) +{ + bool free_msg = true; + uint8_t content_header_sz = 0U; + uint8_t sid = 0U; + uint8_t *header_ptr = Msg_GetHeader(msg_ptr); + sid = Pmp_GetSid(header_ptr); + + if (self->sync_state != FIFO_S_SYNCED) + { /* discard Rx messages while FIFO is not synced */ + TR_ERROR((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessData(): FIFO: %u, state: %u, discards Rx message with SID=0x%02X while not synced (warning)", 3U, self->config.fifo_id, self->sync_state, sid)); + } + else if (sid == self->rx.expected_sid) /* check if SID is ok */ + { + uint8_t pm_header_sz = Pmp_GetPmhl(header_ptr) + 3U; + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (pm_header_sz == self->rx.encoder_ptr->pm_hdr_sz)); + + self->rx.expected_sid++; /* update SID */ + content_header_sz = self->rx.encoder_ptr->msg_hdr_sz; + + /* parasoft suppress item MISRA2004-17_4 reason "necessary offset usage" */ + self->rx.encoder_ptr->decode_fptr(Msg_GetMostTel(msg_ptr), &(header_ptr[pm_header_sz])); + /* parasoft unsuppress item MISRA2004-17_4 reason "necessary offset usage" */ + + Msg_ReserveHeader(msg_ptr, content_header_sz + pm_header_sz); + Msg_PullHeader(msg_ptr, content_header_sz + pm_header_sz); + + if (Msg_VerifyContent(msg_ptr)) + { + if (self->rx.on_complete_fptr != NULL) + { + (void)Fifo_RxGetCredit(self); + free_msg = false; /* callback is responsible to free the message */ + self->rx.on_complete_fptr(self->rx.on_complete_inst, msg_ptr); + /* Fifo_RxReleaseCredit() is called when message is freed */ + } + } + } + else + { + TR_ERROR((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessData(): FIFO: %u, state: %u, discards Rx message with unexpected SID=0x%02X (warning)", 3U, self->config.fifo_id, self->sync_state, sid)); + } + + return free_msg; +} + +/*! \brief Processes an Rx status message + * \param self The instance + * \param msg_ptr The Rx status message + */ +static void Fifo_RxProcessStatus(CPmFifo *self, CMessage *msg_ptr) +{ + CPmh pm_header; + uint8_t current_sid; + uint8_t current_type; + uint8_t current_code; + uint8_t *header_ptr = Msg_GetHeader(msg_ptr); + + Pmh_DecodeHeader(&pm_header, header_ptr); + current_sid = pm_header.sid; + current_type = (uint8_t)Pmh_GetExtStatusType(&pm_header); + current_code = (uint8_t)Pmh_GetExtStatusCode(&pm_header); + + self->wd.request_started = false; /* status finishes a wd request */ + + switch ((Pmp_StatusType_t)current_type) + { + case PMP_STATUS_TYPE_FAILURE: + Fifo_TxUpdateCurrentStatus(self, current_sid, current_type, Fifo_RxCheckFailureCode(self, current_code)); /* just update status type FAILURE */ + break; + case PMP_STATUS_TYPE_FLOW: + Fifo_TxUpdateCurrentStatus(self, current_sid, current_type, current_code); /* just update status type FLOW (codes: BUSY, NACK, SUCCESS, CANCELED) */ + break; + case PMP_STATUS_TYPE_SYNCED: + Fifo_RxProcessSyncStatus(self, current_sid, current_type, current_code, header_ptr); + break; + case PMP_STATUS_TYPE_UNSYNCED_BSY: + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessStatus(): FIFO: %u, state: %u, received UNSYNCED_BSY", 2U, self->config.fifo_id, self->sync_state)); + if (self->sync_state != FIFO_S_SYNCING) + { + Fifo_Stop(self, FIFO_S_UNSYNCED_BUSY, true); + } + break; + case PMP_STATUS_TYPE_UNSYNCED_RDY: + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessStatus(): FIFO: %u, state: %u, received UNSYNCED_RDY", 2U, self->config.fifo_id, self->sync_state)); + if (self->sync_state == FIFO_S_SYNCING) + { + if (current_code == (uint8_t)PMP_UNSYNC_R_COMMAND) + { + Fifo_Synchronize(self); /* retry synchronization */ + } + } + else + { + Fifo_Stop(self, FIFO_S_UNSYNCED_READY, true); + } + break; + default: + /* ignore status */ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessStatus(): FIFO: %u, state: %u, received UNKNOWN TYPE: %u", 3U, self->config.fifo_id, self->sync_state, current_type)); + break; + } +} + +/*! \brief Checks failure_code and sets invalid code to MNS_MSG_STAT_ERROR_UNKNOWN + * \param self The instance + * \param failure_code The INIC failure code + * \return Returns the checked failure code + */ +static uint8_t Fifo_RxCheckFailureCode(CPmFifo *self, uint8_t failure_code) +{ + uint8_t ret; + MISC_UNUSED(self); + + switch (failure_code) + { + case (uint8_t)MNS_MSG_STAT_ERROR_CFG_NO_RCVR: + case (uint8_t)MNS_MSG_STAT_ERROR_BF: + case (uint8_t)MNS_MSG_STAT_ERROR_CRC: + case (uint8_t)MNS_MSG_STAT_ERROR_ID: + case (uint8_t)MNS_MSG_STAT_ERROR_ACK: + case (uint8_t)MNS_MSG_STAT_ERROR_TIMEOUT: + case (uint8_t)MNS_MSG_STAT_ERROR_FATAL_WT: + case (uint8_t)MNS_MSG_STAT_ERROR_FATAL_OA: + case (uint8_t)MNS_MSG_STAT_ERROR_NA_TRANS: + case (uint8_t)MNS_MSG_STAT_ERROR_NA_OFF: + ret = failure_code; + break; + default: + ret = (uint8_t)MNS_MSG_STAT_ERROR_UNKNOWN; + break; + } + + return ret; +} + +/*! \brief Processes an Rx command message + * \param self The instance + * \param msg_ptr The Rx command message + */ +static void Fifo_RxProcessCommand(CPmFifo *self, CMessage *msg_ptr) +{ + MISC_UNUSED(msg_ptr); + /* be aware that PMHL might vary */ + Pmcmd_SetTrigger(&self->rx.status, true); /* just trigger latest Rx status now */ +} + +/*! \brief Processes a status SYNCED from the INIC + * \param self The instance + * \param sid The sid of the sync status + * \param type The type of the sync status + * \param code The code of the sync status + * \param header_ptr Pointer to the raw port message + * \return The current synchronization state + */ +static void Fifo_RxProcessSyncStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code, uint8_t *header_ptr) +{ + bool check = false; + uint8_t tx_credits = 0U; + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (type==(uint8_t)PMP_STATUS_TYPE_SYNCED)); + MISC_UNUSED(type); + MISC_UNUSED(code); + + if (Pmp_GetDataSize(header_ptr) == 4U) + { + tx_credits = Pmp_GetData(header_ptr, 0U) & (uint8_t)PMP_CREDITS_MASK; + + if ((tx_credits >= PMP_CREDITS_MIN) && + (Pmp_GetData(header_ptr, 1U) == self->sync_params[1]) && + (Pmp_GetData(header_ptr, 2U) == self->sync_params[2]) && + (Pmp_GetData(header_ptr, 3U) == self->sync_params[3]) && + (sid == (self->sync_cnt))) + { + check = true; /* the sync status parameters are correct */ + } + } + + if ((check != false) && (self->sync_state == FIFO_S_SYNCING)) + { + Fifo_InitCounters(self, sid, tx_credits); /* values are incremented on each sync attempt */ + self->sync_state = FIFO_S_SYNCED; /* sync status shall have 4 bytes message body */ + self->rx.wait_processing = false; + Fifo_TxStartWatchdog(self); + Sub_Notify(&self->sync_state_subject, &self->config.fifo_id); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Synchronization */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Synchronizes the FIFO + * \param self The instance + */ +void Fifo_Synchronize(CPmFifo *self) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Synchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state)); + self->sync_state = FIFO_S_SYNCING; + Pmcmd_SetTrigger(&self->tx.sync_cmd, true); + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); +} + +/*! \brief Un-synchronizes the FIFO + * \param self The instance + */ +void Fifo_Unsynchronize(CPmFifo *self) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Unsynchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state)); + if ( (self->sync_state == FIFO_S_SYNCING) || (self->sync_state == FIFO_S_SYNCED) ) + { + self->sync_state = FIFO_S_UNSYNCING; + Pmcmd_SetTrigger(&self->tx.sync_cmd, true); + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); + } +} + +/*! \brief Retrieves the current synchronization state + * \param self The instance + * \return The current synchronization state + */ +Fifo_SyncState_t Fifo_GetState(CPmFifo *self) +{ + return self->sync_state; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Watchdog */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Starts the watchdog handling + * \param self The instance + */ +static void Fifo_TxStartWatchdog(CPmFifo *self) +{ + self->wd.request_started = false; + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxStartWatchdog(): fifo_id: %u, timeout: %u", 2U, self->config.fifo_id, self->wd.timer_value)); + + if (self->wd.timer_value != 0U) + { + Tm_SetTimer(&self->init.base_ptr->tm, &self->wd.timer, &Fifo_TxOnWatchdogTimer, + self, + self->wd.timer_value, + self->wd.timer_value + ); + } +} + +/*! \brief Callback function which is invoked if the watchdog timer expires + * \param self The instance + */ +static void Fifo_TxOnWatchdogTimer(void *self) +{ + CPmFifo *self_ = (CPmFifo*)self; + + TR_INFO((self_->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxOnWatchdogTimer(): FIFO: %u, state: %u", 2U, self_->config.fifo_id, self_->sync_state)); + + if (self_->wd.request_started == false) + { + if (Pmcmd_Reserve(&self_->wd.wd_cmd) != false) + { + self_->wd.request_started = true; /* indicate that a status is expected */ + Pmcmd_UpdateContent(&self_->wd.wd_cmd, self_->tx.sid_next_to_use - 1U, PMP_CMD_TYPE_REQ_STATUS, PMP_CMD_CODE_REQ_STATUS); + Pmch_Transmit(self_->init.channel_ptr, Pmcmd_GetLldTxObject(&self_->wd.wd_cmd)); + } + else + { + TR_ERROR((self_->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Unable to reserve watchdog command ", 0U)); + Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true); + } + } + else /* status not received in time - notify communication error */ + { + TR_ERROR((self_->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Missing response on status request", 0U)); + Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifo.h b/mnsl/mns_pmfifo.h new file mode 100644 index 0000000..3c9962f --- /dev/null +++ b/mnsl/mns_pmfifo.h @@ -0,0 +1,232 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of Port Message FIFO + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +#ifndef MNS_PMFIFO_H +#define MNS_PMFIFO_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_base.h" +#include "mns_lld_pb.h" +#include "mns_message.h" +#include "mns_encoder.h" +#include "mns_pmp.h" +#include "mns_lldpool.h" +#include "mns_pmchannel.h" +#include "mns_pmcmd.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Number of LLD Tx handles dedicated to each FIFO */ +#define FIFO_TX_HANDLES 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function which is invoked when receiving an Rx message + * \param self The Instance (of the host) + * \param msg_ptr The Rx message + */ +typedef void (*Fifo_OnRxMsg_t)(void *self, CMessage *msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of class Port Message FIFO */ +typedef struct Fifo_InitData_ +{ + CBase *base_ptr; /*!< \brief Reference to base module */ + CPmChannel *channel_ptr; /*!< \brief Points to channel object which is needed to communicate with + * the driver */ + IEncoder *tx_encoder_ptr; /*!< \brief Encoder for Tx messages */ + IEncoder *rx_encoder_ptr; /*!< \brief Encoder for Rx messages */ + Fifo_OnRxMsg_t rx_cb_fptr; /*!< \brief Callback function invoked for Rx */ + void *rx_cb_inst; /*!< \brief Instance which is referred when invoking rx_cb_fptr */ + +} Fifo_InitData_t; + +/*! \brief Initialization structure of class Port Message FIFO */ +typedef struct Fifo_Config_ +{ + Pmp_FifoId_t fifo_id; /*!< \brief Identifier of message FIFO. + * \details It is required that the fifo_id has the same value as + * specified in PMP. + */ + uint8_t rx_credits; /*!< \brief Number of Rx credits, i.e. reserved Rx messages */ + uint8_t rx_threshold; /*!< \brief Number of Rx credits which are acknowledged in a single status. + * \details The value needs to be smaller or equal than \c rx_credits. + * Valid values are: + * - 0,1: Single message acknowledge + * - 2..rx_credits: Implicit acknowledge is triggered after + * the specified number of messages. + */ + uint8_t tx_wd_timeout; /*!< \brief Idle timeout in x100ms. Formerly known as watchdog timeout */ + uint16_t tx_wd_timer_value; /*!< \brief Timer value used to trigger the watchdog in ms */ + uint8_t rx_ack_timeout; /*!< \brief Rx status timeout in x100ms. */ + uint8_t rx_busy_allowed; /*!< \brief Number of allowed RxStatus busy responds. 0..14, or 0xF (infinite) */ + +} Fifo_Config_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The synchronization status */ +typedef enum Fifo_SyncState_ +{ + FIFO_S_UNSYNCED_INIT, + FIFO_S_SYNCING, + FIFO_S_UNSYNCED_BUSY, + FIFO_S_UNSYNCED_READY, + FIFO_S_SYNCED, + FIFO_S_UNSYNCING + +} Fifo_SyncState_t; + +/*! \brief The class CPmFifo*/ +typedef struct CPmFifo_ +{ + Fifo_InitData_t init; /*!< \brief Initialization data */ + Fifo_Config_t config; /*!< \brief Configuration data */ + + CService service; /*!< \brief Service object */ + Fifo_SyncState_t sync_state; /*!< \brief Synchronization state of the FIFO */ + CSubject sync_state_subject; /*!< \brief Notification of changed synchronization state */ + uint8_t sync_params[4]; /*!< \brief Synchronization parameters */ + uint8_t sync_cnt; /*!< \brief Counts the number of synchronization attempts */ + + struct CPmFifo_wd_ + { + CTimer timer; /*!< \brief The timer object */ + CPmCommand wd_cmd; /*!< \brief The watchdog command message */ + uint16_t timer_value; /*!< \brief The internal timer value used by PMC to trigger the watchdog */ + bool request_started; /*!< \brief Is used to check if the INIC responds with a status before the + * next Cmd.REQUEST_STATUS is triggered. + */ + } wd; + + struct CPmFifo_rx_ + { + CDlList queue; /*!< \brief Message queue containing all incoming messages */ + + IEncoder *encoder_ptr; /*!< \brief Encoder for Rx messages */ + Fifo_OnRxMsg_t on_complete_fptr; /*!< \brief Callback function invoked for Rx */ + void *on_complete_inst; /*!< \brief Instance which is referred when invoking rx_cb_fptr */ + + uint8_t ack_threshold; /*!< \brief Number of unacknowledged Rx credits */ + uint8_t ack_last_ok_sid; /*!< \brief Latest SID which was acknowledged with "success" */ + uint8_t expected_sid; /*!< \brief The next expected Rx message SeqId */ + uint8_t busy_num; /*!< \brief The number of currently processing data messages */ + + bool wait_processing; /*!< \brief If set: Wait until transmission of e.g. NACK has finished + * before continuing with further Rx message processing. + * The flag is used if a status must be sent explicitly. + */ + CPmCommand status; /*!< \brief Rx status channel control */ + + } rx; + + struct CPmFifo_tx_ + { + CDlList waiting_queue; /*!< \brief Queue containing all outgoing messages */ + CDlList pending_q; /*!< \brief Queue containing all messages waiting for Tx status */ + IEncoder *encoder_ptr; /*!< \brief Encoder for Tx messages */ + uint8_t credits; /*!< \brief Remaining Tx credits */ + + CLldPool lld_pool; /*!< \brief Pool of LLD Tx messages, used for data messages */ + + CPmh pm_header; /*!< \brief Temporary header which is used to build the FIFO data messages*/ + CPmCommand cancel_cmd; /*!< \brief Tx cancel command message */ + CPmCommand sync_cmd; /*!< \brief Sync command message */ + + uint8_t sid_next_to_use; /*!< \brief SID that shall be used for the next transmission */ + uint8_t sid_last_completed; /*!< \brief Latest SID that was acknowledged by the INIC */ + uint8_t current_sid; /*!< \brief Tracks the latest valid FIFO status SID received from the INIC */ + Pmp_StatusType_t current_type; /*!< \brief Tracks the latest valid FIFO status type received from the INIC */ + uint8_t current_code; /*!< \brief Tracks the latest valid FIFO status code received from the INIC */ + + bool status_waiting_release; /*!< \brief Is \c true if status notification wasn't completed due to messages + * which are not yet released by the LLD. + */ + bool cancel_all_running; /*!< \brief Is \c true during pending command CANCEL_ALL. This command is required + * if the front-most message is segmented which requires to discard all + * belonging segments (same \c cancel_id) after the CANCEL_ALL was completed. + */ + uint8_t failure_status; /*!< \brief Stores the Tx status until the message is canceled */ + uint8_t failure_sid; /*!< \brief Stores the SID of the last cancelled data message */ + } tx; + +} CPmFifo; + + +/*------------------------------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Fifo_Ctor(CPmFifo *self, const Fifo_InitData_t *init_ptr, const Fifo_Config_t *config_ptr); +extern void Fifo_Stop(CPmFifo *self, Fifo_SyncState_t new_state, bool allow_notification); +extern void Fifo_Cleanup(CPmFifo *self); + +extern void Fifo_Synchronize(CPmFifo *self); +extern void Fifo_Unsynchronize(CPmFifo *self); +extern Fifo_SyncState_t Fifo_GetState(CPmFifo *self); +extern void Fifo_AddStateObserver(CPmFifo *self, CObserver *obs_ptr); +extern void Fifo_RemoveStateObserver(CPmFifo *self, CObserver *obs_ptr); + +/* Rx interface */ +extern void Fifo_RxReleaseMsg(CPmFifo *self, CMessage *msg_ptr); + +/* Tx interface */ +extern void Fifo_Tx(CPmFifo *self, CMessage *msg_ptr, bool bypass); +extern void Fifo_TxOnRelease(void *self, Mns_Lld_TxMsg_t *handle_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMFIFO_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifos.c b/mnsl/mns_pmfifos.c new file mode 100644 index 0000000..e82b837 --- /dev/null +++ b/mnsl/mns_pmfifos.c @@ -0,0 +1,447 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 class CPmFifos + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMFIFOS + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmfifos.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The initialization value of sync_count. It is incremented for each sync or un-sync attempt. */ +static const uint8_t FIFOS_SYNC_CNT_INITIAL = 0xFFU; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Fifos_Cleanup(CPmFifos *self); +static void Fifos_OnSyncTimeout(void *self); +static void Fifos_OnUnsyncTimeout(void *self); +static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr); + +static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id); +static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CPmFifos + * \param self The instance + * \param base_ptr Reference to basic services + * \param channel_ptr Reference to the port message channel + * \param icm_fifo_ptr Reference to ICM FIFO, or NULL. + * \param mcm_fifo_ptr Reference to MCM FIFO, or NULL. + * \param rcm_fifo_ptr Reference to RCM FIFO, or NULL. + * \details At least one FIFO (MCM or ICM) must be provided. + */ +void Fifos_Ctor(CPmFifos *self, CBase *base_ptr, CPmChannel *channel_ptr, CPmFifo *icm_fifo_ptr, CPmFifo *mcm_fifo_ptr, CPmFifo *rcm_fifo_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->base_ptr = base_ptr; + self->channel_ptr = channel_ptr; + self->state = FIFOS_S_UNSYNCED; + + self->unsync_initial = false; + Fifos_ConfigureSyncParams(self, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); + + self->fifos[PMP_FIFO_ID_ICM] = icm_fifo_ptr; + self->fifos[PMP_FIFO_ID_RCM] = rcm_fifo_ptr; + self->fifos[PMP_FIFO_ID_MCM] = mcm_fifo_ptr; + + T_Ctor(&self->init_timer); + Sub_Ctor(&self->event_subject, self->base_ptr->mns_inst_id); + Obs_Ctor(&self->obs_icm, self, &Fifos_OnFifoEvent); + Obs_Ctor(&self->obs_rcm, self, &Fifos_OnFifoEvent); + Obs_Ctor(&self->obs_mcm, self, &Fifos_OnFifoEvent); + + Pmcmd_Ctor(&self->cmd, PMP_FIFO_ID_ALL, PMP_MSG_TYPE_CMD); + + TR_ASSERT(self->base_ptr->mns_inst_id, "[FIFOS]", (!((icm_fifo_ptr == NULL) && (mcm_fifo_ptr == NULL)))); + + if (icm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(icm_fifo_ptr, &self->obs_icm); + } + + if (rcm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(rcm_fifo_ptr, &self->obs_rcm); + } + + if (mcm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(mcm_fifo_ptr, &self->obs_mcm); + } + + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Ctor(): FIFOS created, state %d", 1U, self->state)); +} + +/*! \brief Adds an observer of synchronization events + * \param self The instance + * \param obs_ptr The observer. The notification result type is Fifos_Event_t. + */ +void Fifos_AddEventObserver(CPmFifos *self, CObserver *obs_ptr) +{ + TR_ASSERT(self->base_ptr->mns_inst_id, "[FIFOS]", (obs_ptr != 0)); + (void)Sub_AddObserver(&self->event_subject, obs_ptr); +} + +/*! \brief Removes an observer of synchronization events + * \param self The instance + * \param obs_ptr The observer. + */ +void Fifos_RemoveEventObserver(CPmFifos *self, CObserver *obs_ptr) +{ + TR_ASSERT(self->base_ptr->mns_inst_id, "[FIFOS]", (obs_ptr != 0)); + (void)Sub_RemoveObserver(&self->event_subject, obs_ptr); +} + +/*! \brief Forces all FIFOs to state UNSYNCED without waiting for INIC responses and + * without throwing events + * \details Stops the LLD interface and releases all pending message resources. + * This function shall be called if the MNS requires a un-normal termination + * which is not detected by port message protocol. + * \param self The instance + */ +void Fifos_ForceTermination(CPmFifos *self) +{ + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_ForceTermination(): Termination started, state: %d", 1U, self->state)); + Fifos_Cleanup(self); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_ForceTermination(): Termination done, state: %d", 1U, self->state)); +} + +/*! \brief Configures retries and timeout for synchronize or un-synchronize + * operation + * \details This method shall be called before starting a synchronization or un-synchronization + * or after it has finished. The current counter of synchronization attempts is reset. + * \param self The instance + * \param retries The number of retries until event FIFOS_EV_SYNC_FAILED or + * FIFOS_EV_UNSYNC_FAILED will be notified + * \param timeout The timeout in milliseconds when the retry is performed + */ +void Fifos_ConfigureSyncParams(CPmFifos *self, uint8_t retries, uint16_t timeout) +{ + self->cmd_retries = retries; + self->cmd_timeout = timeout; + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Synchronization */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes all port message FIFOs + * \details Possible results of the operation are the following events which are fired + * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t. + * - \ref FIFOS_EV_SYNC_ESTABLISHED + * - \ref FIFOS_EV_SYNC_FAILED + * \param self The instance + * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic + * retries will be done after the first synchronization timeout. + * \param force_sync If \c true the method will also trigger the synchronization of already + * synced \ref CPmFifo objects. + */ +void Fifos_Synchronize(CPmFifos *self, bool reset_cnt, bool force_sync) +{ + uint8_t cnt; + self->state = FIFOS_S_SYNCING; + self->unsync_initial = false; + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Synchronize(): Synchronization started, state: %d", 1U, self->state)); + + if (reset_cnt) + { + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; + } + + self->sync_cnt++; + Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */ + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + if (force_sync || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_SYNCED)) + { + Fifo_Synchronize(self->fifos[cnt]); + } + } + } + + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnSyncTimeout, self, + self->cmd_timeout, 0U); +} + +/*! \brief Un-initializes all port message FIFOs + * \details Possible results of the operation are the following events which are fired + * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t. + * - \ref FIFOS_EV_UNSYNC_COMPLETE + * - \ref FIFOS_EV_UNSYNC_FAILED + * \param self The instance + * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic + * retries will be done after the first synchronization timeout. + * \param initial If the un-synchronization shall be executed prior to a initial synchronization + * it is recommended to set the argument to \c true. After notifying the event + * FIFOS_EV_UNSYNC_COMPLETE the LLD interface will not be stopped. The subsequent + * call of Fifos_Synchronize() will not start the LLD interface un-necessarily. + * To trigger a final un-synchronization \c initial shall be set to \c false. + * I.e., FIFOS_EV_UNSYNC_COMPLETE stops the LLD interface. + */ +void Fifos_Unsynchronize(CPmFifos *self, bool reset_cnt, bool initial) +{ + self->state = FIFOS_S_UNSYNCING; + self->unsync_initial = initial; + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Unsynchronize(): Un-synchronization started, state: %d", 1U, self->state)); + + if (reset_cnt) + { + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; + } + + self->sync_cnt++; + Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */ + + if (Pmcmd_Reserve(&self->cmd)) + { + Pmcmd_SetContent(&self->cmd, 0xFFU, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_UNSYNC, NULL, 0U); + + Pmch_Transmit(self->channel_ptr, Pmcmd_GetLldTxObject(&self->cmd)); + } + + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnUnsyncTimeout, self, + self->cmd_timeout, 0U); +} + +/*! \brief Handles the synchronization timeout + * \param self The instance + */ +static void Fifos_OnSyncTimeout(void *self) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_Event_t the_event = FIFOS_EV_SYNC_FAILED; + + self_->state = FIFOS_S_UNSYNCED; + + TR_INFO((self_->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_OnSyncTimeout(): state: %d", 1U, self_->state)); + + if (self_->sync_cnt < self_->cmd_retries) + { + Fifos_Synchronize(self_, false, false); /* retry synchronization after first timeout */ + } + else + { + Fifos_Cleanup(self_); + Sub_Notify(&self_->event_subject, &the_event); + } +} + +/*! \brief Handles the un-synchronization timeout + * \param self The instance + */ +static void Fifos_OnUnsyncTimeout(void *self) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_Event_t the_event = FIFOS_EV_UNSYNC_FAILED; + + self_->state = FIFOS_S_UNSYNCED; + TR_INFO((self_->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_OnUnsyncTimeout(): state: %d", 1U, self_->state)); + + if (self_->sync_cnt < self_->cmd_retries) + { + Fifos_Unsynchronize(self_, false, self_->unsync_initial); /* retry synchronization after first timeout */ + } + else + { + self_->unsync_initial = false; /* un-sync timeout will lead to termination - stop LLD */ + Fifos_Cleanup(self_); + Sub_Notify(&self_->event_subject, &the_event); +} +} + +/*! \brief Performs a cleanup of the Port Message Channel and the dedicated FIFOs + * \details Releases all message objects which are currently in use. + * \param self The instance + */ +static void Fifos_Cleanup(CPmFifos *self) +{ + uint8_t count; + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup started", 0U)); + + if (self->unsync_initial == false) + { + Pmch_Uninitialize(self->channel_ptr); + } + + for (count = 0U; count < PMP_MAX_NUM_FIFOS; count++) /* stop & cleanup all FIFOs */ + { + if (self->fifos[count] != NULL) + { /* stop and avoid recursion */ + Fifo_Stop(self->fifos[count], FIFO_S_UNSYNCED_INIT, false); + Fifo_Cleanup(self->fifos[count]); + } + } + + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup completed", 0U)); + + /* notify external event after message objects were released */ + self->state = FIFOS_S_UNSYNCED; +} + +/*------------------------------------------------------------------------------------------------*/ +/* FIFO observation */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Notifies an event to the host class + * \param self The instance + * \param fifo_id_ptr Specific event identifier, pointer to "fifo_id" + */ +static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_HandleFifoStateChange(self_, *((Pmp_FifoId_t*)fifo_id_ptr)); +} + +/*! \brief Executes transition to new synchronization states + * \param self The instance + * \param fifo_id The FIFO identifier + */ +static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id) +{ + Fifos_Event_t the_event; + + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): FIFOs state: %d, FIFO: %d, FIFO State: %d", 3U, + self->state, fifo_id, Fifo_GetState(self->fifos[fifo_id]))); + + switch (self->state) + { + case FIFOS_S_SYNCING: + if (Fifos_AreAllFifosInState(self, FIFO_S_SYNCED)) + { + self->state = FIFOS_S_SYNCED; /* now the complete channel is synced */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + Fifos_ConfigureSyncParams(self, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT); + the_event = FIFOS_EV_SYNC_ESTABLISHED; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_UNSYNCING: + if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY)) + { + Fifos_Cleanup(self); + self->state = FIFOS_S_UNSYNCED; /* now the complete channel is un-synced */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + the_event = FIFOS_EV_UNSYNC_COMPLETE; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Un-synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_SYNCED: + if (!Fifos_AreAllFifosInState(self, FIFO_S_SYNCED)) + { + self->state = FIFOS_S_UNSYNCING; /* set state to 'unsyncing' and wait until all FIFOs are unsynced */ + self->sync_cnt = 0U; /* pretend having triggered an un-sync which starts the timer */ + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnUnsyncTimeout, self, + FIFOS_UNSYNC_TIMEOUT, 0U); + the_event = FIFOS_EV_SYNC_LOST; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Lost synchronization of Port Message channel", 0U)); + } + if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY)) + { + Fifos_Cleanup(self); + self->state = FIFOS_S_UNSYNCED; /* the complete channel suddenly goes unsynced_complete */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + the_event = FIFOS_EV_UNSYNC_COMPLETE; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Sudden un-synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_UNSYNCED: + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFO event in state unsynced", 0U)); + break; + + default: + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFOs state", 0U)); + break; + } + + MISC_UNUSED(fifo_id); +} + +/*! \brief Helper function that evaluates if all configured FIFOs are in a given state + * \param self The instance + * \param target_state The required state that is evaluated for all FIFOs + * \return \c true if all FIFOs are in the given \c target_state, otherwise \c false. + */ +static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state) +{ + bool ret = true; + uint8_t cnt; + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + Fifo_SyncState_t state = Fifo_GetState(self->fifos[cnt]); + + if (state != target_state) + { + ret = false; + } + } + } + + return ret; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifos.h b/mnsl/mns_pmfifos.h new file mode 100644 index 0000000..814b057 --- /dev/null +++ b/mnsl/mns_pmfifos.h @@ -0,0 +1,131 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of class CPmFifos + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMFIFOS + * @{ + */ + +#ifndef MNS_PMFIFOS_H +#define MNS_PMFIFOS_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_base.h" +#include "mns_pmfifo.h" +#include "mns_pmchannel.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define FIFOS_SYNC_TIMEOUT 50U /*!< \brief Synchronization timeout in milliseconds */ +#define FIFOS_SYNC_RETRIES 40U /*!< \brief Maximum number of synchronization retries after timeout */ +#define FIFOS_UNSYNC_TIMEOUT 200U /*!< \brief Un-synchronization timeout in milliseconds */ +#define FIFOS_UNSYNC_RETRIES 0U /*!< \brief Un-synchronization retries */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The synchronization status of all FIFOs */ +typedef enum Fifos_SyncState_ +{ + FIFOS_S_UNSYNCED, /*!< \brief Not all FIFOs are synchronized */ + FIFOS_S_SYNCING, /*!< \brief FIFOs synchronization has started */ + FIFOS_S_SYNCED, /*!< \brief All FIFOs are synchronized */ + FIFOS_S_UNSYNCING /*!< \brief FIFOs un-synchronization has started */ + +} Fifos_SyncState_t; + +/*! \brief PMS Events */ +typedef enum Fifos_Event_ +{ + FIFOS_EV_SYNC_LOST = 0, /*!< \brief Synchronization of at least one FIFO is lost */ + FIFOS_EV_SYNC_ESTABLISHED = 1, /*!< \brief Synchronization of all FIFOs is established */ + FIFOS_EV_SYNC_FAILED = 2, /*!< \brief The initial synchronization of FIFOs failed */ + FIFOS_EV_UNSYNC_COMPLETE = 3, /*!< \brief Un-synchronization of all FIFOs has succeeded */ + FIFOS_EV_UNSYNC_FAILED = 4 /*!< \brief Un-synchronization of all FIFOs has failed */ + +} Fifos_Event_t; + +/*! \brief The class CPmFifos*/ +typedef struct CPmFifos_ +{ + CBase *base_ptr; /*!< \brief Reference to base object */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + CPmChannel *channel_ptr; /*!< \brief MOST NetServices instance ID */ + + CPmFifo *fifos[PMP_MAX_NUM_FIFOS]; /*!< \brief Reference to assigned FIFOs */ + + CObserver obs_icm; /*!< \brief Observes ICM synchronization state */ + CObserver obs_rcm; /*!< \brief Observes ICM synchronization state */ + CObserver obs_mcm; /*!< \brief Observes MCM synchronization state */ + CPmCommand cmd; /*!< \brief The UNSYNC command message */ + Fifos_SyncState_t state; /*!< \brief The Overall synchronization state */ + uint8_t sync_cnt; /*!< \brief The current count of synchronization command */ + + uint8_t cmd_retries; /*!< \brief The number of sync/un-sync retries */ + uint16_t cmd_timeout; /*!< \brief The the timeout to retry sync/un-sync */ + bool unsync_initial; /*!< \brief Specifies if un-sync complete shall un-initialize the channel */ + + CSubject event_subject; /*!< \brief Subject to report synchronization result */ + CTimer init_timer; /*!< \brief Timer elapses on synchronization timeout */ + +} CPmFifos; + +/*------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Fifos_Ctor(CPmFifos *self, CBase *base_ptr, CPmChannel *channel_ptr, CPmFifo *icm_fifo_ptr, CPmFifo *mcm_fifo_ptr, CPmFifo *rcm_fifo_ptr); +extern void Fifos_AddEventObserver(CPmFifos *self, CObserver *obs_ptr); +extern void Fifos_RemoveEventObserver(CPmFifos *self, CObserver *obs_ptr); +extern void Fifos_Synchronize(CPmFifos *self, bool reset_cnt, bool force_sync); +extern void Fifos_Unsynchronize(CPmFifos *self, bool reset_cnt, bool initial); +extern void Fifos_ForceTermination(CPmFifos *self); +extern void Fifos_ConfigureSyncParams(CPmFifos *self, uint8_t retries, uint16_t timeout/*, bool uninit_on_fail*/); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMFIFOS_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmp.c b/mnsl/mns_pmp.c new file mode 100644 index 0000000..765982d --- /dev/null +++ b/mnsl/mns_pmp.c @@ -0,0 +1,352 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Port Message Protocol + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMH + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmp.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* PMP Indexes */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_IDX_PML_H 0U +#define PMP_IDX_PML_L 1U +#define PMP_IDX_PMHL 2U +#define PMP_IDX_FPH 3U +#define PMP_IDX_SID 4U +#define PMP_IDX_EXT_TYPE 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Masks and shifts for bit fields */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_PMHL_MASK 0x1FU /* 0b00011111 */ +#define PMP_VERSION_MASK 0xE0U /* 0b11100000 */ +#define PMP_VERSION 0x40U /* Version: "2" */ +#define PMP_FPH_TYPE_POS 1U +#define PMP_FPH_TYPE_MASK 0x06U /* 0b00000110 */ +#define PMP_FPH_ID_POS 3U +#define PMP_FPH_ID_MASK 0x38U /* 0b00111000 */ +#define PMP_FPH_DIR_RX 0x01U /* RX: "1" */ +#define PMP_FPH_DIR_MASK 0x01U /* 0b00000001 */ +#define PMP_EXT_TYPE_POS 5U +#define PMP_EXT_TYPE_MASK 0xE0U /* 0b11100000 */ +#define PMP_EXT_CODE_MASK 0x1FU /* 0b00011111 */ + +/*------------------------------------------------------------------------------------------------*/ +/* PMP Verification */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_PML_MAX_SIZE_CTRL 69U +#define PMP_PMHL_MIN_SIZE 3U +#define PMP_PMHL_MAX_SIZE 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Macro like functions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Sets the port message length within a given message header + * \param header The message header + * \param length The port message length + */ +void Pmp_SetPml(uint8_t header[], uint8_t length) +{ + header[PMP_IDX_PML_H] = 0U; + header[PMP_IDX_PML_L] = length; +} + +/*! \brief Sets the port message header length within a given message header + * \param header The message header + * \param length The port message header length. Valid values: 3..5. + * Invalid values will set the PMHL to \c 0. + */ +void Pmp_SetPmhl(uint8_t header[], uint8_t length) +{ + if ((length < PMP_PMHL_MIN_SIZE) || (length > PMP_PMHL_MAX_SIZE)) + { + length = 0U; + } + + header[PMP_IDX_PMHL] = length | PMP_VERSION; +} + +/*! \brief Sets the FIFO protocol header within a given message header + * \param header The message header + * \param id The FIFO id + * \param type The port message type + */ +void Pmp_SetFph(uint8_t header[], Pmp_FifoId_t id, Pmp_MsgType_t type) +{ + header[PMP_IDX_FPH] = (uint8_t)((uint8_t)type << PMP_FPH_TYPE_POS) | (uint8_t)((uint8_t)id << PMP_FPH_ID_POS) | (uint8_t)PMP_DIR_TX; +} + +/*! \brief Sets the field ExtType within a given message header + * \param header The message header + * \param type The command or status type + * \param code The command or status code + */ +void Pmp_SetExtType(uint8_t header[], uint8_t type, uint8_t code) +{ + header[PMP_IDX_EXT_TYPE] = (uint8_t)((type << PMP_EXT_TYPE_POS) & PMP_EXT_TYPE_MASK) | (uint8_t)(code & PMP_EXT_CODE_MASK); +} + +/*! \brief Sets the sequence id within a given message header + * \param header The message header + * \param sid The sequence id + */ +void Pmp_SetSid(uint8_t header[], uint8_t sid) +{ + header[PMP_IDX_SID] = sid; +} + +/*! \brief Retrieves the port message length of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The port message length in bytes or 0 if the PML is greater than 255. + */ +uint8_t Pmp_GetPml(uint8_t header[]) +{ + uint8_t pml; + if (header[PMP_IDX_PML_H] != 0U) + { + pml = 0U; + } + else + { + pml = header[PMP_IDX_PML_L]; + } + + return pml; +} + +/*! \brief Retrieves the port message header length of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The port message header length in bytes + */ +uint8_t Pmp_GetPmhl(uint8_t header[]) +{ + return ((uint8_t)(header[PMP_IDX_PMHL] & (uint8_t)PMP_PMHL_MASK)); +} + +/*! \brief Retrieves the FIFO number of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The FIFO number + */ +Pmp_FifoId_t Pmp_GetFifoId(uint8_t header[]) +{ + return (Pmp_FifoId_t)(((uint8_t)PMP_FPH_ID_MASK & (header)[PMP_IDX_FPH]) >> PMP_FPH_ID_POS); +} + +/*! \brief Retrieves the FIFO Type of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The FIFO type + */ +Pmp_MsgType_t Pmp_GetMsgType(uint8_t header[]) +{ + return ((Pmp_MsgType_t)((PMP_FPH_TYPE_MASK & (header)[PMP_IDX_FPH]) >> PMP_FPH_TYPE_POS)); +} + +/*! \brief Retrieves the SequenceID of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The SequenceID + */ +uint8_t Pmp_GetSid(uint8_t header[]) +{ + return ((header)[PMP_IDX_SID]); +} + +/*! \brief Retrieves payload data of a port message + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \param index The index of the payload byte starting with '0' + * \return The content of a payload data byte + */ +uint8_t Pmp_GetData(uint8_t header[], uint8_t index) +{ + return header[Pmp_GetPmhl(header) + 3U + index]; +} + +/*! \brief Retrieves the payload size of the port message + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \details The function Pmp_VerifyHeader() must be called before + * to verify that the port message fields are consistent. + * \return The payload size of a port message + */ +uint8_t Pmp_GetDataSize(uint8_t header[]) +{ + return Pmp_GetPml(header) - (Pmp_GetPmhl(header) + 1U); +} + +/*! \brief Checks if header length fields are set to valid values + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \param buf_len Length of the complete port message in bytes + * \return Returns \c true if the header was checked successfully, + * otherwise \c false. + */ +bool Pmp_VerifyHeader(uint8_t header[], uint8_t buf_len) +{ + uint8_t pml = Pmp_GetPml(header); + uint8_t pmhl = Pmp_GetPmhl(header); + bool ok = true; + + ok = ((pmhl >= 3U)&&(pmhl <= 5U)) ? ok : false; + ok = ((header[PMP_IDX_PMHL] & PMP_VERSION_MASK) == PMP_VERSION) ? ok : false; + ok = (pml >= (pmhl + 1U)) ? ok : false; + ok = ((pml + 2U) <= buf_len) ? ok : false; + ok = (pml <= PMP_PML_MAX_SIZE_CTRL) ? ok : false; + ok = ((header[PMP_IDX_FPH] & PMP_FPH_DIR_MASK) == PMP_FPH_DIR_RX) ? ok : false; + + return ok; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Creates a Port Message Header instance + * \param self The instance + */ +void Pmh_Ctor(CPmh *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); +} + +/*! \brief Inserts a port message header of the specified size into a given buffer + * \param self Header content to be written to the buffer (source) + * \param data Data buffer the header shall be written to (target) + */ +void Pmh_BuildHeader(CPmh *self, uint8_t data[]) +{ + uint8_t cnt; + + data[PMP_IDX_PML_H] = 0U; + data[PMP_IDX_PML_L] = (uint8_t)self->pml; + data[PMP_IDX_PMHL] = (uint8_t)PMP_VERSION | self->pmhl; + data[PMP_IDX_FPH] = (uint8_t)PMP_DIR_TX | ((uint8_t)((self->fifo_id) << PMP_FPH_ID_POS)) | + ((uint8_t)((self->msg_type) << PMP_FPH_TYPE_POS)); + + data[PMP_IDX_SID] = self->sid; + data[PMP_IDX_EXT_TYPE]= self->ext_type; + + for (cnt=3U; cnt < self->pmhl; cnt++) + { + data[3U + cnt] = 0U; /* add stuffing bytes */ + } +} + +/*! \brief Decodes a given data buffer into a provided port message header structure + * \param self Header content structure (target) + * \param data Data buffer containing the port message with a minimum size + * of 6 bytes (source) + */ +void Pmh_DecodeHeader(CPmh *self, uint8_t data[]) +{ + self->pml = Pmp_GetPml(data); + self->pmhl = data[PMP_IDX_PMHL] & PMP_PMHL_MASK; /* ignore version */ + + self->fifo_id = Pmp_GetFifoId(data); + self->msg_type = Pmp_GetMsgType(data); + self->sid = data[PMP_IDX_SID]; + self->ext_type = data[PMP_IDX_EXT_TYPE]; +} + +/*! \brief Setter function for FIFO protocol header which contains several subfields + * \details The "retransmitted" flag is currently not supported (always Tx) + * \param self Reference to the PM content structure that shall be modified + * \param fifo_id Id of the PM FIFO + * \param msg_type PM type + */ +void Pmh_SetFph(CPmh *self, Pmp_FifoId_t fifo_id, Pmp_MsgType_t msg_type) +{ + self->msg_type = msg_type; + self->fifo_id = fifo_id; +} + +/*! \brief Retrieves the ExtType StatusType + * \param self The instance + * \return Returns The Status Type + */ +Pmp_StatusType_t Pmh_GetExtStatusType(CPmh *self) +{ + return ((Pmp_StatusType_t)((uint8_t)(PMP_EXT_TYPE_MASK & self->ext_type) >> PMP_EXT_TYPE_POS)); +} + +/*! \brief Retrieves the ExtType StatusCode + * \param self The instance + * \return Returns The Status Code + */ +Pmp_CommandCode_t Pmh_GetExtCommandCode(CPmh *self) +{ + return ((Pmp_CommandCode_t)(uint8_t)(PMP_EXT_CODE_MASK & self->ext_type)); +} + +/*! \brief Retrieves the ExtType StatusType + * \param self The instance + * \return Returns The Status Type + */ +Pmp_CommandType_t Pmh_GetExtCommandType(CPmh *self) +{ + return ((Pmp_CommandType_t)((uint8_t)(PMP_EXT_TYPE_MASK & self->ext_type) >> PMP_EXT_TYPE_POS)); +} + +/*! \brief Retrieves the ExtType StatusCode + * \param self The instance + * \return Returns The Status Code + */ +Pmp_StatusCode_t Pmh_GetExtStatusCode(CPmh *self) +{ + return ((Pmp_StatusCode_t)(uint8_t)(PMP_EXT_CODE_MASK & self->ext_type)); +} + +/*! \brief Sets the ExtType field by passing the values for type and code + * \details The function is applicable for status and command + * \param self The Instance + * \param type The status or command type + * \param code The status or command code + */ +void Pmh_SetExtType(CPmh *self, uint8_t type, uint8_t code) +{ + self->ext_type = (uint8_t)((type << PMP_EXT_TYPE_POS) & PMP_EXT_TYPE_MASK) | (uint8_t)(code & PMP_EXT_CODE_MASK); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmp.h b/mnsl/mns_pmp.h new file mode 100644 index 0000000..96b22b0 --- /dev/null +++ b/mnsl/mns_pmp.h @@ -0,0 +1,211 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of Port Message Protocol + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMH + * @{ + */ + +#ifndef MNS_PMP_H +#define MNS_PMP_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_PM_MAX_SIZE_HEADER 8U /*!< \brief Maximum size of a port message header */ +#define PMP_PM_MIN_SIZE_HEADER 6U /*!< \brief Minimum size of a port message header */ +#define PMP_MAX_NUM_FIFOS 7U /*!< \brief Maximum number if FIFOs an an array + * \details Means "3" if FIFO "0" and "2" is used + */ +#define PMP_CREDITS_MASK 0x3FU /*!< \brief Valid bits for credits: 5..0 */ +#define PMP_CREDITS_MIN 1U /*!< \brief Minimum value for credits: 1 */ +#define PMP_CREDITS_MAX 63U /*!< \brief Maximum value for credits: 63 */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Specifies the FIFO */ +typedef enum Pmp_FifoId_ +{ + PMP_FIFO_ID_MCM = 0U, /*!< \brief FIFO dedicated to MOST Control Messages (MCM) */ + /* PMP_FIFO_ID_MDP = 1U, < (reserved identifier) */ + PMP_FIFO_ID_ICM = 2U, /*!< \brief FIFO dedicated to INIC Control Messages (ICM) */ + PMP_FIFO_ID_ALL = 3U, /*!< \brief All FIFOs (ICM, MCM) */ + /* PMP_FIFO_ID_MEP = 4U < (reserved identifier) */ + /* PMP_FIFO_ID_IOCM = 5U < (reserved identifier) */ + PMP_FIFO_ID_RCM = 6U /*!< \brief FIFO dedicated to Remote Control Messages (RCM) */ + /* PMP_FIFO_ID_RSVD = 7U < (reserved identifier) */ + +} Pmp_FifoId_t; + +/*! \brief Specifies the messages type */ +typedef enum Pmp_MsgType_ +{ + PMP_MSG_TYPE_CMD = 0U, /*!< \brief FIFO command message */ + PMP_MSG_TYPE_STATUS = 1U, /*!< \brief FIFO status message */ + PMP_MSG_TYPE_DATA = 2U /*!< \brief FIFO data message */ + +} Pmp_MsgType_t; + +/*! \brief Specifies the direction of the Port Message */ +typedef enum Pmp_Direction_ +{ + PMP_DIR_TX = 0, /*!< \brief Direction Tx (EHC -> INIC) */ + PMP_DIR_RX = 1 /*!< \brief Direction Rx (INIC -> EHC) */ + +} Pmp_Direction_t; + +/*! \brief Specifies FIFO status types */ +typedef enum Pmp_StatusType_ +{ + PMP_STATUS_TYPE_FAILURE = 0U, /*!< \brief PMP status type "failure" */ + PMP_STATUS_TYPE_FLOW = 1U, /*!< \brief PMP status type "flow" */ + PMP_STATUS_TYPE_SYNCED = 4U, /*!< \brief PMP status type "synced" */ + PMP_STATUS_TYPE_UNSYNCED_BSY = 5U, /*!< \brief PMP status type "unsynced_busy" */ + PMP_STATUS_TYPE_UNSYNCED_RDY = 6U /*!< \brief PMP status type "unsynced_ready" */ + +} Pmp_StatusType_t; + +/*! \brief Specifies FIFO status codes */ +typedef enum Pmp_StatusCode_ +{ + PMP_STATUS_CODE_BUSY = 0U, /*!< \brief PMP status code "busy" */ + PMP_STATUS_CODE_SUCCESS = 1U, /*!< \brief PMP status code "success" */ + PMP_STATUS_CODE_CANCELED = 3U, /*!< \brief PMP status code "canceled" */ + PMP_STATUS_CODE_NACK = 8U /*!< \brief PMP status code "not_acknowledge" */ + +} Pmp_StatusCode_t; + +/*! \brief Specifies FIFO status codes */ +typedef enum Pmp_UnsyncReason_ +{ + PMP_UNSYNC_R_STARTUP = 1U, /*!< \brief PMP status code, UnsyncReason "INIC Startup" */ + PMP_UNSYNC_R_REINIT = 2U, /*!< \brief PMP status code, UnsyncReason "Re-init of another FIFO" */ + PMP_UNSYNC_R_COMMAND = 3U, /*!< \brief PMP status code, UnsyncReason "By sync or un-sync command" */ + PMP_UNSYNC_R_ACK_TIMEOUT = 4U, /*!< \brief PMP status code, UnsyncReason "Missing EHC Rx acknowledge" */ + PMP_UNSYNC_R_WD_TIMEOUT = 5U, /*!< \brief PMP status code, UnsyncReason "Missing EHC status request" */ + PMP_UNSYNC_R_TX_TIMEOUT = 6U /*!< \brief PMP status code, UnsyncReason "Missing EHC read, or blocked communication" */ + +} Pmp_UnsyncReason_t; + +/*! \brief Specifies FIFO command types */ +typedef enum Pmp_CommandType_ +{ + PMP_CMD_TYPE_REQ_STATUS = 0U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_TYPE_MSG_ACTION = 1U, /*!< \brief PMP command type "message_action" */ + /* PMP_CMD_TYPE_TRIGGER_NAOMDP = 2U, < (reserved identifier) */ + PMP_CMD_TYPE_SYNCHRONIZATION = 4U /*!< \brief PMP command type "synchronization" */ + +} Pmp_CommandType_t; + +/*! \brief Specifies FIFO command codes */ +typedef enum Pmp_CommandCode_ +{ + PMP_CMD_CODE_REQ_STATUS = 0U, /*!< \brief PMP command code "request_status" */ + /* PMP_CMD_CODE_TRIGGER_NAOMDP = 0U, < (reserved identifier)*/ + PMP_CMD_CODE_ACTION_RETRY = 1U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_ACTION_CANCEL = 2U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_ACTION_CANCEL_ALL = 3U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_UNSYNC = 10U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_SYNC = 21U /*!< \brief PMP command type "request_status" */ + +} Pmp_CommandCode_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Header buffer operations */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pmp_SetPml(uint8_t header[], uint8_t length); +extern void Pmp_SetPmhl(uint8_t header[], uint8_t length); +extern void Pmp_SetFph(uint8_t header[], Pmp_FifoId_t id, Pmp_MsgType_t type); +extern void Pmp_SetExtType(uint8_t header[], uint8_t type, uint8_t code); +extern void Pmp_SetSid(uint8_t header[], uint8_t sid); + +extern uint8_t Pmp_GetPml(uint8_t header[]); +extern uint8_t Pmp_GetPmhl(uint8_t header[]); +extern Pmp_FifoId_t Pmp_GetFifoId(uint8_t header[]); +extern Pmp_MsgType_t Pmp_GetMsgType(uint8_t header[]); +extern uint8_t Pmp_GetSid(uint8_t header[]); +extern uint8_t Pmp_GetDataSize(uint8_t header[]); +extern uint8_t Pmp_GetData(uint8_t header[], uint8_t index); +extern bool Pmp_VerifyHeader(uint8_t header[], uint8_t buf_len); + +/*------------------------------------------------------------------------------------------------*/ +/* Class CPmh */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines the content of a Port Message Header + * \details The current structure does not support "direction" and "retransmitted" flag. + */ +typedef struct CPmh_ +{ + uint8_t pml; /*!< \brief Port Message length */ + uint8_t pmhl; /*!< \brief Port Message Header length */ + Pmp_MsgType_t msg_type; /*!< \brief Port Message type */ + Pmp_FifoId_t fifo_id; /*!< \brief FIFO identifier */ + uint8_t sid; /*!< \brief The SequenceId */ + uint8_t ext_type; /*!< \brief status or content type */ + +} CPmh; + +/*------------------------------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pmh_Ctor(CPmh *self); +extern void Pmh_BuildHeader(CPmh *self, uint8_t data[]); +extern void Pmh_DecodeHeader(CPmh *self, uint8_t data[]); +extern void Pmh_SetFph(CPmh *self, Pmp_FifoId_t fifo_id, Pmp_MsgType_t msg_type); +extern Pmp_StatusType_t Pmh_GetExtStatusType(CPmh *self); +extern Pmp_StatusCode_t Pmh_GetExtStatusCode(CPmh *self); +extern Pmp_CommandCode_t Pmh_GetExtCommandCode(CPmh *self); +extern Pmp_CommandType_t Pmh_GetExtCommandType(CPmh *self); +extern void Pmh_SetExtType(CPmh *self, uint8_t type, uint8_t code); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMP_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pool.c b/mnsl/mns_pool.c new file mode 100644 index 0000000..870efd3 --- /dev/null +++ b/mnsl/mns_pool.c @@ -0,0 +1,127 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 message pool class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_POOL + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pool.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of message pool class + * \param self The instance + * \param messages Reference to an array of message objects + * \param size Number of message objects the \c messages array is comprising. + * \param mns_inst_id MOST NetServices instance ID + */ +void Pool_Ctor(CPool *self, CMessage messages[], uint16_t size, uint8_t mns_inst_id) +{ + uint16_t index; + + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = mns_inst_id; + self->initial_size = size; + self->messages = messages; + + Dl_Ctor(&self->message_list, self->mns_inst_id); + + for (index = 0U; index < size; index++) + { + Msg_Ctor(&messages[index]); + Msg_SetPoolReference(&messages[index], self); + Dl_InsertTail(&self->message_list, Msg_GetNode(&messages[index])); + } +} + +/*! \brief Retrieves a message object from the pool + * \param self The instance + * \return Reference to the CMessage structure if a message is available. + * Otherwise \c NULL. + */ +CMessage* Pool_GetMsg(CPool *self) +{ + CMessage *msg = NULL; + CDlNode *node = Dl_PopHead(&self->message_list); + + if (node != NULL) + { + msg = (CMessage*)node->data_ptr; + } + + return msg; +} + +/*! \brief Returns a message object to the pool pre-assigned pool + * \param msg_ptr Reference to the message object which needs + * to be returned to the pool. + */ +void Pool_ReturnMsg(CMessage *msg_ptr) +{ + CPool *pool_ptr = (CPool*)Msg_GetPoolReference(msg_ptr); + + if (pool_ptr != NULL) + { + TR_ASSERT(pool_ptr->mns_inst_id, "[POOL]", (Pool_GetCurrentSize(pool_ptr) < pool_ptr->initial_size)); + Dl_InsertTail(&pool_ptr->message_list, Msg_GetNode(msg_ptr)); + } + else + { + TR_ERROR((pool_ptr->mns_inst_id, "[POOL]", "Pool_ReturnMsg(): released msg_ptr=0x%p without pool reference", 1U, msg_ptr)); + } +} + +/*! \brief Retrieves the current number of available message objects in the pool + * \param self The instance + * \return The current number of available message objects in the pool + */ +uint16_t Pool_GetCurrentSize(CPool *self) +{ + uint16_t list_size = Dl_GetSize(&self->message_list); + + return list_size; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ diff --git a/mnsl/mns_pool.h b/mnsl/mns_pool.h new file mode 100644 index 0000000..c9b6edd --- /dev/null +++ b/mnsl/mns_pool.h @@ -0,0 +1,82 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of message pool class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_POOL + * @{ + */ +#ifndef MNS_POOL_H +#define MNS_POOL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_dl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif +/*------------------------------------------------------------------------------------------------*/ +/* Class CPool */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CMessage + * \details Common internal message class which embeds a list of MOST telegrams + */ +typedef struct CPool_ +{ + uint16_t initial_size; /*! \brief The size of a provided message array */ + CMessage *messages; /*! \brief Reference to a message array provided by another module */ + CDlList message_list; /*! \brief Doubly linked list required providing available messages */ + uint8_t mns_inst_id; /*! \brief MOST NetServices instance ID */ + +} CPool; + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pool_Ctor(CPool *self, CMessage messages[], uint16_t size, uint8_t mns_inst_id); +extern CMessage* Pool_GetMsg(CPool *self); +extern void Pool_ReturnMsg(CMessage *msg_ptr); +extern uint16_t Pool_GetCurrentSize(CPool *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_POOL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ret.h b/mnsl/mns_ret.h new file mode 100644 index 0000000..50305bf --- /dev/null +++ b/mnsl/mns_ret.h @@ -0,0 +1,115 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 This header file contains standard return values used by MOST NetServices functions + * and methods. + * \addtogroup G_MNS_MISC_RET_RES + * @{ + */ + +#ifndef MNS_RET_H +#define MNS_RET_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return codes used for synchronous response */ +typedef enum Mns_Return_ +{ + MNS_RET_SUCCESS = 0x00, /*!< \brief Operation successfully completed */ + MNS_RET_ERR_PARAM = 0x01, /*!< \brief At least one parameter exceeds its + admissible range */ + MNS_RET_ERR_BUFFER_OVERFLOW = 0x02, /*!< \brief Buffer overflow or service busy */ + MNS_RET_ERR_NOT_AVAILABLE = 0x03, /*!< \brief Functionality not available */ + MNS_RET_ERR_NOT_SUPPORTED = 0x04, /*!< \brief This function is not supported by this + derivative of INIC / physical layer / MOST + speed */ + MNS_RET_ERR_INVALID_SHADOW = 0x05, /*!< \brief The requested information is not yet + available */ + MNS_RET_ERR_ALREADY_SET = 0x06, /*!< \brief The value to be set is already set. The + application can therefore be aware that no + message will be send to INIC and no + callback will be called */ + MNS_RET_ERR_API_LOCKED = 0x07, /*!< \brief INIC performs already requested function. */ + MNS_RET_ERR_NOT_INITIALIZED = 0x08 /*!< \brief MOST NetServices is not initialized */ + +} Mns_Return_t; + +/*! \brief Result codes used for asynchronous response */ +typedef enum Mns_Result_ +{ + MNS_RES_SUCCESS = 0x00, /*!< \brief Operation successfully completed */ + MNS_RES_ERR_MOST_STANDARD = 0x01, /*!< \brief MOST standard error occurred */ + MNS_RES_ERR_BUSY = 0x02, /*!< \brief Function currently busy */ + MNS_RES_ERR_PROCESSING = 0x03, /*!< \brief Processing error occurred */ + MNS_RES_ERR_CONFIGURATION = 0x04, /*!< \brief Configuration error occurred */ + MNS_RES_ERR_SYSTEM = 0x05, /*!< \brief System error occurred */ + MNS_RES_ERR_TIMEOUT = 0x06, /*!< \brief Timeout occurred */ + MNS_RES_ERR_TRANSMISSION = 0x07 /*!< \brief Transmission error occurred */ + +} Mns_Result_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard result structure which provides fields for detailed status and + * error information + * \details \mns_ic_started{ The \ref P_UM_SYNC_AND_ASYNC_RESULTS section in \c Getting \c Started will provide you with more detailed information concerning the info pointer and the error code. } + */ +typedef struct Mns_StdResult_ +{ + Mns_Result_t code; /*!< \brief Result/Error code */ + uint8_t *info_ptr; /*!< \brief INIC error data */ + uint8_t info_size; /*!< \brief Size of the INIC error data in bytes */ + +} Mns_StdResult_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for MOST NetServices standard result callbacks + * \param result Result of the callback + */ +typedef void (*Mns_StdResultCb_t)(Mns_StdResult_t result); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_RET_H */ + +/*! + * @} + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_scheduler.c b/mnsl/mns_scheduler.c new file mode 100644 index 0000000..1fc5273 --- /dev/null +++ b/mnsl/mns_scheduler.c @@ -0,0 +1,259 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the scheduler module. The module consists of the two classes + * CScheduler and CService. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_SCHEDULER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_scheduler.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Constants */ +/*------------------------------------------------------------------------------------------------*/ +const Srv_Event_t SRV_EMPTY_EVENT_MASK = (Srv_Event_t)0x00000000; /*!< \brief Empty event mask */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static bool Scd_SearchSlot(void *current_prio_ptr, void *new_prio_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CScheduler */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the scheduler class. + * \param self Instance pointer + * \param init_ptr Reference to the initialization data + */ +void Scd_Ctor(CScheduler *self, Scd_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = init_ptr->mns_inst_id; + Dl_Ctor(&self->srv_list, self->mns_inst_id); + Ssub_Ctor(&self->service_request_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->service_request_subject, + init_ptr->service_request_obs_ptr); + self->scd_srv_is_running = false; +} + +/*! \brief Add the given service to the scheduler. All services are arranged in priority order. + * A service with a higher priority will execute before a service with a lower priority. + * \param self Instance pointer + * \param srv_ptr Reference of the service which shall be added + * \return SCD_OK: Service added + * \return SCD_SRV_ALREADY_LISTED: Services already listed + */ +Scd_Ret_t Scd_AddService(CScheduler *self, CService *srv_ptr) +{ + Scd_Ret_t ret_val; + + /* Check that service is not already part of scheduler */ + if(false == Dl_IsNodeInList(&self->srv_list, &srv_ptr->list_node)) + { + /* Search slot where the service must be inserted depending on the priority value. */ + CDlNode *result_ptr = Dl_Foreach(&self->srv_list, &Scd_SearchSlot, &srv_ptr->priority); + + if(NULL != result_ptr) /* Slot found? */ + { + Dl_InsertBefore(&self->srv_list, result_ptr, &srv_ptr->list_node); + } + else /* No slot found -> Insert as last node */ + { + Dl_InsertTail(&self->srv_list, &srv_ptr->list_node); + } + /* Create back link service -> scheduler */ + srv_ptr->scd_ptr = self; + Dln_SetData(&srv_ptr->list_node, &srv_ptr->priority); + ret_val = SCD_OK; + } + else /* Service is already part of schedulers list */ + { + ret_val = SCD_SRV_ALREADY_LISTED; + } + + return ret_val; +} + +/*! \brief Remove the given service from the schedulers list. + * \param self Instance pointer + * \param srv_ptr Reference of the service which shall be removed + * \return SCD_OK: Service removed + * \return SCD_UNKNOWN_SRV: Unknown service can not be removed + */ +Scd_Ret_t Scd_RemoveService(CScheduler *self, CService *srv_ptr) +{ + Scd_Ret_t ret_val = SCD_OK; + + /* Error occurred? */ + if(DL_UNKNOWN_NODE == Dl_Remove(&self->srv_list, &srv_ptr->list_node)) + { + ret_val = SCD_UNKNOWN_SRV; + } + + return ret_val; +} + +/*! \brief Service function of the scheduler module. + * \param self Instance pointer + */ +void Scd_Service(CScheduler *self) +{ + CService *current_srv_ptr = (CService *)(void*)self->srv_list.head; + + /* Scheduler service is running. Important for event handling */ + self->scd_srv_is_running = true; + + while(NULL != current_srv_ptr) /* Process registered services */ + { + if(NULL != current_srv_ptr->service_fptr) + { + /* Are events pending for the current service */ + if(SRV_EMPTY_EVENT_MASK != current_srv_ptr->event_mask) + { + /* Execute service callback function */ + current_srv_ptr->service_fptr(current_srv_ptr->instance_ptr); + /* Was the current service removed from the schedulers list? */ + if((current_srv_ptr->list_node.prev == NULL) && (current_srv_ptr->list_node.next == NULL)) + { + break; /* Abort scheduler service */ + } + } + } + current_srv_ptr = (CService *)(void*)current_srv_ptr->list_node.next; + } + /* Scheduler services finished */ + self->scd_srv_is_running = false; +} + +/*! \brief Searches for pending events. + * \param self Instance pointer + * \return true: At least one event is active + * \return false: No event is pending + */ +bool Scd_AreEventsPending(CScheduler *self) +{ + bool ret_val = false; + CService *current_srv_ptr = (CService *)(void*)self->srv_list.head; + + while(NULL != current_srv_ptr) + { + if(SRV_EMPTY_EVENT_MASK != current_srv_ptr->event_mask) + { + ret_val = true; + break; + } + current_srv_ptr = (CService *)(void*)current_srv_ptr->list_node.next; + } + + return ret_val; +} + +/*! \brief Searches the slot where the new service has to be inserted. The position depends on + * the given priority. If a the priority of the new service is higher than the priority + * of the current service \c true is returned which stops the search. + * \param current_prio_ptr Current service which is analyzed + * \param new_prio_ptr Priority of the new service + * \return false: The priority of the current service is greater than the new priority + * \return true: The priority of the current service is less than or equal to the new priority + */ +static bool Scd_SearchSlot(void *current_prio_ptr, void *new_prio_ptr) +{ + uint8_t current_prio_ptr_ = *((uint8_t *)current_prio_ptr); + uint8_t new_prio_ = *((uint8_t*)new_prio_ptr); + bool ret_val = false; + + if(current_prio_ptr_ <= new_prio_) + { + ret_val = true; + } + + return ret_val; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CService */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Parameter constructor of the service class. + * \param self Instance pointer + * \param instance_ptr Reference to object which contains the corresponding service + * \param priority Priority of the service + * \param service_fptr Service callback + */ +void Srv_Ctor(CService *self, uint8_t priority, void *instance_ptr, Srv_Cb_t service_fptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + Dln_Ctor(&self->list_node, NULL); + self->priority = priority; + self->instance_ptr = instance_ptr; + self->service_fptr = service_fptr; +} + +/*! \brief Sets events for the given service according to the given event mask. + * \param self Instance pointer + * \param event_mask Mask of the events to be set + */ +void Srv_SetEvent(CService *self, Srv_Event_t event_mask) +{ + self->event_mask |= event_mask; + if(false == self->scd_ptr->scd_srv_is_running) + { + Ssub_Notify(&self->scd_ptr->service_request_subject, NULL, false); + } +} + +/*! \brief The function returns the current state of all event bits of the service. + * \param self Instance pointer + * \param event_mask_ptr Reference to the memory of the returned event mask + */ +void Srv_GetEvent(CService *self, Srv_Event_t *event_mask_ptr) +{ + *event_mask_ptr = self->event_mask; +} + +/*! \brief Clears events for the given service according to the given event mask. + * \param self Instance pointer + * \param event_mask Mask of the events to be clear + */ +void Srv_ClearEvent(CService *self, Srv_Event_t event_mask) +{ + self->event_mask &= ~event_mask; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_scheduler.h b/mnsl/mns_scheduler.h new file mode 100644 index 0000000..624d0ad --- /dev/null +++ b/mnsl/mns_scheduler.h @@ -0,0 +1,148 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the scheduler module. The module consists of the two classes + * CScheduler and CService. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_SCHEDULER + * @{ + */ + +#ifndef MNS_SCHEDULER_H +#define MNS_SCHEDULER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_dl.h" +#include "mns_obs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for MNS request callback function */ +typedef void (*Scd_MnsServiceRequest_t)(void); +/*! \brief Function signature used for service callback functions + * \param self Instance pointer + */ +typedef void (*Srv_Cb_t)(void *self); +/*! \brief Data type of event masks */ +typedef uint32_t Srv_Event_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Definitions */ +/*------------------------------------------------------------------------------------------------*/ +extern const Srv_Event_t SRV_EMPTY_EVENT_MASK; /*!< \brief Empty event mask */ + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerators */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return values of scheduler module. */ +typedef enum Scd_Ret_ +{ + SCD_OK, /*!< \brief No error */ + SCD_UNKNOWN_SRV, /*!< \brief Service is unknown */ + SCD_SRV_ALREADY_LISTED /*!< \brief Service is already part of the schedulers list */ + +} Scd_Ret_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of the scheduler module. */ +typedef struct Scd_InitData_ +{ + /*! \brief Observer to request a MNS service call */ + CSingleObserver *service_request_obs_ptr; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} Scd_InitData_t; + +/*! \brief Class structure of the scheduler. */ +typedef struct CScheduler_ +{ + /*! \brief Subject to request a MNS service call */ + CSingleSubject service_request_subject; + /*! \brief Service list of the scheduler */ + CDlList srv_list; + /*! \brief Indicates if the scheduler services is running */ + bool scd_srv_is_running; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} CScheduler; + +/*! \brief Class structure of services used by the scheduler. */ +typedef struct CService_ +{ + CDlNode list_node; /*!< \brief Administration area for the linked list */ + CScheduler *scd_ptr; /*!< \brief Back link to scheduler */ + void *instance_ptr; /*!< \brief Reference of instance passed to service_fptr() */ + Srv_Cb_t service_fptr; /*!< \brief Reference of the service callback function */ + Srv_Event_t event_mask; /*!< \brief Event mask of the service */ + uint8_t priority; /*!< \brief Priority of the service */ + +} CService; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CScheduler */ +/*------------------------------------------------------------------------------------------------*/ +extern void Scd_Ctor(CScheduler *self, Scd_InitData_t *init_ptr); +extern void Scd_Service(CScheduler *self); +extern Scd_Ret_t Scd_AddService(CScheduler *self, CService *srv_ptr); +extern Scd_Ret_t Scd_RemoveService(CScheduler *self, CService *srv_ptr); +extern bool Scd_AreEventsPending(CScheduler *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CService */ +/*------------------------------------------------------------------------------------------------*/ +extern void Srv_Ctor(CService *self, uint8_t priority, void *instance_ptr, Srv_Cb_t service_fptr); +extern void Srv_SetEvent(CService *self, Srv_Event_t event_mask); +extern void Srv_GetEvent(CService *self, Srv_Event_t *event_mask_ptr); +extern void Srv_ClearEvent(CService *self, Srv_Event_t event_mask); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_SCHEDULER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_segmentation.c b/mnsl/mns_segmentation.c new file mode 100644 index 0000000..7bfa2ff --- /dev/null +++ b/mnsl/mns_segmentation.c @@ -0,0 +1,555 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 AMS Segmentation Class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSSEGM + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_segmentation.h" +#include "mns_ams.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + /*!\brief Timeout for garbage collector */ +static const uint16_t SEGM_GC_TIMEOUT = 5000U; /* parasoft-suppress MISRA2004-8_7 "intended usage as configuration parameter" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static Mns_AmsRx_Msg_t *Segm_RxRetrieveProcessingHandle(CSegmentation *self, Msg_MostTel_t *tel_ptr); +static void Segm_RxStoreProcessingHandle(CSegmentation *self, Mns_AmsRx_Msg_t *msg_ptr); +static bool Segm_RxSearchProcessingHandle(void *current_data, void *search_data); +static bool Segm_RxGcSetLabel(void *current_data, void *search_data); +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId0(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); +static void Segm_RxProcessTelId1(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); +static void Segm_RxProcessTelId2(CSegmentation *self, Msg_MostTel_t *tel_ptr); +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId3(CSegmentation *self, Msg_MostTel_t *tel_ptr); +static void Segm_RxProcessTelId4(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CSegmentation + * \param self The instance + * \param base_ptr Reference to base services + * \param pool_ptr Reference to the (Rx) message pool + * \param rx_def_payload_sz Default memory size that is allocated when receiving segmented messages + * without size prefix + */ +void Segm_Ctor(CSegmentation *self, CBase *base_ptr, CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->base_ptr = base_ptr; /* initialize members */ + self->pool_ptr = pool_ptr; + + self->rx_default_payload_sz = rx_def_payload_sz; + + Dl_Ctor(&self->processing_list, self->base_ptr->mns_inst_id); + T_Ctor(&self->gc_timer); + Tm_SetTimer(&self->base_ptr->tm, /* start garbage collector timer */ + &self->gc_timer, + &Segm_RxGcScanProcessingHandles, + self, + SEGM_GC_TIMEOUT, + SEGM_GC_TIMEOUT + ); +} + +/*! \brief Constructor of class CSegmentation + * \param self The instance + * \param error_fptr Reference to segmentation error callback function + * \param error_inst Reference to segmentation error instance + */ +void Segm_AssignRxErrorHandler(CSegmentation *self, Segm_OnError_t error_fptr, void *error_inst) +{ + self->error_fptr = error_fptr; + self->error_inst = error_inst; +} + +/*! \brief Performs cleanup of pending messages + * \param self The instance + */ +void Segm_Cleanup(CSegmentation *self) +{ + CDlNode *node_ptr = NULL; + /* cleanup Tx queue */ + for (node_ptr = Dl_PopHead(&self->processing_list); node_ptr != NULL; node_ptr = Dl_PopHead(&self->processing_list)) + { + Mns_AmsRx_Msg_t *rx_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + + Amsp_FreeRxPayload(self->pool_ptr, rx_ptr); + Amsp_FreeRxObj(self->pool_ptr, rx_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx segmentation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Builds next MOST telegram according to given Application Messages + * \param self The instance + * \param msg_ptr Reference to the Application Message Tx handle + * \param tel_ptr Reference to the MOST Telegram handle + * \return Returns \c True if segmentation was completed for the Application Message. Otherwise \c false. + */ +bool Segm_TxBuildSegment(CSegmentation *self, Mns_AmsTx_Msg_t* msg_ptr, Msg_MostTel_t *tel_ptr) +{ + bool finished = false; + MISC_UNUSED(self); + + tel_ptr->destination_addr = msg_ptr->destination_address; + tel_ptr->id.fblock_id = msg_ptr->fblock_id; + tel_ptr->id.instance_id = msg_ptr->instance_id; + tel_ptr->id.function_id = msg_ptr->function_id; + tel_ptr->id.op_type = msg_ptr->op_type; + tel_ptr->opts.llrbc = msg_ptr->llrbc; + tel_ptr->info_ptr = msg_ptr; /* info_ptr must carry the reference to AMS Tx message object */ + tel_ptr->opts.cancel_id = Amsg_TxGetFollowerId(msg_ptr); + + if (msg_ptr->data_size <= SEGM_MAX_SIZE_TEL) /* is single transfer? */ + { + Msg_SetExtPayload((CMessage*)(void*)tel_ptr, msg_ptr->data_ptr, (uint8_t)msg_ptr->data_size, NULL); + finished = true; + } + else /* is segmented transfer? */ + { + uint16_t next_segm_cnt = Amsg_TxGetNextSegmCnt(msg_ptr); + + if (next_segm_cnt == 0xFFFFU) /* first segment: size prefixed segmented message TelId = "4" */ + { + tel_ptr->tel.tel_id = 4U; + tel_ptr->tel.tel_data_ptr[0] = MISC_HB(msg_ptr->data_size); + tel_ptr->tel.tel_data_ptr[1] = MISC_LB(msg_ptr->data_size); + tel_ptr->tel.tel_len = 2U; + } + else /* further segments: TelId = "1,2,3" */ + { + uint16_t index = next_segm_cnt * ((uint16_t)SEGM_MAX_SIZE_TEL - 1U); + uint16_t remaining_sz = msg_ptr->data_size - index; + uint8_t tel_sz = SEGM_MAX_SIZE_TEL - 1U; + + if (remaining_sz < SEGM_MAX_SIZE_TEL) + { + tel_ptr->tel.tel_id = 3U; /* is last segment */ + tel_sz = (uint8_t)remaining_sz; + finished = true; + } + else + { + if (0U == index) + { + tel_ptr->tel.tel_id = 1U; /* is first segment */ + } + else + { + tel_ptr->tel.tel_id = 2U; /* is subsequent segment */ + } + } + + tel_ptr->tel.tel_cnt = (uint8_t)next_segm_cnt; + Msg_SetExtPayload((CMessage*)(void*)tel_ptr, &msg_ptr->data_ptr[index], tel_sz, NULL); + } + + Amsg_TxIncrementNextSegmCnt(msg_ptr); + } + + return finished; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx pools */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves a processing Rx Application message object to a corresponding MOST telegram + * \param self The instance + * \param tel_ptr Reference to the MOST telegram + * \return The reference to the corresponding Rx Application Message or \c NULL if no appropriate + * Rx Application Message is available. + */ +static Mns_AmsRx_Msg_t* Segm_RxRetrieveProcessingHandle(CSegmentation *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + CDlNode *result_node_ptr = Dl_Foreach(&self->processing_list, &Segm_RxSearchProcessingHandle, tel_ptr); + + if (result_node_ptr != NULL) + { + Dl_Ret_t ret = Dl_Remove(&self->processing_list, result_node_ptr); + TR_ASSERT(self->base_ptr->mns_inst_id, "[SEGM]", (ret == DL_OK)); + msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(result_node_ptr); + MISC_UNUSED(ret); + } + + return msg_ptr; +} + +/*! \brief Stores a processing Rx Application message object into a dedicated list + * \param self The instance + * \param msg_ptr Reference to the Rx Application Message + */ +static void Segm_RxStoreProcessingHandle(CSegmentation *self, Mns_AmsRx_Msg_t *msg_ptr) +{ + Amsg_RxSetGcMarker(msg_ptr, false); + Amsg_RxEnqueue(msg_ptr, &self->processing_list); /* insert at tail, since garbage collector starts at head */ +} + +/*! \brief Performs garbage collection of outdated message objects + * \param self The instance + */ +void Segm_RxGcScanProcessingHandles(void *self) +{ + CSegmentation *self_ = (CSegmentation*)self; + /* first remove outdated messages */ + CDlNode *node_ptr = Dl_PeekHead(&self_->processing_list); /* get first candidate from head */ + + while (node_ptr != NULL) + { + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + + if (Amsg_RxGetGcMarker(msg_ptr) != false) + { + Msg_MostTel_t tel; + + Amsg_RxCopySignatureToTel(msg_ptr, &tel); + self_->error_fptr(self_->error_inst, &tel, SEGM_ERR_5); + + (void)Dl_Remove(&self_->processing_list, node_ptr); + + Amsp_FreeRxPayload(self_->pool_ptr, msg_ptr); + Amsp_FreeRxObj(self_->pool_ptr, msg_ptr); + + node_ptr = Dl_PeekHead(&self_->processing_list); /* get next candidate from head */ + } + else + { + break; + } + } + + (void)Dl_Foreach(&self_->processing_list, &Segm_RxGcSetLabel, NULL); /* set label of all remaining messages */ +} + +/*! \brief Sets garbage collector flags for all list members + * \param current_data The Application message object present in list + * \param search_data unused (\c NULL) + * \return Returns always false in order to handle all list members */ +static bool Segm_RxGcSetLabel(void *current_data, void *search_data) +{ + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)current_data; + Amsg_RxSetGcMarker(msg_ptr, true); + MISC_UNUSED(search_data); + return false; +} + +/*! \brief Search routine to identify message objects with the same signature + * than a given MOST telegram + * \param current_data The Application message object present in list + * \param search_data The MOST Telegram object + * \return Returns \c true if both handles have the same functional signature, + * otherwise \c false. */ +static bool Segm_RxSearchProcessingHandle(void *current_data, void *search_data) +{ + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)current_data; + Msg_MostTel_t *tel_ptr = (Msg_MostTel_t*)search_data; + + return Amsg_RxHandleIsIdentical(msg_ptr, tel_ptr); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx segmentation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Processes segmentation for a received MOST telegram + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr The result of the segmentation process + * \return The completed Rx Application Message or \c NULL if segmentation process is still + * ongoing. + */ +Mns_AmsRx_Msg_t* Segm_RxExecuteSegmentation(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + *result_ptr = SEGM_RES_OK; + + switch (tel_ptr->tel.tel_id) /* parasoft-suppress MISRA2004-15_3 "ignore unexpected TelIds" */ + { + case 0U: + msg_ptr = Segm_RxProcessTelId0(self, tel_ptr, result_ptr); + break; + case 1U: + Segm_RxProcessTelId1(self, tel_ptr, result_ptr); + break; + case 2U: + Segm_RxProcessTelId2(self, tel_ptr); + break; + case 3U: + msg_ptr = Segm_RxProcessTelId3(self, tel_ptr); + break; + case 4U: + Segm_RxProcessTelId4(self, tel_ptr, result_ptr); + break; + default: + break; + } + + return msg_ptr; /* return completed message */ +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="0" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr Result of segmentation process + * \return The completed Rx Application Message or \c NULL if segmentation process + * does not finish successfully. + */ +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId0(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + *result_ptr = SEGM_RES_OK; + + if (msg_ptr != NULL) /* treat error: segmentation process is ongoing */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_7); + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr);/* free assigned user payload and throw segmentation error */ + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); + msg_ptr = NULL; + } + /* try to allocate handle, memory is NetServices provided (payload <= 45 bytes) */ + msg_ptr = Amsp_AllocRxObj(self->pool_ptr, (uint16_t)tel_ptr->tel.tel_len); + + if (msg_ptr == NULL) + { + msg_ptr = Amsp_AllocRxRsvd(self->pool_ptr); + } + + if (msg_ptr != NULL) /* handle available: setup Rx Application Message */ + { + Amsg_RxCopySignatureFromTel(msg_ptr, tel_ptr); + + if (tel_ptr->tel.tel_len > 0U) + { /* copy payload to message */ + Amsg_RxCopyToPayload(msg_ptr, tel_ptr->tel.tel_data_ptr, tel_ptr->tel.tel_len); + } + else + { /* set payload length to zero */ + msg_ptr->data_ptr = NULL; + msg_ptr->data_size = 0U; + } + } + else + { + *result_ptr = SEGM_RES_RETRY; /* retry when next Rx object is released */ + } + + return msg_ptr; +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="1" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr Result of segmentation process + */ +static void Segm_RxProcessTelId1(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + *result_ptr = SEGM_RES_OK; + + if (tel_ptr->tel.tel_cnt != 0U) /* handle incorrect tel_cnt */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_3); + } + else /* tel_cnt is correct -> continue segmentation */ + { + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + bool is_size_prefixed = false; + + if (msg_ptr != NULL) /* has previous message */ + { + if ((Amsg_RxGetExpTelCnt(msg_ptr) != 0U) || (msg_ptr->data_size > 0U)) + { /* error: previous message already contains segments */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_7); + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); + Amsg_RxHandleSetup(msg_ptr); /* initialize message for re-use */ + } + else /* message and payload had been allocated by TelId '4' */ + { + is_size_prefixed = true; + } + } + else /* allocate message object if pre-allocation was not initiated by TelId "4" */ + { + msg_ptr = Amsp_AllocRxObj(self->pool_ptr, 0U); + } + + if (msg_ptr != NULL) /* now allocate payload */ + { + if (is_size_prefixed == false) + { + Amsg_RxCopySignatureFromTel(msg_ptr, tel_ptr); /* save signature and try to allocate */ + (void)Amsp_AllocRxPayload(self->pool_ptr, self->rx_default_payload_sz, msg_ptr); + } + + if (!Amsg_RxHasExternalPayload(msg_ptr)) /* allocation of payload failed */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_2); + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); + msg_ptr = NULL; + } + else /* allocation of payload succeeded */ + { + (void)Amsg_RxAppendPayload(msg_ptr, tel_ptr); + Segm_RxStoreProcessingHandle(self, msg_ptr); + msg_ptr = NULL; + } + } + else /* no message object allocated */ + { /* send segmentation error */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_4); + } + } +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="2" + * \param self The instance + * \param tel_ptr The received MOST telegram + */ +static void Segm_RxProcessTelId2(CSegmentation *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxProcessTelId3(self, tel_ptr); /* pretend having TelId '2' but store the */ + /* assembled message again */ + if (msg_ptr != NULL) + { + Segm_RxStoreProcessingHandle(self, msg_ptr); + } +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="3" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \return The assembled Rx Application Message or \c NULL if segmentation process + * did not process successfully. + */ +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId3(CSegmentation *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + + if (msg_ptr == NULL) /* is first segment missing */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_1); + } + else + { + uint8_t exp_tel_cnt = Amsg_RxGetExpTelCnt(msg_ptr); + + if ((exp_tel_cnt == 0U) && (msg_ptr->data_size == 0U)) + { /* error: did not receive first segment */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_1); + Segm_RxStoreProcessingHandle(self, msg_ptr); + msg_ptr = NULL; + } + else if (exp_tel_cnt != tel_ptr->tel.tel_cnt) + { /* has wrong TelCnt */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_3); + Segm_RxStoreProcessingHandle(self, msg_ptr); + msg_ptr = NULL; + } + + if (msg_ptr != NULL) + { + bool succ = Amsg_RxAppendPayload(msg_ptr, tel_ptr); + + if (succ == false) + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_2); + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); + msg_ptr = NULL; + } + } + } + + return msg_ptr; +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="4" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr Result of segmentation process + */ +static void Segm_RxProcessTelId4(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + *result_ptr = SEGM_RES_OK; + + if (tel_ptr->tel.tel_len >= 2U) /* telegrams has necessary length */ + { + uint16_t msg_size; + MISC_DECODE_WORD(&msg_size, tel_ptr->tel.tel_data_ptr); + + if (msg_size > SEGM_MAX_SIZE_TEL) /* application message has correct size */ + { + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + + if (msg_ptr != NULL) /* treat error: segmentation process is ongoing */ + { + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_7); + Amsg_RxHandleSetup(msg_ptr); /* initialize message for re-use */ + } + else + { /* try to allocate handle, memory is NetServices provided (payload <= 45 bytes) */ + msg_ptr = Amsp_AllocRxObj(self->pool_ptr, 0U); + } + + if (msg_ptr != NULL) /* allocation succeeded: decode length and allocate payload */ + { + Amsg_RxCopySignatureFromTel(msg_ptr, tel_ptr); + (void)Amsp_AllocRxPayload(self->pool_ptr, msg_size, msg_ptr); + Segm_RxStoreProcessingHandle(self, msg_ptr);/* store handle and don't care if payload was allocated or not */ + msg_ptr = NULL; /* segmentation error 2 is treated by TelId 1 */ + } + else + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_4); + } + } + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_segmentation.h b/mnsl/mns_segmentation.h new file mode 100644 index 0000000..1295be3 --- /dev/null +++ b/mnsl/mns_segmentation.h @@ -0,0 +1,146 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of AMS Segmentation Class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSSEGM + * @{ + */ + +#ifndef MNS_SEGMENTATION_H +#define MNS_SEGMENTATION_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_amsmessage.h" +#include "mns_amspool.h" +#include "mns_base.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines the maximum payload size of a single transfer in bytes */ +#define SEGM_MAX_SIZE_TEL 45U + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Collection of possible segmentation errors */ +typedef enum Segm_Error_ +{ + SEGM_ERR_1 = 1, /*!< \brief The first segment is missing. */ + + SEGM_ERR_2 = 2, /*!< \brief The device is not able to receive a message of this size. + * MNS specific: The allocation of user provided payload failed. + */ + SEGM_ERR_3 = 3, /*!< \brief Unexpected segment number. */ + + SEGM_ERR_4 = 4, /*!< \brief Too many unfinished segmentation messages were pending. */ + + SEGM_ERR_5 = 5, /*!< \brief A timeout occurred while waiting for the next segment. */ + + SEGM_ERR_6 = 6, /*!< \brief The Device is not capable to handle segmented messages. + * MNS specific: The application did not assign the payload allocation + * function in Mns_Ams_InitData_t prior calling Mns_Init(). + */ + SEGM_ERR_7 = 7 /*!< \brief Segmented message has not been finished before the arrival of + * another message with the identical FBlockID, InstID, FktID, and + * OPType sent by the same node. + */ +} Segm_Error_t; + +/*! \brief Segmentation result */ +typedef enum Segm_Result_ +{ + SEGM_RES_OK, /*!< \brief Telegram was processed */ + SEGM_RES_RETRY /*!< \brief Telegram shall be processed again as soon as messages are freed to the Rx pool */ + +} Segm_Result_t; + +/*! \brief Callback function to notify that a segmentation error has occurred + * \param self The instance + * \param tel_ptr The affected telegram + * \param error The segmentation error code (1..7) + */ +typedef void (*Segm_OnError_t)(void *self, Msg_MostTel_t *tel_ptr, Segm_Error_t error); + +/*------------------------------------------------------------------------------------------------*/ +/* Class */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief AMS Segmentation Class + * \details Performs Tx and Rx Segmentation + */ +typedef struct CSegmentation_ +{ + CBase *base_ptr; /*!< \brief Reference to base services */ + CAmsMsgPool *pool_ptr; /*!< \brief Reference to object/payload pool */ + + Segm_OnError_t error_fptr; /*!< \brief Callback function to notify segmentation errors */ + void *error_inst; /*!< \brief Instance which is notified on segmentation errors */ + + CDlList processing_list; /*!< \brief Segmented and un-finished Rx messages */ + CTimer gc_timer; /*!< \brief Timer to trigger the garbage collector */ + uint16_t rx_default_payload_sz; /*!< \brief Payload size that shall be allocated if size-prefixes + * segmentation message is missing */ + +} CSegmentation; + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Segm_Ctor(CSegmentation *self, CBase *base_ptr, CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz); +extern void Segm_AssignRxErrorHandler(CSegmentation *self, Segm_OnError_t error_fptr, void *error_inst); +extern void Segm_Cleanup(CSegmentation *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Public method prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern bool Segm_TxBuildSegment(CSegmentation *self, Mns_AmsTx_Msg_t *msg_ptr, Msg_MostTel_t *tel_ptr); +extern Mns_AmsRx_Msg_t* Segm_RxExecuteSegmentation(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); +extern void Segm_RxGcScanProcessingHandles(void *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_SEGMENTATION_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_telqueue.c b/mnsl/mns_telqueue.c new file mode 100644 index 0000000..c250507 --- /dev/null +++ b/mnsl/mns_telqueue.c @@ -0,0 +1,119 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 class CTelQueue + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MSG_QUEUE + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_telqueue.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CTelQueue + * \param self The instance + * \param mns_inst_id MOST NetServices instance id + */ +void Telq_Ctor(CTelQueue *self, uint8_t mns_inst_id) +{ + self->mns_inst_id = mns_inst_id; + Dl_Ctor(&self->list, self->mns_inst_id); +} + +/*! \brief Retrieves the head object of the telegram queue + * \param self The instance + * \return Reference to the telegram if a telegram object is available. + * Otherwise \c NULL. + */ +Msg_MostTel_t* Telq_Dequeue(CTelQueue *self) +{ + Msg_MostTel_t *tel_ptr = NULL; + CDlNode *node_ptr = Dl_PopHead(&self->list); + + if (node_ptr != NULL) + { + tel_ptr = (Msg_MostTel_t*)Dln_GetData(node_ptr); + } + + return tel_ptr; +} + +/*! \brief Retrieves a reference to the head object + * without removing it from the telegram queue + * \param self The instance + * \return Reference to the telegram if a telegram object is available. + * Otherwise \c NULL. + */ +Msg_MostTel_t* Telq_Peek(CTelQueue *self) +{ + Msg_MostTel_t *tel_ptr = NULL; + CDlNode *node_ptr = Dl_PeekHead(&self->list); + + if (node_ptr != NULL) + { + tel_ptr = (Msg_MostTel_t*)Dln_GetData(node_ptr); + } + + return tel_ptr; +} + +/*! \brief Adds a telegram to the tail of the queue + * \param self The instance + * \param tel_ptr Reference to the telegram + */ +void Telq_Enqueue(CTelQueue *self, Msg_MostTel_t *tel_ptr) +{ + Dl_InsertTail(&self->list, Msg_GetNode((CMessage*)(void*)tel_ptr)); +} + +/*! \brief Retrieves the current number of objects in the telegram queue + * \param self The instance + * \return The current number of available telegram objects in the pool + */ +uint8_t Telq_GetSize(CTelQueue *self) +{ + return (uint8_t)Dl_GetSize(&self->list); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_telqueue.h b/mnsl/mns_telqueue.h new file mode 100644 index 0000000..2a86387 --- /dev/null +++ b/mnsl/mns_telqueue.h @@ -0,0 +1,93 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of class CTelQueue + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MSG_QUEUE + * @{ + */ + +#ifndef MNS_MSGQUEUE_H +#define MNS_MSGQUEUE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_dl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Class CTelQueue */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CTelQueue + * \details Internal class to queue MOST telegrams + */ +typedef struct CTelQueue_ +{ + CDlList list; /*! \brief Doubly linked list */ + uint8_t mns_inst_id; /*! \brief MOST NetServices instance ID */ + +} CTelQueue; + + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Telq_Ctor(CTelQueue *self, uint8_t mns_inst_id); +extern Msg_MostTel_t* Telq_Dequeue(CTelQueue *self); +extern Msg_MostTel_t* Telq_Peek(CTelQueue *self); +extern void Telq_Enqueue(CTelQueue *self, Msg_MostTel_t *tel_ptr); +extern uint8_t Telq_GetSize(CTelQueue *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MSGQUEUE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_timer.c b/mnsl/mns_timer.c new file mode 100644 index 0000000..c28d32b --- /dev/null +++ b/mnsl/mns_timer.c @@ -0,0 +1,454 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 the timer management module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TIMER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_timer.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Service parameters */ +/*------------------------------------------------------------------------------------------------*/ +/*! Priority of the TM service used by scheduler */ +static const uint8_t TM_SRV_PRIO = 255U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */ +/*! Main event for the TM service */ +static const Srv_Event_t TM_EVENT_UPDATE_TIMERS = 1U; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Tm_Service(void *self); +static void Tm_UpdateTimers(CTimerManagement *self); +static bool Tm_HandleElapsedTimer(CTimerManagement *self); +static bool Tm_UpdateTimersAdd(void *c_timer_ptr, void *n_timer_ptr); +static void Tm_SetTimerInternal(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CTimerManagement */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the timer management class. + * \param self Instance pointer + * \param scd Scheduler instance + * \param init_ptr Reference to the initialization data + */ +void Tm_Ctor(CTimerManagement *self, CScheduler *scd, const Tm_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = init_ptr->mns_inst_id; + /* Initialize subjects and add observers */ + Ssub_Ctor(&self->get_tick_count_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->get_tick_count_subject, + init_ptr->get_tick_count_obs_ptr); + if(init_ptr->set_application_timer_obs_ptr != NULL) + { + self->delayed_tm_service_enabled = true; + Ssub_Ctor(&self->set_application_timer_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->set_application_timer_subject, + init_ptr->set_application_timer_obs_ptr); + } + /* Initialize timer management service */ + Srv_Ctor(&self->tm_srv, TM_SRV_PRIO, self, &Tm_Service); + /* Add timer management service to scheduler */ + (void)Scd_AddService(scd, &self->tm_srv); +} + +/*! \brief Service function of the timer management. + * \param self Instance pointer + */ +static void Tm_Service(void *self) +{ + CTimerManagement *self_ = (CTimerManagement *)self; + Srv_Event_t event_mask; + + Srv_GetEvent(&self_->tm_srv, &event_mask); + + if(TM_EVENT_UPDATE_TIMERS == (event_mask & TM_EVENT_UPDATE_TIMERS)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->tm_srv, TM_EVENT_UPDATE_TIMERS); + Tm_UpdateTimers(self_); + } +} + +/*! \brief If event TM_EVENT_UPDATE_TIMERS is set this function is called. Handles the update + * of the timer list. If a timer has expired the corresponding callback function is + * executed. If the expired timer is a periodic timer, the timer will be set again. + * \param self Instance pointer + */ +static void Tm_UpdateTimers(CTimerManagement *self) +{ + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + bool continue_loop = true; + /* Calculate time difference between the current and the last TM service run */ + uint16_t tick_count_diff = (uint16_t)(current_tick_count - self->last_tick_count); + /* Save current tick count for next service run */ + self->last_tick_count = current_tick_count; + + /* Loop while timer list is not empty */ + while((self->timer_list.head != NULL) && (continue_loop!= false)) + { + /* Is not first timer in list elapsed yet? */ + if(tick_count_diff <= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Update delta of first timer in list */ + ((CTimer *)self->timer_list.head->data_ptr)->delta -= tick_count_diff; + tick_count_diff = 0U; + } + else /* At least first timer in list elapsed */ + { + /* Update tick count difference for next timer in list */ + tick_count_diff -= ((CTimer *)self->timer_list.head->data_ptr)->delta; + /* First timer elapsed */ + ((CTimer *)self->timer_list.head->data_ptr)->delta = 0U; + } + + /* First timer in list elapsed? */ + if(0U == ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Handle elapsed timer */ + continue_loop = Tm_HandleElapsedTimer(self); + } + else /* No elapsed timer in list. */ + { + /* First timer in list updated! Set trigger to inform application (see + Tm_CheckForNextService()) and stop TM service. */ + self->set_service_timer = true; + continue_loop = false; + } + } + } +} + +/*! \brief This function is called if the first timer in list is elapsed. The timer handler + * callback function is invoked. If the timer is a periodic timer it is wound up again. + * \param self Instance pointer + * \return \c true if the next timer must be check. + * \return \c false if the wound up timer (periodic timer) is new head of timer list + */ +static bool Tm_HandleElapsedTimer(CTimerManagement *self) +{ + bool ret_val = true; + + CDlNode *node = self->timer_list.head; + /* Reset flag to be able to check if timer object has changed within handler + callback function */ + ((CTimer *)node->data_ptr)->changed = false; + /* Call timer handler callback function */ + ((CTimer *)node->data_ptr)->handler_fptr(((CTimer *)node->data_ptr)->args_ptr); + + /* Timer object hasn't changed within handler callback function? */ + if(false == ((CTimer *)node->data_ptr)->changed) + { + /* Remove current timer from list */ + (void)Dl_Remove(&self->timer_list, node); + /* Mark timer as unused */ + ((CTimer *)node->data_ptr)->in_use = false; + /* Is current timer a periodic timer? */ + if(((CTimer *)node->data_ptr)->period > 0U) + { + /* Reload current timer */ + Tm_SetTimerInternal(self, + ((CTimer *)node->data_ptr), + ((CTimer *)node->data_ptr)->handler_fptr, + ((CTimer *)node->data_ptr)->args_ptr, + ((CTimer *)node->data_ptr)->period, + ((CTimer *)node->data_ptr)->period); + + if(node == self->timer_list.head) /* Is current timer new head of list? */ + { + /* Set trigger to inform application (see Tm_CheckForNextService()) and + stop TM service. */ + self->set_service_timer = true; + ret_val = false; + } + } + } + + return ret_val; +} + +/*! \brief Calls an application callback function to inform the application that the MNS must be + * serviced not later than the passed time period. If the timer list is empty a possible + * running application timer will be stopped. This function is called at the end of + * Mns_Service(). + * \param self Instance pointer + */ +void Tm_CheckForNextService(CTimerManagement *self) +{ + if(self->delayed_tm_service_enabled != false) + { + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + /* Has head of timer list changed? */ + if(self->set_service_timer != false) + { + uint16_t new_time; + uint16_t diff = current_tick_count - self->last_tick_count; + self->set_service_timer = false; + /* Timer expired since last TM service? */ + if(diff >= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + new_time = 1U; /* Return minimum value */ + } + else + { + /* Calculate new timeout */ + new_time = (uint16_t)(((CTimer *)self->timer_list.head->data_ptr)->delta - diff); + } + /* Inform the application that the MNS must be serviced not later than the passed + time period. */ + Ssub_Notify(&self->set_application_timer_subject, &new_time, false); + } + } + else + { + Tm_TriggerService(self); /* Application timer not implemented -> Retrigger TM */ + } +} + +/*! \brief Helper function to set the TM service event. + * \details This function is used by the application to trigger a service call of the Timer + * Management if the application timer has expired. + * \param self Instance pointer + */ +void Tm_TriggerService(CTimerManagement *self) +{ + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + Srv_SetEvent(&self->tm_srv, TM_EVENT_UPDATE_TIMERS); + } +} + +/*! \brief Helper function to stop the TM service. + * \param self Instance pointer + */ +void Tm_StopService(CTimerManagement *self) +{ + uint16_t new_time = 0U; + + /* Clear probable running application timer */ + Ssub_Notify(&self->set_application_timer_subject, &new_time, false); + + /* Reset the service timer. Not necessary ? */ + self->set_service_timer = false; + + /* Clear the timer head queue to prevent any event to be set */ + self->timer_list.head = NULL; +} + +/*! \brief Creates a new timer. The timer expires at the specified elapse time and then after + * every specified period. When the timer expires the specified callback function is + * called. + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \param handler_fptr Callback function which is called when the timer expires + * \param args_ptr Reference to an optional parameter which is passed to the specified + * callback function + * \param elapse The elapse value before the timer expires for the first time, in + * milliseconds + * \param period The period of the timer, in milliseconds. If this parameter is zero, the + * timer is signaled once. If the parameter is greater than zero, the timer + * is periodic. + */ +void Tm_SetTimer(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period) +{ + (void)Tm_ClearTimer(self, timer_ptr); /* Clear timer if running */ + /* Call the internal method to set the new timer (-> does not trigger TM service!) */ + Tm_SetTimerInternal(self, timer_ptr, handler_fptr, args_ptr, elapse, period); + Tm_TriggerService(self); /* New timer added -> trigger timer list update */ +} + +/*! \brief This function contains the internal part when adding a new timer. The function is + * called within Tm_SetTimer() and within Tm_UpdateTimers(). + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \param handler_fptr Callback function which is called when the timer expires + * \param args_ptr Reference to an optional parameter which is passed to the specified + * callback function + * \param elapse The elapse value before the timer expires for the first time, in + * milliseconds + * \param period The period of the timer, in milliseconds. If this parameter is zero, the + * timer is signaled once. If the parameter is greater than zero, the timer + * is periodic. + */ +static void Tm_SetTimerInternal(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period) +{ + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + + /* Save timer specific values */ + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + timer_ptr->in_use = true; + timer_ptr->handler_fptr = handler_fptr; + timer_ptr->args_ptr = args_ptr; + timer_ptr->elapse = elapse; + timer_ptr->period = period; + timer_ptr->delta = elapse; + + /* Create back link to be able to point from node to timer object */ + timer_ptr->node.data_ptr = (void *)timer_ptr; + + if(NULL == self->timer_list.head) /* Is timer list empty? */ + { + Dl_InsertHead(&self->timer_list, &timer_ptr->node); /* Add first timer to list */ + /* Save current tick count */ + Ssub_Notify(&self->get_tick_count_subject, &self->last_tick_count, false); + } + else /* Timer list is not empty */ + { + CDlNode *result_ptr = NULL; + + /* Set delta value in relation to last saved tick count (last TM service) */ + timer_ptr->delta += (uint16_t)(current_tick_count - self->last_tick_count); + + /* Search slot where new timer must be inserted. Update delta of new timer + and delta of the following timer in the list. */ + result_ptr = Dl_Foreach(&self->timer_list, &Tm_UpdateTimersAdd, (void *)timer_ptr); + + if(NULL != result_ptr) /* Slot found? */ + { + /* Insert new timer at found position */ + Dl_InsertBefore(&self->timer_list, result_ptr, &timer_ptr->node); + } + else /* No slot found -> Insert as last node */ + { + /* Add new timer to end of list */ + Dl_InsertTail(&self->timer_list, &timer_ptr->node); + } + } +} + +/*! \brief Removes the specified timer from the timer list. + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \attention Make sure that for a timer object Tm_SetTimer() is called before Tm_ClearTimer() + * is called! + */ +void Tm_ClearTimer(CTimerManagement *self, CTimer *timer_ptr) +{ + if(timer_ptr->in_use != false) /* Is timer currently in use? */ + { + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + + if(NULL != timer_ptr->node.next) /* Has deleted timer a follower? */ + { + /* Adjust delta of following timer */ + ((CTimer *)timer_ptr->node.next->data_ptr)->delta += timer_ptr->delta; + } + + (void)Dl_Remove(&self->timer_list, &timer_ptr->node); + timer_ptr->in_use = false; + + Tm_TriggerService(self); /* Timer removed -> trigger timer list update */ + } +} + +/*! \brief Used by Tm_SetTimer() to find the slot where the new timer must be inserted. + * \param c_timer_ptr Reference to current timer processed by foreach loop + * \param n_timer_ptr Reference to new timer + * \return \c true: Slot found, stop foreach loop + * \return \c false: Slot not found, continue foreach loop + */ +static bool Tm_UpdateTimersAdd(void *c_timer_ptr, void *n_timer_ptr) +{ + CTimer *current_timer_ptr = (CTimer *)c_timer_ptr; + CTimer *new_timer_ptr = (CTimer *)n_timer_ptr; + bool ret_val; + + /* Is current timer lesser than new timer? */ + if(current_timer_ptr->delta <= new_timer_ptr->delta) + { + /* Update delta of new timer and continue foreach loop */ + new_timer_ptr->delta -= current_timer_ptr->delta; + ret_val = false; + } + else /* Slot found! */ + { + /* Correct delta of current timer and stop foreach loop */ + current_timer_ptr->delta -= new_timer_ptr->delta; + ret_val = true; + } + + return ret_val; +} + + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CTimer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the Timer class. + * \param self Instance pointer + */ +void T_Ctor(CTimer *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); +} + +/*! \brief Returns the status of the given timer. + * \param self Instance pointer + * \return \c true if the timer is currently in use + * \return \c false if the timer is not currently in use + */ +bool T_IsTimerInUse(CTimer *self) +{ + return self->in_use; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_timer.h b/mnsl/mns_timer.h new file mode 100644 index 0000000..9e5c02a --- /dev/null +++ b/mnsl/mns_timer.h @@ -0,0 +1,146 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the timer management module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TIMER + * @{ + */ + +#ifndef MNS_TIMER_H +#define MNS_TIMER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_dl.h" +#include "mns_scheduler.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for timer handler + * \param args Void pointer to optional data + */ +typedef void (*Tm_Handler_t)(void *args); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of the timer management module. */ +typedef struct Tm_InitData_ +{ + /*! \brief Observer used to request current tick count value */ + CSingleObserver *get_tick_count_obs_ptr; + /*! \brief Observer used to start application timer for delayed TM service calls */ + CSingleObserver *set_application_timer_obs_ptr; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} Tm_InitData_t; + +/*! \brief Class structure of the a timer object. */ +typedef struct CTimer_ +{ + /*! \brief Node of the doubly linked (timer-) list */ + CDlNode node; + /*! \brief Handler function which is invoked when the timer expires */ + Tm_Handler_t handler_fptr; + /*! \brief Reference to optional parameter */ + void *args_ptr; + /*! \brief The Timeout value before the timer expires for the first time, in milliseconds */ + uint16_t elapse; + /*! \brief The period of the timer, in milliseconds */ + uint16_t period; + /*! \brief Delta time related to next timer in list */ + uint16_t delta; + /*! \brief Flag which signals that the timer is in use */ + bool in_use; + /*! \brief Flag to check if timer object has changed within timer handler callback function */ + bool changed; + +} CTimer; + +/*! \brief Class structure of the timer management */ +typedef struct CTimerManagement_ +{ + /*! \brief Doubly linked list to manage the active timers */ + CDlList timer_list; + /*! \brief Subject to request current tick count */ + CSingleSubject get_tick_count_subject; + /*! \brief Subject to start the application timer which triggers a MNS service call */ + CSingleSubject set_application_timer_subject; + /*! \brief Service instance to add the timer management to the scheduler */ + CService tm_srv; + /*! \brief Last tick count value (saved at TM service) */ + uint16_t last_tick_count; + /*! \brief Signals that the application timer callbacks are used */ + bool delayed_tm_service_enabled; + /*! \brief Indicates that the application timer must be started */ + bool set_service_timer; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} CTimerManagement; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CTimerManagement */ +/*------------------------------------------------------------------------------------------------*/ +extern void Tm_Ctor(CTimerManagement *self, CScheduler *scd, const Tm_InitData_t *init_ptr); +extern void Tm_SetTimer(CTimerManagement *self, CTimer *timer_ptr, Tm_Handler_t handler_fptr, + void *args_ptr, uint16_t elapse, uint16_t period); +extern void Tm_ClearTimer(CTimerManagement *self, CTimer *timer_ptr); +extern void Tm_CheckForNextService(CTimerManagement *self); +extern void Tm_TriggerService(CTimerManagement *self); +extern void Tm_StopService(CTimerManagement *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CTimer */ +/*------------------------------------------------------------------------------------------------*/ +extern void T_Ctor(CTimer *self); +extern bool T_IsTimerInUse(CTimer *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_TIMER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_trace.h b/mnsl/mns_trace.h new file mode 100644 index 0000000..7ef4758 --- /dev/null +++ b/mnsl/mns_trace.h @@ -0,0 +1,87 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Internal header file of the trace interface + */ + +#ifndef MNSL_TRACE_H +#define MNSL_TRACE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Unit and entry ids */ +/*------------------------------------------------------------------------------------------------*/ +#define TR_MNSL_ASSERT "ASSERT failed in line %d" +#define TR_MNSL_INIC_RESULT_ID_1 "INIC error data:" +#define TR_MNSL_INIC_RESULT_ID_2 "--> Data[%u]: 0x%02X" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal macros */ +/*------------------------------------------------------------------------------------------------*/ + +/* parasoft suppress MISRA2004-19_7 MISRA2004-19_4 reason "function-like macros are allowed for tracing" */ +#ifdef MNS_TR_ERROR +# define TR_ERROR(args) MNS_TR_ERROR args; +# define TR_FAILED_ASSERT(mns_inst_id, unit) TR_ERROR(((mns_inst_id), (unit), TR_MNSL_ASSERT, 1U, __LINE__)) +# define TR_ASSERT(mns_inst_id, unit, expr) if (!(expr)) {TR_FAILED_ASSERT((mns_inst_id), (unit));} +# define TR_ERROR_INIC_RESULT(mns_inst_id, unit, info_ptr, info_size) \ + { \ + uint8_t i; \ + TR_ERROR(((mns_inst_id), (unit), TR_MNSL_INIC_RESULT_ID_1, 0U)); \ + for(i=0U; i<info_size; i++) \ + { \ + TR_ERROR(((mns_inst_id), (unit), TR_MNSL_INIC_RESULT_ID_2, 2U, i, info_ptr[i])) \ + } \ + } +#else +# define MNS_TR_ERROR +# define TR_ERROR(args) +# define TR_FAILED_ASSERT(mns_inst_id, unit) +# define TR_ASSERT(mns_inst_id, unit, expr) +# define TR_ERROR_INIC_RESULT(mns_inst_id, unit, info_ptr, info_size) +#endif + +#ifdef MNS_TR_INFO +# define TR_INFO(args) MNS_TR_INFO args; +#else +# define MNS_TR_INFO +# define TR_INFO(args) +#endif +/* parasoft unsuppress item MISRA2004-19_7 item MISRA2004-19_4 reason "function-like macros are allowed for tracing" */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNSL_TRACE_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_transceiver.c b/mnsl/mns_transceiver.c new file mode 100644 index 0000000..ed5e3af --- /dev/null +++ b/mnsl/mns_transceiver.c @@ -0,0 +1,292 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 class CTransceiver + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TRCV + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_transceiver.h" +#include "mns_misc.h" +#include "mns_pmp.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Trcv_OnTxStatusInternal(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CTransceiver + * \param self The instance + * \param fifo_ptr Reference to the dedicated port message FIFO + * \param def_src_addr Source address that is preset in Tx message object + * \param mns_inst_id MOST NetServices instance ID + * \param trace_id ID specifies FIFO in traces if multiple transceivers are running + */ +void Trcv_Ctor(CTransceiver *self, CPmFifo *fifo_ptr, uint16_t def_src_addr, uint8_t mns_inst_id, uint8_t trace_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->fifo_ptr = fifo_ptr; + self->tx_def_src = def_src_addr; + self->mns_inst_id = mns_inst_id; + self->own_id = trace_id; + Pool_Ctor(&self->tx_msg_pool, self->tx_msgs, TRCV_SIZE_TX_POOL, mns_inst_id); + TR_ASSERT(self->mns_inst_id, "[TRCV]", (fifo_ptr != NULL)); +} + +/*! \brief Assigns a function of another class to receive messages + * \details The assigned function is responsible to call Trcv_RxReleaseMsg() it has finished to process it + * \param self The instance + * \param callback_fptr Callback function + * \param inst_ptr The instance of the receiver class + */ +void Trcv_RxAssignReceiver(CTransceiver *self, Trcv_RxCompleteCb_t callback_fptr, void *inst_ptr) +{ + self->rx_complete_fptr = callback_fptr; + self->rx_complete_inst = inst_ptr; +} + +/*! \brief Assigns a function of another class to filter Rx messages + * \details The assigned function is responsible to discard or pass Rx messages + * \param self The instance + * \param callback_fptr Callback function + * \param inst_ptr The instance of the filter class + */ +void Trcv_RxAssignFilter(CTransceiver *self, Trcv_RxFilterCb_t callback_fptr, void *inst_ptr) +{ + self->rx_filter_fptr = callback_fptr; + self->rx_filter_inst = inst_ptr; +} + +/*! \brief Releases an Rx message which was received by the assigned receiver + * \param self The instance + * \param tel_ptr Reference to the received message + */ +void Trcv_RxReleaseMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr) +{ + CMessage *msg_ptr = (CMessage*)(void*)tel_ptr; + bool check_ok = !Dln_IsNodePartOfAList(Msg_GetNode(msg_ptr)); /* message object shall not be part of a list */ + /* because it was provided in an earlier step */ + TR_ASSERT(self->mns_inst_id, "[TRCV]", check_ok); + if (check_ok) + { + Fifo_RxReleaseMsg(self->fifo_ptr, msg_ptr); + } +} + +/*! \brief Retrieves a message object from the pool + * \param self The instance + * \param size Size of the message in bytes. Valid range: 0..45. + * \return Reference to the Msg_MostTel_t structure if a message is available. + * Otherwise \c NULL. + */ +extern Msg_MostTel_t* Trcv_TxAllocateMsg(CTransceiver *self, uint8_t size) +{ + const uint8_t TRCV_CTRL_MAX_SIZE = 45U; /* replace by PMS constant in future */ + CMessage *handle = NULL; + Msg_MostTel_t *tel_ptr = NULL; + + if (size <= TRCV_CTRL_MAX_SIZE) + { + handle = Pool_GetMsg(&self->tx_msg_pool); + + if (handle != NULL) + { + Msg_Cleanup(handle); /* reset headers and fields */ + Msg_ReserveHeader(handle, PMP_PM_MAX_SIZE_HEADER + ENC_MAX_SIZE_CONTENT); + tel_ptr = Msg_GetMostTel(handle); /* return public struct of the message object */ + tel_ptr->tel.tel_id = 0U; + tel_ptr->tel.tel_len = size; + tel_ptr->tel.tel_cnt = 0U; + tel_ptr->source_addr = self->tx_def_src; + } + } + + return tel_ptr; +} + +/*! \brief Returns a message object to the transceiver pool a message was allocated from + * \param tel_ptr Reference to the message object which needs to be returned. + */ +void Trcv_TxReleaseMsg(Msg_MostTel_t *tel_ptr) +{ + CMessage* msg_ptr = (CMessage*)(void*)tel_ptr; /* avoid MISRA-C warning by converting to "void*" */ + bool check_ok = !Dln_IsNodePartOfAList(Msg_GetNode(msg_ptr)); /* message object shall not be part of a list */ + TR_ASSERT(0U, "[TRCV]", check_ok); /* because it was provided in an earlier step */ + + if (check_ok) + { + Pool_ReturnMsg(msg_ptr); + } +} + +/*! \brief Prepares a message object for re-transmission + * \param tel_ptr Reference to the Tx message object which needs + * to be reused. + */ +void Trcv_TxReuseMsg(Msg_MostTel_t *tel_ptr) +{ + CMessage* msg_ptr = (CMessage*)(void*)tel_ptr; + TR_ASSERT(0U, "[TRCV]", (!Dln_IsNodePartOfAList(Msg_GetNode(msg_ptr)))); /* message object shall not be part of a list */ + /* because it was provided in an earlier step */ + Msg_Cleanup(msg_ptr); /* reset headers and fields */ + Msg_ReserveHeader(msg_ptr, PMP_PM_MAX_SIZE_HEADER + ENC_MAX_SIZE_CONTENT); +} + +/*! \brief Transmits a given message object to the INIC + * \details After completed transmission the message object is released automatically + * \param self The instance + * \param tel_ptr Reference to the message object + */ +void Trcv_TxSendMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr) +{ + CMessage *msg_ptr; + + TR_ASSERT(self->mns_inst_id, "[TRCV]", (tel_ptr != NULL)); + msg_ptr = (CMessage*)(void*)tel_ptr; + + TR_INFO((self->mns_inst_id, "[TRCV]", "Trcv_TxSendMsg(): FIFO: %u, MSG(tgt:0x%04X, id:%02X.%01X.%04X.%01X)", 6U, self->own_id, tel_ptr->destination_addr, tel_ptr->id.fblock_id, tel_ptr->id.instance_id, tel_ptr->id.function_id, tel_ptr->id.op_type)); + Msg_SetTxStatusHandler(msg_ptr, &Trcv_OnTxStatusInternal, self); /* just release the message */ + Fifo_Tx(self->fifo_ptr, msg_ptr, false); +} + +/*! \brief Transmits a given message object to the INIC with a dedicated result callback + * \param self The instance + * \param tel_ptr Reference to the message object + * \param callback_fptr Callback function which is invoked after message transmission has finished. + * Must be \c NULL to avoid that a callback function is invoked. In this case + * the message object is freed internally. Hence, the message object must + * not provide external payload. + * \param inst_ptr Reference to the instance which is invoked with callback_fptr. Has to be \c + * NULL if callback_fptr is \c NULL. + * \note The provided callback function is responsible to free the message object by calling + * Trcv_TxReleaseMsg() or to reuse the message object by calling Trcv_TxReuseMsg() before + * passing it to one of the transmit functions again. + */ +void Trcv_TxSendMsgExt(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr) +{ + CMessage *msg_ptr; + + TR_ASSERT(self->mns_inst_id, "[TRCV]", (tel_ptr != NULL)); + msg_ptr = (CMessage*)(void*)tel_ptr; + + if (callback_fptr == NULL) + { + TR_ASSERT(self->mns_inst_id, "[TRCV]", (inst_ptr == NULL)); + callback_fptr = &Trcv_OnTxStatusInternal; + inst_ptr = self; + } + + TR_INFO((self->mns_inst_id, "[TRCV]", "Trcv_TxSendMsgExt(): FIFO: %u, MSG(tgt:0x%04X, id:%02X.%01X.%04X.%01X)", 6U, self->own_id, tel_ptr->destination_addr, tel_ptr->id.fblock_id, tel_ptr->id.instance_id, tel_ptr->id.function_id, tel_ptr->id.op_type)); + Msg_SetTxStatusHandler(msg_ptr, callback_fptr, inst_ptr); + Fifo_Tx(self->fifo_ptr, msg_ptr, false); +} + +/*! \brief Transmits a given message object to the INIC bypassing all other messages in the FIFO + * \param self The instance + * \param tel_ptr Reference to the message object + * \param callback_fptr Callback function which is invoked after message transmission has finished. + * Must be \c NULL to avoid that a callback function is invoked. In this case + * the message object is freed internally. Hence, the message object must + * not provide external payload. + * \param inst_ptr Reference to the instance which is invoked + * \note The provided callback function is responsible to free the message object by calling + * Trcv_TxReleaseMsg() or to reuse the message object by calling Trcv_TxReuseMsg() before + * passing it to one of the transmit functions again. + */ +void Trcv_TxSendMsgBypass(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr) +{ + CMessage *msg_ptr; + + TR_ASSERT(self->mns_inst_id, "[TRCV]", (tel_ptr != NULL)); + msg_ptr = (CMessage*)(void*)tel_ptr; + + if (callback_fptr == NULL) + { + TR_ASSERT(self->mns_inst_id, "[TRCV]", (inst_ptr == NULL)); + callback_fptr = &Trcv_OnTxStatusInternal; + inst_ptr = self; + } + + Msg_SetTxStatusHandler(msg_ptr, callback_fptr, inst_ptr); + Fifo_Tx(self->fifo_ptr, msg_ptr, true); +} + +/*! \brief Callback function which is invoked instead of an external callback + * as soon as channel transmission was finished in PMS. + * \param self The instance + * \param tel_ptr Reference to the message object + * \param status Transmission status + */ +static void Trcv_OnTxStatusInternal(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status) +{ + Trcv_TxReleaseMsg(tel_ptr); + MISC_UNUSED(self); + MISC_UNUSED(status); +} + +/*! \brief Internal callback function which is intended to be + * invoked by the port message channel on completed reception. + * \param self The instance + * \param tel_ptr Reference to the message object + */ +void Trcv_RxOnMsgComplete(void *self, CMessage *tel_ptr) +{ + CTransceiver *self_ = (CTransceiver*)self; + bool discard = false; + + TR_INFO((self_->mns_inst_id, "[TRCV]", "Trcv_RxOnMsgComplete(): FIFO: %u, MSG(src:0x%04X, id:%02X.%01X.%04X.%01X)", 6U, self_->own_id, tel_ptr->pb_msg.source_addr, tel_ptr->pb_msg.id.fblock_id, tel_ptr->pb_msg.id.instance_id, tel_ptr->pb_msg.id.function_id, tel_ptr->pb_msg.id.op_type)); + if (self_->rx_filter_fptr != NULL) + { + discard = self_->rx_filter_fptr(self_->rx_filter_inst, Msg_GetMostTel(tel_ptr)); + } + + if ((self_->rx_complete_fptr != NULL) && (discard == false)) + { + /* the assigned Rx function is responsible to release the message */ + self_->rx_complete_fptr(self_->rx_complete_inst, Msg_GetMostTel(tel_ptr)); + } + else + { + Fifo_RxReleaseMsg(self_->fifo_ptr, tel_ptr); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_transceiver.h b/mnsl/mns_transceiver.h new file mode 100644 index 0000000..b1701c5 --- /dev/null +++ b/mnsl/mns_transceiver.h @@ -0,0 +1,135 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 Declaration of class CTransceiver + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TRCV + * @{ + */ + +#ifndef MNS_TRANSCEIVER_H +#define MNS_TRANSCEIVER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_pool.h" +#include "mns_pmfifo.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Assignable callback function which is invoked for message reception + * \param self The instance + * \param tel_ptr Reference to the message object + */ +typedef void (*Trcv_RxCompleteCb_t)(void *self, Msg_MostTel_t *tel_ptr); + +/*! \brief Assignable callback function which is invoked to filter Rx messages + * \details Filtering is a synchronous operation. Hence, it is not possible to keep a message + * object for delayed processing. The invoked function has to decide whether a + * message shall be discarded and freed to the Rx pool. Therefore, it has to return + * \c true. By returning \ false, the message will be received in the usual way. + * \param self The instance + * \param tel_ptr Reference to the message object + * \return Returns \c true to discard the message and free it to the pool (no-pass). Otherwise, returns + * \c false (pass). + */ +typedef bool (*Trcv_RxFilterCb_t)(void *self, Msg_MostTel_t *tel_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define TRCV_SIZE_TX_POOL 100U /*!< \brief Number of messages in the message pool */ + +/*------------------------------------------------------------------------------------------------*/ +/* Class CTransceiver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CTransceiver + * \details Provides MOST message objects and communication methods to further classes + */ +typedef struct CTransceiver_ +{ + CMessage tx_msgs[TRCV_SIZE_TX_POOL];/*!< \brief Messages in message pool */ + CPool tx_msg_pool; /*!< \brief The message pool */ + uint16_t tx_def_src; /*!< \brief Default source address for Tx message object */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + uint8_t own_id; /*!< \brief ID of the transceiver required for tracing */ + CPmFifo *fifo_ptr; /*!< \brief Reference to dedicated port message FIFO */ + + Trcv_RxCompleteCb_t rx_complete_fptr; /*!< \brief Callback function which is invoked on + * message reception + */ + void *rx_complete_inst; /*!< \brief Instance which is notified on + * message reception + */ + Trcv_RxFilterCb_t rx_filter_fptr; /*!< \brief Callback function which is invoked + * to filter Rx messages + */ + void *rx_filter_inst; /*!< \brief Instance which is notified to + * filter Rx messages + */ +} CTransceiver; + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +/* Constructor */ +extern void Trcv_Ctor(CTransceiver *self, CPmFifo *fifo_ptr, uint16_t def_src_addr, uint8_t mns_inst_id, uint8_t trace_id); +/* Tx */ +extern Msg_MostTel_t* Trcv_TxAllocateMsg(CTransceiver *self, uint8_t size); +extern void Trcv_TxSendMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr); +extern void Trcv_TxSendMsgExt(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr); +extern void Trcv_TxSendMsgBypass(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr); +extern void Trcv_TxReleaseMsg(Msg_MostTel_t *tel_ptr); +extern void Trcv_TxReuseMsg(Msg_MostTel_t *tel_ptr); +/* Rx */ +extern void Trcv_RxAssignReceiver(CTransceiver *self, Trcv_RxCompleteCb_t callback_fptr, void *inst_ptr); +extern void Trcv_RxAssignFilter(CTransceiver *self, Trcv_RxFilterCb_t callback_fptr, void *inst_ptr); +extern void Trcv_RxReleaseMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr); +extern void Trcv_RxOnMsgComplete(void *self, CMessage *tel_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_TRANSCEIVER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_types_cfg.h b/mnsl/mns_types_cfg.h new file mode 100644 index 0000000..32ce84e --- /dev/null +++ b/mnsl/mns_types_cfg.h @@ -0,0 +1,70 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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 MOST NetServices data types. + */ + +#ifndef MNS_TYPES_H +#define MNS_TYPES_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Data Types */ +/*------------------------------------------------------------------------------------------------*/ +/* Definition of standard integer typed, typically defined in <stdint.h> */ +/* typedef signed char int8_t; */ +/* typedef short int16_t; */ +/* typedef int int32_t; */ +/* typedef unsigned char uint8_t; */ +/* typedef unsigned short uint16_t; */ +/* typedef unsigned int uint32_t; */ + +/* Definition of size_t, typically defined in <stddef.h> */ +/* typedef uint32_t size_t; */ + +/* Definition of character type */ + typedef char char_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_TYPES_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mnsl.c b/mnsl/mnsl.c new file mode 100644 index 0000000..2b59746 --- /dev/null +++ b/mnsl/mnsl.c @@ -0,0 +1,435 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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. + * + */ + +/* parasoft suppress item * reason "not part of MOST NetServices delivery" */ + +/*! + * \file + * \brief Implementation of MOST NetServices Light + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mnsl.h" +#include "mns_base.h" +#include "mns_misc.h" +#include "mns_pmchannel.h" +#include "mns_ams.h" +#include "mns_transceiver.h" +#include "mns_pmfifos.h" + +#include <assert.h> //TKU + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Mnsl_OnPmsEvent(void *self, void *event_code); +static void Mnsl_OnServiceRequest(void *self, void *data_ptr); +static void Mnsl_OnGetTickCount(void *self, void *tick_count_value_ptr); +static void Mnsl_OnSetApplicationTimer(void *self, void *new_time_value_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CMnsl */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Assigns default values to a provided MNSL init structure + * \param init_ptr Reference to MNSL init structure. + * \param enable_watchdog Set to \c true for normal use. Set to \c false to disable the watchdog + * supervision for debugging purpose only. + */ +extern void Mnsl_SetDefaultConfig(Mnsl_InitData_t *init_ptr, bool enable_watchdog) +{ + MISC_MEM_SET(init_ptr, 0, sizeof(Mnsl_InitData_t)); + + init_ptr->pms.active_fifos = MNSL_FIFOS_MCM_ICM; + init_ptr->pms.compressed = false; + + init_ptr->pms.icm_config.fifo_id = PMP_FIFO_ID_ICM; + init_ptr->pms.icm_config.tx_wd_timeout = 0U; + init_ptr->pms.icm_config.tx_wd_timer_value = 0U; + init_ptr->pms.icm_config.rx_ack_timeout = 10U; + init_ptr->pms.icm_config.rx_busy_allowed = 0xFU; + init_ptr->pms.icm_config.rx_credits = PMCH_FIFO_CREDITS; + init_ptr->pms.icm_config.rx_threshold = PMCH_FIFO_THRESHOLD; + + init_ptr->pms.rcm_config.fifo_id = PMP_FIFO_ID_RCM; + init_ptr->pms.rcm_config.tx_wd_timeout = 10U; /* watchdog timeout: 1s */ + init_ptr->pms.rcm_config.tx_wd_timer_value = 600U; /* watchdog trigger every 600 ms */ + init_ptr->pms.rcm_config.rx_ack_timeout = 10U; + init_ptr->pms.rcm_config.rx_busy_allowed = 0xFU; + init_ptr->pms.rcm_config.rx_credits = PMCH_FIFO_CREDITS; + init_ptr->pms.rcm_config.rx_threshold = PMCH_FIFO_THRESHOLD; + + init_ptr->pms.mcm_config.fifo_id = PMP_FIFO_ID_MCM; + init_ptr->pms.mcm_config.tx_wd_timeout = 10U; /* watchdog timeout: 1s */ + init_ptr->pms.mcm_config.tx_wd_timer_value = 600U; /* watchdog trigger every 600 ms */ + init_ptr->pms.mcm_config.rx_ack_timeout = 10U; + init_ptr->pms.mcm_config.rx_busy_allowed = 0xFU; + init_ptr->pms.mcm_config.rx_credits = PMCH_MCM_CREDITS; + init_ptr->pms.mcm_config.rx_threshold = PMCH_MCM_THRESHOLD; + + if (enable_watchdog == false) + { + init_ptr->pms.icm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */ + init_ptr->pms.rcm_config.tx_wd_timeout = 0U; /* watchdog timeout: 0 -> infinite */ + init_ptr->pms.rcm_config.tx_wd_timer_value = 0U;/* watchdog timer: 0 -> no timer */ + init_ptr->pms.rcm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */ + init_ptr->pms.mcm_config.tx_wd_timeout = 0U; /* watchdog timeout: 0 -> infinite */ + init_ptr->pms.mcm_config.tx_wd_timer_value = 0U;/* watchdog timer: 0 -> no timer */ + init_ptr->pms.mcm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */ + } +} + +/*! \brief Initialize MNS Light class + * \param init_ptr Reference to the MNSL init structure. + * The memory of the init structure can be freed after the function returns. + */ +void Mnsl_Init(CMnsl *mnsl, Mnsl_InitData_t *init_ptr, void *inst_ptr) +{ + assert(NULL != mnsl); + CSingleObserver *srv_request_obs_ptr = NULL; + CSingleObserver *app_timer_obs_ptr = NULL; + Base_InitData_t base_init_data; + Pmch_InitData_t pmch_init_data; + CPmFifo *icm_ptr = NULL; + CPmFifo *rcm_ptr = NULL; + CPmFifo *mcm_ptr = NULL; + CTransceiver *trcv_mcm_ptr = NULL; + CTransceiver *trcv_rcm_ptr = NULL; + + MISC_MEM_SET(mnsl, 0, sizeof(mnsl)); + MISC_MEM_SET(&base_init_data, 0, sizeof(base_init_data)); + + mnsl->inst_ptr = inst_ptr; //TKU + mnsl->inst_id = 1U; + mnsl->get_tick_count_fptr = init_ptr->general.get_tickcount_fptr; + mnsl->srv_request_fptr = init_ptr->general.request_service_fptr; + mnsl->set_app_timer_fptr = init_ptr->general.set_app_timer_fptr; + + if(mnsl->srv_request_fptr != NULL) + { + Sobs_Ctor(&mnsl->srv_request_obs, mnsl, &Mnsl_OnServiceRequest); + srv_request_obs_ptr = &mnsl->srv_request_obs; + } + if (mnsl->set_app_timer_fptr != NULL) + { + Sobs_Ctor(&mnsl->set_app_timer_obs, mnsl, &Mnsl_OnSetApplicationTimer); + app_timer_obs_ptr = &mnsl->set_app_timer_obs; + } + + base_init_data.mns_inst_id = 1U; + base_init_data.tm.mns_inst_id = 1U; + base_init_data.scd.mns_inst_id = 1U; + + Sobs_Ctor(&mnsl->get_tick_count_obs, mnsl, &Mnsl_OnGetTickCount); + base_init_data.tm.get_tick_count_obs_ptr = &mnsl->get_tick_count_obs; + base_init_data.scd.service_request_obs_ptr = srv_request_obs_ptr; + base_init_data.tm.set_application_timer_obs_ptr = app_timer_obs_ptr; + + Base_Ctor(&mnsl->base, &base_init_data); + + /* Initialize port message service */ + pmch_init_data.mns_inst_id = 1U; + pmch_init_data.tx_release_fptr = &Fifo_TxOnRelease; + pmch_init_data.lld_iface = init_ptr->lld; + Pmch_Ctor(&mnsl->pm_channel, &pmch_init_data, inst_ptr); + + if((init_ptr->pms.active_fifos & MNSL_FIFOS_ICM) == MNSL_FIFOS_ICM) + { + Fifo_InitData_t icm_init; + + icm_init.base_ptr = &mnsl->base; + icm_init.channel_ptr = &mnsl->pm_channel; + icm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete; + icm_init.rx_cb_inst = &mnsl->icm_transceiver; + + if (init_ptr->pms.compressed) + { + icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80); + icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80); + } + else + { + icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + } + + Fifo_Ctor(&mnsl->icm_fifo, &icm_init, &init_ptr->pms.icm_config); + Trcv_Ctor(&mnsl->icm_transceiver, &mnsl->icm_fifo, MSG_ADDR_EHC_CFG, base_init_data.mns_inst_id, PMP_FIFO_ID_ICM);/* initialize ICM transceiver and set link to PMS instance */ + + icm_ptr = &mnsl->icm_fifo; + } + + if((init_ptr->pms.active_fifos & MNSL_FIFOS_RCM) == MNSL_FIFOS_RCM) + { + Fifo_InitData_t rcm_init; + + rcm_init.base_ptr = &mnsl->base; + rcm_init.channel_ptr = &mnsl->pm_channel; + rcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete; + rcm_init.rx_cb_inst = &mnsl->rcm_transceiver; + rcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + rcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + /* Note: RCM compressed data format is not supported */ + + Fifo_Ctor(&mnsl->rcm_fifo, &rcm_init, &init_ptr->pms.rcm_config); + Trcv_Ctor(&mnsl->rcm_transceiver, &mnsl->rcm_fifo, MSG_ADDR_EHC_CFG, base_init_data.mns_inst_id, PMP_FIFO_ID_RCM);/* initialize ICM transceiver and set link to PMS instance */ + + rcm_ptr = &mnsl->rcm_fifo; + trcv_rcm_ptr = &mnsl->rcm_transceiver; + } + + if((init_ptr->pms.active_fifos & MNSL_FIFOS_MCM) == MNSL_FIFOS_MCM) + { + Fifo_InitData_t mcm_init; + + mcm_init.base_ptr = &mnsl->base; + mcm_init.channel_ptr = &mnsl->pm_channel; + mcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete; + mcm_init.rx_cb_inst = &mnsl->mcm_transceiver; + + if (init_ptr->pms.compressed) + { + mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_81); + mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80); + } + else + { + mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + } + + Fifo_Ctor(&mnsl->mcm_fifo, &mcm_init, &init_ptr->pms.mcm_config); + Trcv_Ctor(&mnsl->mcm_transceiver, &mnsl->mcm_fifo, MSG_ADDR_EHC_APP, base_init_data.mns_inst_id, PMP_FIFO_ID_MCM);/* initialize ICM transceiver and set link to PMS instance */ + + mcm_ptr = &mnsl->mcm_fifo; + trcv_mcm_ptr = &mnsl->mcm_transceiver; + } + + if ((trcv_mcm_ptr != NULL) || (trcv_rcm_ptr != NULL)) /* initialize AMS if MCM or RCM is active */ + { + mnsl->ams_allocator.alloc_fptr = init_ptr->ams.rx_alloc_mem_fptr; + mnsl->ams_allocator.free_fptr = init_ptr->ams.rx_free_mem_fptr; + Amsp_Ctor(&mnsl->ams_pool, &mnsl->ams_allocator, mnsl->base.mns_inst_id); + Ams_Ctor(&mnsl->ams, &mnsl->base, trcv_mcm_ptr, trcv_rcm_ptr, &mnsl->ams_pool, AMS_RX_DEF_SIZE_PAYLOAD); + } + + /* initialize FIFO handler */ + Fifos_Ctor(&mnsl->fifos, &mnsl->base, &mnsl->pm_channel, icm_ptr, mcm_ptr, rcm_ptr); + + /* register event callback */ + mnsl->event_fptr = init_ptr->general.event_fptr; + Obs_Ctor(&mnsl->pms_observer, mnsl, &Mnsl_OnPmsEvent); + Fifos_AddEventObserver(&mnsl->fifos, &mnsl->pms_observer); +} + +/*! \brief Synchronizes the PMS + * \details Accordingly MNSL_EVENT_SYNC_COMPLETE or MNSL_EVENT_SYNC_FAILED will be notified. + */ +void Mnsl_Synchronize(CMnsl *mnsl) +{ + assert(NULL != mnsl); + /* initializes the port message service & LLD interface */ + Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); + Fifos_Synchronize(&mnsl->fifos, true, true); +} + +/*! \brief Un-synchronizes the PMS + * \details Accordingly MNSL_EVENT_UNSYNC_COMPLETE or MNSL_EVENT_UNSYNC_FAILED will be notified. + * Calling this function if MNSL is already un-synced will report MNSL_EVENT_UNSYNC_FAILED, + * since the low-level driver interface is not active. + * \param initial MNSL is able to trigger un-synchronization in advance of the initial + * synchronization. In this case, timeouts and retries behave like a synchronization + * and on MNSL_EVENT_UNSYNC_COMPLETE the LLD interface will stay running. + * I.e. if set to \c true the un-synchronization behaves like MNS initial un-synchronization. + * If set to \c false, the un-synchronization behaves like MNS final un-synchronization. + */ +void Mnsl_Unsynchronize(CMnsl *mnsl, bool initial) +{ + assert(NULL != mnsl); + if (initial == false) + { + Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT); /* final sync */ + } + else + { + Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); /* initial sync */ + } + + Fifos_Unsynchronize(&mnsl->fifos, true, initial); +} + +/*! \brief Handles PMS event and notifies MNSL registered event callback + * \param self Reference to instance + * \param event_code Event notified by the PMS + */ +static void Mnsl_OnPmsEvent(void *self, void *event_code) +{ + assert(NULL != self); + Fifos_Event_t *code = (Fifos_Event_t*)event_code; + CMnsl *mnsl = (CMnsl *)self; + + if (mnsl->event_fptr != NULL) + { + switch (*code) + { + case FIFOS_EV_SYNC_LOST: + Eh_ReportEvent(&mnsl->base.eh, EH_E_SYNC_LOST); /* event is necessary for AMS cleanup */ + mnsl->event_fptr(MNSL_EVENT_SYNC_LOST, mnsl->inst_ptr); + break; + case FIFOS_EV_SYNC_ESTABLISHED: + mnsl->event_fptr(MNSL_EVENT_SYNC_COMPLETE, mnsl->inst_ptr); + break; + case FIFOS_EV_SYNC_FAILED: + mnsl->event_fptr(MNSL_EVENT_SYNC_FAILED, mnsl->inst_ptr); + break; + case FIFOS_EV_UNSYNC_COMPLETE: + mnsl->event_fptr(MNSL_EVENT_UNSYNC_COMPLETE, mnsl->inst_ptr); + Eh_ReportEvent(&mnsl->base.eh, EH_E_UNSYNC_COMPLETE); /* event is necessary for AMS cleanup */ + break; + case FIFOS_EV_UNSYNC_FAILED: + mnsl->event_fptr(MNSL_EVENT_UNSYNC_FAILED, mnsl->inst_ptr); + Eh_ReportEvent(&mnsl->base.eh, EH_E_UNSYNC_FAILED); /* event is necessary for AMS cleanup */ + break; + default: + break; + } + } +} + +/*! \brief Service function of MNS Light + */ +void Mnsl_Service(CMnsl *mnsl) +{ + assert(NULL != mnsl); + bool pending_events; + + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service()", 0U)); + Scd_Service(&mnsl->base.scd); /* Run the scheduler */ + pending_events = Scd_AreEventsPending(&mnsl->base.scd); /* Check if events are still pending? */ + + if (pending_events != false) /* At least one event is pending? */ + { + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service(): events still pending", 0U)); + Mnsl_OnServiceRequest(mnsl, NULL); + } + else /* No event is pending */ + { + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service(): calling Tm_CheckForNextService()", 0U)); + mnsl->base.scd.scd_srv_is_running = true; /* prevent continuous service requests if no app timer is provided */ + Tm_CheckForNextService(&mnsl->base.tm); /* If MNS timers are running: What is the next time that the timer management must be */ + mnsl->base.scd.scd_srv_is_running = false; /* serviced again? */ + } +} + +/*! \brief Reports that the application provided timer has expired. + */ +void Mnsl_ReportTimeout(CMnsl *mnsl) +{ + assert(NULL != mnsl); + TR_INFO((mnsl->inst_id, "[API]", "Mns_ReportTimeout()", 0U)); + Tm_TriggerService(&mnsl->base.tm); /* Trigger TM service call */ +} + +/*! \brief Callback function which is invoked by the scheduler + * \param self Parameter not used with single instance API + * \param data_ptr Currently unused + */ +static void Mnsl_OnServiceRequest(void *self, void *data_ptr) +{ + assert(NULL != self); + CMnsl *mnsl = (CMnsl *)self; + MISC_UNUSED(data_ptr); + + if (mnsl->srv_request_fptr != NULL) + { + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_OnServiceRequest(): calling srv_request_fptr()", 0U)); + mnsl->srv_request_fptr(mnsl->inst_ptr); + } +} + +/*! \brief This function is used in combination with the observer \c mns.get_tick_count_obs + * which is used to request the current tick count value form the application. + * \param self Parameter not used with single instance API + * \param tick_count_value_ptr Reference to the requested tick count value. The pointer must + * be casted into data type uint16_t. + */ +static void Mnsl_OnGetTickCount(void *self, void *tick_count_value_ptr) +{ + assert(NULL != self); + CMnsl *mnsl = (CMnsl *)self; + *((uint16_t *)tick_count_value_ptr) = mnsl->get_tick_count_fptr(); +} + +/*! \brief Callback function which is invoked to start the application timer when the MNSL service + * is implemented event driven + * \param self The instance + * \param new_time_value_ptr Reference to the new timer value. The pointer must be casted into + * data type uint16_t. + */ +static void Mnsl_OnSetApplicationTimer(void *self, void *new_time_value_ptr) +{ + CMnsl *self_ = (CMnsl*)self; + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_OnSetApplicationTimer(): set_app_timer_fptr(%d)", 1U, *((uint16_t *)new_time_value_ptr))); + self_->set_app_timer_fptr(*((uint16_t *)new_time_value_ptr), self_->inst_ptr); +} + +/*! \brief Returns the ICM transceiver object + * \return Reference to the ICM transceiver + */ +CTransceiver * Mnsl_GetIcmTransceiver(CMnsl *mnsl) +{ + return &mnsl->icm_transceiver; +} + +/*! \brief Returns the RCM transceiver object + * \return Reference to the RCM transceiver + */ +CTransceiver * Mnsl_GetRcmTransceiver(CMnsl *mnsl) +{ + return &mnsl->rcm_transceiver; +} + +/*! \brief Returns the MCM transceiver object + * \return Reference to the MCM transceiver + */ +CTransceiver * Mnsl_GetMcmTransceiver(CMnsl *mnsl) +{ + return &mnsl->mcm_transceiver; +} + +/*! \brief Returns the AMS transceiver object + * \details It is important not make usage of competing transceivers + * AMS, MCM and RCM. Either use AMS exclusively or MCM and RCM. + * Important: Do not use AMS in mode \c MNSL_FIFOS_ICM. + * \return Reference to the AMS + */ +CAms * Mnsl_GetAmsTransceiver(CMnsl *mnsl) +{ + return &mnsl->ams; +} + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ diff --git a/mnsl/mnsl.h b/mnsl/mnsl.h new file mode 100644 index 0000000..7288f50 --- /dev/null +++ b/mnsl/mnsl.h @@ -0,0 +1,195 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 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 3 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. + * + */ + +/* parasoft suppress item * reason "not part of MOST NetServices delivery" */ + +/*! + * \file + * \brief Header file of MOST NetServices Light + */ + +#ifndef MNSL_H +#define MNSL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_transceiver.h" +#include "mns_ams.h" +#include "mns_pmfifos.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MNS Major Version Number */ +#define MNSL_VERSION_MAJOR 3 +/*! \brief MNS Minor Version Number */ +#define MNSL_VERSION_MINOR 2 +/*! \brief MNS Release Version Number */ +#define MNSL_VERSION_RELEASE 7 +/*! \brief MNS Service Release Number */ +#define MNSL_VERSION_SR 0 +/*! \brief MNS Build Number */ +#define MNSL_VERSION_BUILD 1796 + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines which FIFOs shall be built on the Port Message Channel */ +typedef enum Mnsl_ActiveFifos_ +{ + MNSL_FIFOS_MCM = 0x01U, /*!< \brief Initialize PM channel with MCM FIFO only */ + MNSL_FIFOS_ICM = 0x02U, /*!< \brief Initialize PM channel with ICM FIFO only */ + MNSL_FIFOS_MCM_ICM = 0x03U, /*!< \brief Initialize PM channel with MCM and ICM FIFO */ + MNSL_FIFOS_RCM = 0x04U, /*!< \brief Initialize PM channel with RCM FIFO only */ + MNSL_FIFOS_MCM_RCM = 0x05U, /*!< \brief Initialize PM channel with MCM and RCM FIFO */ + MNSL_FIFOS_ICM_RCM = 0x06U, /*!< \brief Initialize PM channel with ICM and RCM FIFO */ + MNSL_FIFOS_MCM_ICM_RCM = 0x07U /*!< \brief Initialize PM channel with MCM, ICM and RCM FIFO */ + +} Mnsl_ActiveFifos_t; + +/*! \brief Possible MNSL events */ +typedef enum Mnsl_Event_ +{ + MNSL_EVENT_SYNC_COMPLETE = 0, /*!< \brief The initial synchronization completed successfully */ + MNSL_EVENT_SYNC_FAILED = 1, /*!< \brief The initial synchronization failed */ + MNSL_EVENT_SYNC_LOST = 2, /*!< \brief The synchronization to the INIC was lost irrecoverably */ + MNSL_EVENT_UNSYNC_COMPLETE = 3, /*!< \brief The un-synchronization was completed successfully */ + MNSL_EVENT_UNSYNC_FAILED = 4 /*!< \brief The un-synchronization failed */ + +} Mnsl_Event_t; + +typedef uint16_t (*Mnsl_GetTickCountCb_t)(void); +typedef void (*Mnsl_EventCb_t)(Mnsl_Event_t event_code, void *inst_ptr); +typedef void (*Mnsl_ServiceRequestCb_t)(void *inst_ptr); +typedef void (*Mnsl_SetAppTimerCb_t)(uint16_t timeout, void *inst_ptr); + +/*! \brief General initialization settings */ +typedef struct Mnsl_General_InitData_ +{ + Mnsl_EventCb_t event_fptr; /*!< \brief Reports an MNSL event (mandatory callback) */ + Mnsl_GetTickCountCb_t get_tickcount_fptr; /*!< \brief Requests the current tick count in milliseconds (mandatory callback) */ + Mnsl_ServiceRequestCb_t request_service_fptr;/*!< \brief Reports that the application shall call Mnsl_Service() (optional callback) */ + Mnsl_SetAppTimerCb_t set_app_timer_fptr; /*!< \brief Triggers the application to call Mnsl_ReportTimeout() after a specific time (mandatory callback) */ + +} Mnsl_General_InitData_t; + +/*! \brief Port Message Service (PMS) settings */ +typedef struct Mnsl_Pms_InitData_ +{ + Mnsl_ActiveFifos_t active_fifos; /*!< \brief Defines which FIFOs shall be initialized */ + Fifo_Config_t icm_config; /*!< \brief Setup of ICM FIFO */ + Fifo_Config_t mcm_config; /*!< \brief Setup of MCM FIFO */ + Fifo_Config_t rcm_config; /*!< \brief Setup of RCM FIFO */ + bool compressed; /*!< \brief Set to \c true in order to use OS81118 Rev.C */ + +} Mnsl_Pms_InitData_t; + +/*! \brief Application Message Service (AMS) settings */ +typedef struct Mnsl_Ams_InitData_ +{ + Mns_Ams_AllocMemCb_t rx_alloc_mem_fptr; /*!< \brief Required to allocate memory for an AMS Rx message */ + Mns_Ams_FreeMemCb_t rx_free_mem_fptr; /*!< \brief Required to free memory for an AMS Rx message */ + +} Mnsl_Ams_InitData_t; + +/*! \brief Initialization settings */ +typedef struct Mnsl_InitData_ +{ + Mnsl_General_InitData_t general; /*!< \brief Basic settings to drive MNSL */ + Mns_Lld_Callbacks_t lld; /*!< \brief Mandatory callback functions of the driver */ + Mnsl_Pms_InitData_t pms; /*!< \brief Port Message Service settings */ + Mnsl_Ams_InitData_t ams; /*!< \brief Application Message Service settings */ + +} Mnsl_InitData_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Helper Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define MNSL_UNUSED(p) ((p) = (p)) /* parasoft-suppress MISRA2004-19_7 "suppress warning" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal structures */ +/*------------------------------------------------------------------------------------------------*/ +typedef struct CMnsl_ +{ + uint8_t inst_id; + CBase base; + CPmChannel pm_channel; + CPmFifos fifos; + CPmFifo icm_fifo; + CPmFifo rcm_fifo; + CPmFifo mcm_fifo; + + Mnsl_GetTickCountCb_t get_tick_count_fptr; + CSingleObserver get_tick_count_obs; + + Mnsl_ServiceRequestCb_t srv_request_fptr; + CSingleObserver srv_request_obs; + + Mnsl_SetAppTimerCb_t set_app_timer_fptr; + CSingleObserver set_app_timer_obs; + + CTransceiver icm_transceiver; + CTransceiver rcm_transceiver; + CTransceiver mcm_transceiver; + CAms ams; + CAmsMsgPool ams_pool; + Ams_MemAllocator_t ams_allocator; + + Mnsl_EventCb_t event_fptr; + CObserver pms_observer; + + void *inst_ptr; //TKU: added + +} CMnsl; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Mnsl_SetDefaultConfig(Mnsl_InitData_t *init_ptr, bool enable_watchdog); +extern void Mnsl_Init(CMnsl *mnsl, Mnsl_InitData_t *init_ptr, void *inst_ptr); +extern void Mnsl_Synchronize(CMnsl *mnsl); +extern void Mnsl_Unsynchronize(CMnsl *mnsl, bool initial); +extern void Mnsl_Service(CMnsl *mnsl); +extern void Mnsl_ReportTimeout(CMnsl *mnsl); +extern CTransceiver * Mnsl_GetIcmTransceiver(CMnsl *mnsl); +extern CTransceiver * Mnsl_GetRcmTransceiver(CMnsl *mnsl); +extern CTransceiver * Mnsl_GetMcmTransceiver(CMnsl *mnsl); +extern CAms * Mnsl_GetAmsTransceiver(CMnsl *mnsl); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNSL_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + |