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