summaryrefslogtreecommitdiffstats
path: root/mnsl/mns_ams.c
diff options
context:
space:
mode:
Diffstat (limited to 'mnsl/mns_ams.c')
-rw-r--r--mnsl/mns_ams.c665
1 files changed, 665 insertions, 0 deletions
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 */
+/*------------------------------------------------------------------------------------------------*/
+