aboutsummaryrefslogtreecommitdiffstats
path: root/ucs2-lib/src
diff options
context:
space:
mode:
Diffstat (limited to 'ucs2-lib/src')
-rw-r--r--ucs2-lib/src/CMakeLists.txt38
-rw-r--r--ucs2-lib/src/ucs_alm.c275
-rw-r--r--ucs2-lib/src/ucs_amd.c570
-rw-r--r--ucs2-lib/src/ucs_ams.c669
-rw-r--r--ucs2-lib/src/ucs_amsmessage.c639
-rw-r--r--ucs2-lib/src/ucs_amspool.c335
-rw-r--r--ucs2-lib/src/ucs_amtp.c114
-rw-r--r--ucs2-lib/src/ucs_attach.c607
-rw-r--r--ucs2-lib/src/ucs_base.c69
-rw-r--r--ucs2-lib/src/ucs_bc_diag.c784
-rw-r--r--ucs2-lib/src/ucs_class.c1790
-rw-r--r--ucs2-lib/src/ucs_cmd.c191
-rw-r--r--ucs2-lib/src/ucs_dec.c131
-rw-r--r--ucs2-lib/src/ucs_dl.c390
-rw-r--r--ucs2-lib/src/ucs_eh.c153
-rw-r--r--ucs2-lib/src/ucs_encoder.c253
-rw-r--r--ucs2-lib/src/ucs_epm.c495
-rw-r--r--ucs2-lib/src/ucs_exc.c1711
-rw-r--r--ucs2-lib/src/ucs_factory.c830
-rw-r--r--ucs2-lib/src/ucs_fsm.c172
-rw-r--r--ucs2-lib/src/ucs_gpio.c713
-rw-r--r--ucs2-lib/src/ucs_i2c.c646
-rw-r--r--ucs2-lib/src/ucs_inic.c1817
-rw-r--r--ucs2-lib/src/ucs_inic_res.c3735
-rw-r--r--ucs2-lib/src/ucs_jobs.c369
-rw-r--r--ucs2-lib/src/ucs_lldpool.c99
-rw-r--r--ucs2-lib/src/ucs_message.c353
-rw-r--r--ucs2-lib/src/ucs_mgr.c354
-rw-r--r--ucs2-lib/src/ucs_misc.c80
-rw-r--r--ucs2-lib/src/ucs_net.c310
-rw-r--r--ucs2-lib/src/ucs_nodedis.c1038
-rw-r--r--ucs2-lib/src/ucs_nodeobserver.c341
-rw-r--r--ucs2-lib/src/ucs_nsm.c725
-rw-r--r--ucs2-lib/src/ucs_obs.c449
-rw-r--r--ucs2-lib/src/ucs_pmchannel.c307
-rw-r--r--ucs2-lib/src/ucs_pmcmd.c155
-rw-r--r--ucs2-lib/src/ucs_pmevent.c130
-rw-r--r--ucs2-lib/src/ucs_pmfifo.c1366
-rw-r--r--ucs2-lib/src/ucs_pmfifos.c448
-rw-r--r--ucs2-lib/src/ucs_pmp.c350
-rw-r--r--ucs2-lib/src/ucs_pool.c126
-rw-r--r--ucs2-lib/src/ucs_prog.c957
-rw-r--r--ucs2-lib/src/ucs_rsm.c640
-rw-r--r--ucs2-lib/src/ucs_rtm.c1366
-rw-r--r--ucs2-lib/src/ucs_scheduler.c258
-rw-r--r--ucs2-lib/src/ucs_segmentation.c550
-rw-r--r--ucs2-lib/src/ucs_smm.c219
-rw-r--r--ucs2-lib/src/ucs_sys_diag.c1343
-rw-r--r--ucs2-lib/src/ucs_telqueue.c117
-rw-r--r--ucs2-lib/src/ucs_timer.c456
-rw-r--r--ucs2-lib/src/ucs_transceiver.c290
-rw-r--r--ucs2-lib/src/ucs_xrm.c1174
-rw-r--r--ucs2-lib/src/ucs_xrm_res.c1443
-rw-r--r--ucs2-lib/src/ucs_xrmpool.c210
54 files changed, 33150 insertions, 0 deletions
diff --git a/ucs2-lib/src/CMakeLists.txt b/ucs2-lib/src/CMakeLists.txt
new file mode 100644
index 0000000..1aedb22
--- /dev/null
+++ b/ucs2-lib/src/CMakeLists.txt
@@ -0,0 +1,38 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(ucs2-lib)
+
+# Define targets source files
+ADD_LIBRARY(ucs2-lib STATIC ucs_alm.c ucs_amd.c ucs_ams.c ucs_amsmessage.c ucs_amspool.c ucs_amtp.c ucs_attach.c ucs_base.c ucs_bc_diag.c ucs_class.c ucs_cmd.c ucs_dec.c ucs_dl.c ucs_eh.c ucs_encoder.c ucs_epm.c ucs_exc.c ucs_factory.c ucs_fsm.c ucs_gpio.c ucs_i2c.c ucs_inic.c ucs_inic_res.c ucs_jobs.c ucs_lldpool.c ucs_message.c ucs_mgr.c ucs_misc.c ucs_net.c ucs_nodedis.c ucs_nodeobserver.c ucs_nsm.c ucs_obs.c ucs_pmchannel.c ucs_pmcmd.c ucs_pmevent.c ucs_pmfifo.c ucs_pmfifos.c ucs_pmp.c ucs_pool.c ucs_prog.c ucs_rsm.c ucs_rtm.c ucs_scheduler.c ucs_segmentation.c ucs_smm.c ucs_sys_diag.c ucs_telqueue.c ucs_timer.c ucs_transceiver.c ucs_xrm.c ucs_xrmpool.c ucs_xrm_res.c)
+
+ # Expose Library Properties
+ SET_TARGET_PROPERTIES(ucs2-lib PROPERTIES OUTPUT_NAME ucs2net)
+
+ # Library dependencies from PKG_REQUIRED_LIST
+ TARGET_LINK_LIBRARIES(ucs2-lib ${link_libraries})
+
+ # Define properties to expose when others use this target
+ TARGET_INCLUDE_DIRECTORIES(ucs2-lib
+ PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/../inc
+ ${CMAKE_CURRENT_SOURCE_DIR}/../cfg
+ ${CMAKE_CURRENT_SOURCE_DIR}/ucs-xml
+ )
+
diff --git a/ucs2-lib/src/ucs_alm.c b/ucs2-lib/src/ucs_alm.c
new file mode 100644
index 0000000..972402d
--- /dev/null
+++ b/ucs2-lib/src/ucs_alm.c
@@ -0,0 +1,275 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the API locking manager.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_ALM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_alm.h"
+#include "ucs_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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Alm_Ctor(CApiLockingManager *self,
+ CTimerManagement *tm_ptr,
+ CEventHandler *eh_ptr,
+ void * ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ T_Ctor(&self->garbage_collector);
+ self->tm_ptr = tm_ptr;
+ self->eh_ptr = eh_ptr;
+ self->ucs_user_ptr = ucs_user_ptr;
+
+ /* 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(Dl_Foreach(&self->api_list, &Alm_SearchLockedApi, self) == NULL)
+ {
+ 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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Al_Ctor(CApiLocking *self, CSingleObserver *obs_ptr, void * ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->ucs_user_ptr = ucs_user_ptr;
+ Dln_Ctor(&self->node, NULL);
+ if(obs_ptr != NULL)
+ {
+ Ssub_Ctor(&self->subject, self->ucs_user_ptr);
+ (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/ucs2-lib/src/ucs_amd.c b/ucs2-lib/src/ucs_amd.c
new file mode 100644
index 0000000..c4d3456
--- /dev/null
+++ b/ucs2-lib/src/ucs_amd.c
@@ -0,0 +1,570 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Application Message Distributor
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_AMD
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_amd.h"
+#include "ucs_misc.h"
+
+/*! \brief Priority of the Application Message Distribution */
+static const uint8_t AMD_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */
+/*! \brief Event which starts the Rx message distribution */
+static const Srv_Event_t AMD_EV_NOTIFY_RX = 1U;
+/*! \brief Event triggers notification of messages in tx_notify_queue */
+static const Srv_Event_t AMD_EV_NOTIFY_TX = 2U;
+/*! \brief FBlockID of FBlock NetBlock */
+static const uint8_t AMD_FB_NETBLOCK = 1U;
+/*! \brief FBlockID of FBlock NetworkMaster */
+static const uint8_t AMD_FB_NETWORKMASTER = 2U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Amd_Service(void *self);
+static void Amd_OnAmsComplete(void* self, Ucs_AmsRx_Msg_t* msg_ptr);
+static void Amd_OnEvent(void *self, void *error_code_ptr);
+static void Amd_OnTerminateEvent(void *self, void *error_code_ptr);
+static void Amd_RxFlush(CAmd *self, CDlList *list_ptr);
+#ifdef AMD_TX_DISTRIB
+static bool Amd_TxIsRcmMsg(Ucs_AmsTx_Msg_t *msg_ptr);
+static bool Amd_TxReceiveInternal(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr);
+static void Amd_TxProcessNotifyQueue(CAmd *self);
+#endif
+
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of class CAmd
+ * \param self The instance
+ * \param base_ptr Reference to base services
+ * \param ams_ptr Reference to the AMS
+ */
+void Amd_Ctor(CAmd *self, CBase *base_ptr, CAms *ams_ptr)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */
+
+ self->base_ptr = base_ptr;
+ self->ams_ptr = ams_ptr;
+
+ self->started = false;
+ Srv_Ctor(&self->service, AMD_SRV_PRIO, self, &Amd_Service); /* register service */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->service);
+
+ Dl_Ctor(&self->pre_queue, self->base_ptr->ucs_user_ptr); /* init preprocessor queue */
+ Dl_Ctor(&self->rx_queue, self->base_ptr->ucs_user_ptr); /* init Rx queue */
+ /* register event observer */
+ Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Amd_OnEvent);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
+ /* register termination events */
+ Mobs_Ctor(&self->terminate_observer, self, EH_M_TERMINATION_EVENTS, &Amd_OnTerminateEvent);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->terminate_observer);
+
+ Ams_RxAssignReceiver(self->ams_ptr, &Amd_OnAmsComplete, self);
+}
+
+
+#ifdef AMD_TX_DISTRIB
+/*! \brief Constructor of class CAmd
+ * \param self The instance
+ * \param base_ptr Reference to base services
+ * \param ams_ptr Reference to the AMS
+ * \param pool_ptr Reference to the AMS message pool
+ * \param inic_ptr Reference to the INIC
+ * \param net_ptr Reference to the network management
+ */
+void Amd_Ctor(CAmd *self, CBase *base_ptr, CAms *ams_ptr, CAmsMsgPool *pool_ptr, CInic *inic_ptr, CNetworkManagement *net_ptr)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */
+
+ self->base_ptr = base_ptr;
+ self->ams_ptr = ams_ptr;
+ self->pool_ptr = pool_ptr;
+ self->inic_ptr = inic_ptr;
+ self->net_ptr = net_ptr;
+
+ self->started = false;
+ Srv_Ctor(&self->service, AMD_SRV_PRIO, self, &Amd_Service); /* register service */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->service);
+
+ Dl_Ctor(&self->tx_notify_queue, self->base_ptr->ucs_inst_id); /* init queues */
+ Dl_Ctor(&self->pre_queue, self->base_ptr->ucs_inst_id); /* init preprocessor queue */
+ Dl_Ctor(&self->rx_queue, self->base_ptr->ucs_inst_id); /* init Rx queue */
+ /* register event observer */
+ Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Amd_OnEvent);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
+ /* register termination events */
+ Mobs_Ctor(&self->terminate_observer, self, EH_M_TERMINATION_EVENTS, &Amd_OnTerminateEvent);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->terminate_observer);
+
+ Ams_RxAssignReceiver(self->ams_ptr, &Amd_OnAmsComplete, self);
+ Ams_TxAssignTrcvSelector(self->ams_ptr, &Amd_TxIsRcmMsg);
+}
+#endif
+
+/*! \brief Assigns a pre-processor callback function for Rx messages
+ * \details This function must be called during initialization time.
+ * The AMS shall not already run.
+ * \param self The instance
+ * \param callback_fptr Reference to the callback function
+ * \param inst_ptr Reference to the pre-processor
+ */
+void Amd_AssignPreprocessor(CAmd *self, Amd_RxMsgCompleteCb_t callback_fptr, void *inst_ptr)
+{
+ if (callback_fptr != NULL)
+ {
+ self->preprocess_fptr = callback_fptr;
+ self->preprocess_inst_ptr = inst_ptr;
+
+ self->first_receive_fptr = callback_fptr;
+ self->first_receive_inst_ptr = inst_ptr;
+ self->first_q_ptr = &self->pre_queue;
+ }
+}
+
+/*! \brief Assigns a receiver callback function for Rx messages
+ * \details This function must be called during initialization time.
+ * The AMS shall not already run.
+ * \param self The instance
+ * \param callback_fptr Reference to the callback function
+ * \param inst_ptr Reference to the receiver
+ */
+void Amd_AssignReceiver(CAmd *self, Amd_RxMsgCompleteCb_t callback_fptr, void *inst_ptr)
+{
+ if (callback_fptr != NULL)
+ {
+ self->receive_fptr = callback_fptr;
+ self->receive_inst_ptr = inst_ptr;
+
+ if (self->first_receive_fptr == NULL)
+ {
+ self->first_receive_fptr = callback_fptr;
+ self->first_receive_inst_ptr = inst_ptr;
+ self->first_q_ptr = &self->rx_queue;
+ }
+ }
+}
+
+/*! \brief Assigns as callback function which is able to read and modify the Rx message
+ * \param self The instance
+ * \param callback_fptr Reference to the callback function
+ * \param inst_ptr Reference to the instance (owner of the callback function)
+ */
+void Amd_RxAssignModificator(CAmd *self, Amd_RxModificationCb_t callback_fptr, void *inst_ptr)
+{
+ if (callback_fptr != NULL)
+ {
+ self->rx_modification_fptr = callback_fptr;
+ self->rx_modification_inst_ptr = inst_ptr;
+ }
+}
+
+/*! \brief Service function of CAmd
+ * \details The processing of the Rx queues shall be started asynchronously
+ * after the initialization has succeeded.
+ * \param self The instance
+ */
+static void Amd_Service(void *self)
+{
+ CAmd *self_ = (CAmd*)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->service, &event_mask);
+
+ if((event_mask & AMD_EV_NOTIFY_RX) == AMD_EV_NOTIFY_RX) /* triggered on internal transmission */
+ {
+ Srv_ClearEvent(&self_->service, AMD_EV_NOTIFY_RX);
+ if ((self_->started != false) && (self_->first_receive_fptr != NULL))
+ {
+ uint16_t size = Dl_GetSize(self_->first_q_ptr);
+ if (size > 0U)
+ {
+ self_->first_receive_fptr(self_->first_receive_inst_ptr);
+ }
+ }
+ }
+
+#ifdef AMD_TX_DISTRIB
+ if((event_mask & AMD_EV_NOTIFY_TX) == AMD_EV_NOTIFY_TX) /* notify Tx distribution failure asynchronously */
+ {
+ Srv_ClearEvent(&self_->service, AMD_EV_NOTIFY_TX);
+ Amd_TxProcessNotifyQueue(self_);
+ }
+#endif
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Events */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Callback function which is invoked on completed application message reception
+ * \param self The instance
+ * \param msg_ptr Reference to the completed application message
+ */
+static void Amd_OnAmsComplete(void *self, Ucs_AmsRx_Msg_t *msg_ptr)
+{
+ CAmd *self_ = (CAmd*)self;
+
+ if (self_->rx_modification_fptr != NULL)
+ {
+ self_->rx_modification_fptr(self_->rx_modification_inst_ptr, msg_ptr);
+ }
+
+ if (self_->first_receive_fptr != NULL)
+ {
+ Amsg_RxEnqueue(msg_ptr, self_->first_q_ptr);
+
+ if (self_->started != false)
+ {
+ self_->first_receive_fptr(self_->first_receive_inst_ptr);
+ }
+ }
+ else
+ {
+ Ams_RxFreeMsg(self_->ams_ptr, msg_ptr);
+ }
+}
+
+/*! \brief Callback function if an events leads to the termination of the MNS
+ * \param self The instance
+ * \param error_code_ptr Reference to the error code
+ */
+static void Amd_OnTerminateEvent(void *self, void *error_code_ptr)
+{
+ CAmd *self_ = (CAmd*)self;
+ MISC_UNUSED(error_code_ptr);
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Starting AMD Cleanup", 0U));
+ Amd_RxFlush(self_, &self_->pre_queue);
+ Amd_RxFlush(self_, &self_->rx_queue);
+#ifdef AMD_TX_DISTRIB
+ Amd_TxProcessNotifyQueue(self_);
+#endif
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Finished AMD Cleanup", 0U));
+}
+
+/*! \brief Callback function which is invoked if the initialization is complete
+ * \param self The instance
+ * \param error_code_ptr Reference to the error code
+ */
+static void Amd_OnEvent(void *self, void *error_code_ptr)
+{
+ CAmd *self_ = (CAmd*)self;
+ MISC_UNUSED(error_code_ptr);
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Received init complete event", 0U));
+ self_->started = true;
+ Srv_SetEvent(&self_->service, AMD_EV_NOTIFY_RX);
+}
+
+/*! \brief Flushes a given application Rx message queue
+ * \param self The instance
+ * \param list_ptr Reference to a list containing application Rx message objects
+ */
+static void Amd_RxFlush(CAmd *self, CDlList *list_ptr)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr;
+
+ for (msg_ptr = Amsg_RxDequeue(list_ptr); msg_ptr != NULL; msg_ptr = Amsg_RxDequeue(list_ptr))
+ {
+ Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Pre-processor methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Peeks the front-most application message from the preprocessing queue
+ * \param self The instance
+ * \return Returns a reference to the front-most application message or \c NULL if the queue
+ * is empty.
+ */
+Ucs_AmsRx_Msg_t* Amd_PrePeekMsg(CAmd *self)
+{
+ return (Ucs_AmsRx_Msg_t*)(void*)Amsg_RxPeek(&self->pre_queue);
+}
+
+/*! \brief Removes the front-most application message from the preprocessing queue and frees it
+ * \param self The instance
+ */
+void Amd_PreReleaseMsg(CAmd *self)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->pre_queue);
+
+ if (msg_ptr != NULL)
+ {
+ Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
+ }
+}
+
+/*! \brief Forwards the front-most application message from the preprocessing queue to the Rx queue
+ * \param self The instance
+ */
+void Amd_PreForwardMsg(CAmd *self)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->pre_queue);
+
+ if (msg_ptr != NULL)
+ {
+ if (self->receive_fptr != NULL)
+ {
+ Amsg_RxEnqueue(msg_ptr, &self->rx_queue);
+ self->receive_fptr(self->receive_inst_ptr);
+ }
+ else
+ {
+ Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
+ }
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Receiver methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Peeks the front-most application message from the Rx queue
+ * \param self The instance
+ * \return Returns a reference to the front-most application message or \c NULL if the queue
+ * is empty.
+ */
+Ucs_AmsRx_Msg_t* Amd_RxPeekMsg(CAmd *self)
+{
+ return (Ucs_AmsRx_Msg_t*)(void*)Amsg_RxPeek(&self->rx_queue);
+}
+
+/*! \brief Removes the front-most application message from the Rx queue and frees it
+ * \param self The instance
+ */
+void Amd_RxReleaseMsg(CAmd *self)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->rx_queue);
+
+ if (msg_ptr != NULL)
+ {
+ Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
+ }
+}
+
+/*! \brief Retrieves the number of messages which are appended to the Rx queue
+ * \param self The instance
+ * \return Returns the number of messages.
+ */
+uint16_t Amd_RxGetMsgCnt(CAmd *self)
+{
+ return Dl_GetSize(&self->rx_queue);
+}
+
+#ifdef AMD_TX_DISTRIB
+/*------------------------------------------------------------------------------------------------*/
+/* Transmitter methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Distributes a Tx message internally as Rx message
+ * \param self The instance
+ * \param msg_ptr The Tx message
+ * \param tx_complete_sia_fptr Single instance API callback function which is invoked as soon as
+ * the transmission was finished.
+ * \param tx_complete_fptr Multi instance callback function which is invoked as soon as
+ * the transmission was finished.
+ * \param tx_complete_inst_ptr Instance pointer which is referred when tx_complete_fptr is invoked.
+ * \return Returns \c UCS_RET_SUCCESS if the message accepted for transmission.
+ * Returns \c UCS_RET_ERR_PARAM if the message is refused due to invalid message attributes.
+ */
+Ucs_Return_t Amd_TxSendMsg(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr,
+ Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr)
+{
+ Ucs_Return_t ret = UCS_RET_SUCCESS;
+ bool tx_internal = false;
+ bool tx_network = false;
+ bool tx_ignore_nw_failure = false;
+ bool tx_check_ni = true; /* if false, transmit to INIC even during NET_OFF */
+
+ if (Ams_TxIsValidMessage(msg_ptr) != false)
+ {
+ Net_IsOwnAddrResult_t addr_type = Net_IsOwnAddress(self->net_ptr, msg_ptr->destination_address);
+
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[AMS]", (((tx_complete_sia_fptr != NULL) && (tx_complete_fptr != NULL)) == false));
+ Amsg_TxSetCompleteCallback(msg_ptr, tx_complete_sia_fptr, tx_complete_fptr, tx_complete_inst_ptr);
+
+ if (msg_ptr->destination_address == UCS_ADDR_INTERNAL)
+ {
+ tx_internal = true; /* do not forward internal messages to INIC */
+ }
+ else if ((msg_ptr->fblock_id == AMD_FB_NETBLOCK) || (msg_ptr->fblock_id == AMD_FB_NETWORKMASTER))
+ {
+ if (addr_type == NET_IS_OWN_ADDR_NODE) /* replace own node address by "1" to force INIC internal routing */
+ { /* do not replace multicast addresses (these are static and handled by INIC) */
+ Amsg_TxReplaceDestinationAddr(msg_ptr, MSG_ADDR_INIC);
+ }
+ else if ((addr_type == NET_IS_OWN_ADDR_GROUP) ||
+ (msg_ptr->destination_address == UCS_ADDR_BROADCAST_BLOCKING) ||
+ (msg_ptr->destination_address == UCS_ADDR_BROADCAST_UNBLOCKING))
+ {
+ tx_ignore_nw_failure = true;
+ }
+
+ tx_network = true; /* route FBlocks NB and NWM to INIC */
+ tx_check_ni = false; /* INIC performs checks independent from NI state */
+ }
+ else if ((msg_ptr->destination_address == UCS_ADDR_BROADCAST_BLOCKING) ||
+ (msg_ptr->destination_address == UCS_ADDR_BROADCAST_UNBLOCKING))
+ {
+ tx_internal = true; /* forward broadcast messages to INIC and distribute internally */
+ tx_network = true;
+ }
+ else
+ {
+ switch (addr_type)
+ {
+ case NET_IS_OWN_ADDR_NODE:
+ tx_internal = true;
+ break;
+ case NET_IS_OWN_ADDR_GROUP:
+ tx_internal = true;
+ tx_network = true;
+ break;
+ case NET_IS_OWN_ADDR_NONE:
+ default:
+ tx_network = true;
+ break;
+ }
+ }
+
+ if ((Inic_GetAvailability(self->inic_ptr) == UCS_NW_NOT_AVAILABLE) && (tx_check_ni == true))
+ {
+ tx_network = false; /* abort network transmission */
+ Amsg_TxUpdateResult(msg_ptr, UCS_MSG_STAT_ERROR_NA_OFF);
+ }
+
+ if (tx_internal != false)
+ {
+ if (Amd_TxReceiveInternal(self, msg_ptr) == false)
+ { /* internal transmission failed */
+ Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_ERRBUF);
+ }
+ else
+ { /* internal transmission succeeded */
+ Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_SUCCESS);
+ }
+ }
+ else if (tx_ignore_nw_failure != false)
+ { /* INIC routing will succeed while NW transmission fails */
+ Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_SUCCESS);
+ }
+
+ if (tx_network == false)
+ { /* enqueue message to notification queue and set event */
+ Amsg_TxEnqueue(msg_ptr, &self->tx_notify_queue);
+ Srv_SetEvent(&self->service, AMD_EV_NOTIFY_TX);
+ }
+ else
+ {
+ Ams_TxSendMsgDirect(self->ams_ptr, msg_ptr);
+ }
+ }
+ else
+ {
+ ret = UCS_RET_ERR_PARAM; /* invalid message parameters */
+ }
+
+ return ret;
+}
+
+/*! \brief Decides whether to root a message to MCM or RCM FIFO
+ * \param msg_ptr The Tx message object
+ * \return Returns \c true if a Tx message shall be routed to RCM FIFO, otherwise returns \c false.
+ */
+static bool Amd_TxIsRcmMsg(Ucs_AmsTx_Msg_t *msg_ptr)
+{
+ bool ret = false;
+ if (((msg_ptr->fblock_id == AMD_FB_NETBLOCK) && (msg_ptr->op_type <= UCS_OP_STARTACK)) /* is NB.Command */
+ || ((msg_ptr->fblock_id == AMD_FB_NETWORKMASTER) && (msg_ptr->op_type > UCS_OP_STARTACK))) /* or NWM.Report?*/
+ {
+ ret = true;
+ }
+
+ return ret;
+}
+
+/*! \brief Distributes a Tx message internally as Rx message
+ * \param self The instance
+ * \param msg_ptr The Tx message
+ * \return Returns \c true if the message distributed successfully.
+ * Returns \c false if the allocation of the Rx message has failed.
+ */
+static bool Amd_TxReceiveInternal(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr)
+{
+ bool ret = false;
+ Ucs_AmsRx_Msg_t *rx_ptr = Amsp_AllocRxObj(self->pool_ptr, msg_ptr->data_size);
+
+ if (rx_ptr != NULL)
+ {
+ uint16_t src_addr = UCS_ADDR_INTERNAL;
+ if (msg_ptr->destination_address != UCS_ADDR_INTERNAL)
+ {
+ src_addr = Inic_GetNodeAddress(self->inic_ptr);
+ }
+ Amsg_RxBuildFromTx(rx_ptr, msg_ptr, src_addr);
+ if (self->first_q_ptr != NULL)
+ {
+ Amsg_RxEnqueue(rx_ptr, self->first_q_ptr);
+ Srv_SetEvent(&self->service, AMD_EV_NOTIFY_RX);
+ ret = true;
+ }
+ else
+ {
+ Amsp_FreeRxObj(self->pool_ptr, rx_ptr);
+ TR_FAILED_ASSERT(self->base_ptr->ucs_user_ptr, "[AMD]");
+ }
+ }
+
+ return ret;
+}
+
+/*! \brief Notifies the transmission result for all messages in the tx_notify_queue
+ * \param self The instance
+ */
+static void Amd_TxProcessNotifyQueue(CAmd *self)
+{
+ Ucs_AmsTx_Msg_t *tx_ptr = NULL;
+
+ for (tx_ptr = Amsg_TxDequeue(&self->tx_notify_queue); tx_ptr != NULL; tx_ptr = Amsg_TxDequeue(&self->tx_notify_queue))
+ {
+ /* just just notify completion, the object is automatically freed to the pool */
+ Amsg_TxNotifyComplete(tx_ptr, Amsg_TxGetResultCode(tx_ptr), Amsg_TxGetResultInfo(tx_ptr));
+ }
+}
+#endif
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_ams.c b/ucs2-lib/src/ucs_ams.c
new file mode 100644
index 0000000..2df7129
--- /dev/null
+++ b/ucs2-lib/src/ucs_ams.c
@@ -0,0 +1,669 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Application Message Service
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_AMSC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_ams.h"
+#include "ucs_amsmessage.h"
+#include "ucs_dl.h"
+#include "ucs_misc.h"
+#include "ucs_pmp.h"
+#include "ucs_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, Ucs_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->ucs_user_ptr); /* init Rx waiting queue */
+
+ Dl_Ctor(&self->tx.queue, self->base_ptr->ucs_user_ptr);
+
+ 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 UCS_AMSTX_RES_NOT_AVAILABLE.
+ * \param self The instance
+ */
+static void Ams_Cleanup(CAms *self)
+{
+ Ucs_AmsTx_Msg_t *tx_ptr = NULL;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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, UCS_AMSTX_RES_ERR_NOT_AVAILABLE, UCS_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->ucs_user_ptr, "[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 ((event_mask & AMS_EV_TX_SERVICE) == AMS_EV_TX_SERVICE) /* Is event pending? */
+ {
+ Srv_ClearEvent(&self_->service, AMS_EV_TX_SERVICE);
+ Ams_TxService(self_);
+ }
+
+ if ((event_mask & AMS_EV_RX_SERVICE) == 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;
+ Ucs_AmsTx_Msg_t *tx_ptr = (Ucs_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 */
+ {
+ CDlNode *node2_ptr = Dl_PopHead(&self->tx.queue); /* now retrieve application message from queue, previously checked by peek operation */
+
+ if (node2_ptr != NULL)
+ {
+ bool done;
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[AMS]", (node1_ptr == node2_ptr));
+ tx_ptr = (Ucs_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->ucs_user_ptr, "[AMS]", "Ams_TxService(tel_ptr=0x%p)", 1U, tel_ptr));
+
+ if (done == false)
+ {
+ Dl_InsertHead(&self->tx.queue, node2_ptr);
+ }
+ }
+ else
+ {
+ TR_FAILED_ASSERT(self->base_ptr->ucs_user_ptr, "[AMS]"); /* inconsistency between peek and pop operation */
+ }
+ }
+ 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.
+ */
+Ucs_AmsTx_Msg_t * Ams_TxGetMsg(CAms *self, uint16_t size)
+{
+ Ucs_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, Ucs_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 UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_PARAM if the transmission was refused due to an invalid parameter
+ */
+Ucs_Return_t Ams_TxSendMsg(CAms *self, Ucs_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr,
+ Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[AMS]", (((tx_complete_sia_fptr != NULL) && (tx_complete_fptr != NULL)) == false))
+ Amsg_TxSetCompleteCallback(msg_ptr, tx_complete_sia_fptr, tx_complete_fptr, tx_complete_inst_ptr);
+ Ams_TxSendMsgDirect(self, msg_ptr);
+ ret_val = UCS_RET_SUCCESS;
+ }
+
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[AMS]", (ret_val == UCS_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, Ucs_AmsTx_Msg_t *msg_ptr)
+{
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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, Ucs_MsgTxStatus_t status)
+{
+ CAms *self_ = (CAms*)self;
+ Ucs_AmsTx_Msg_t* msg_ptr = (Ucs_AmsTx_Msg_t*)tel_ptr->info_ptr;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[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 ((tel_ptr->tel.tel_id == 0U) || (tel_ptr->tel.tel_id == 3U)) /* 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 != UCS_MSG_STAT_OK) /* check transmission needs termination before transmission end */
+ {
+ TR_ASSERT(self_->base_ptr->ucs_user_ptr, "[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) && (tel_ptr->tel.tel_id == 4U))) /* 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 != UCS_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(Ucs_AmsTx_Msg_t *msg_ptr)
+{
+ bool ret = false;
+
+ if (msg_ptr != NULL)
+ {
+ if (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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[AMS]", ((tel_ptr != NULL) && (tel_ptr->info_ptr != NULL)));
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[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)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = NULL;
+
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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, (Ucs_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;
+ Ucs_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, (Ucs_AmsRx_Msg_t*)(void*)msg_ptr);
+ }
+ }
+ else
+ {
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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 = UCS_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, Ucs_AmsRx_Msg_t *msg_ptr)
+{
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[AMS]", "Ams_RxFreeMsg(msg_ptr=0x%p)", 1U, msg_ptr));
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[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/ucs2-lib/src/ucs_amsmessage.c b/ucs2-lib/src/ucs_amsmessage.c
new file mode 100644
index 0000000..c3d4757
--- /dev/null
+++ b/ucs2-lib/src/ucs_amsmessage.c
@@ -0,0 +1,639 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Application Message Classes
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_AMSMSG
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_amsmessage.h"
+#include "ucs_dl.h"
+#include "ucs_misc.h"
+#include "ucs_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 Ucs_AmsRx_ReceiveType_t Amsg_RxGetReceiveType(uint16_t destination_address);
+static void Amsg_TxRestoreDestinationAddr(Ucs_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(Ucs_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 = UCS_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(Ucs_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(Ucs_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 = UCS_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(Ucs_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(Ucs_AmsTx_Msg_t *self, Ucs_AmsTx_Result_t result, Ucs_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, result, info, SELF_TX->complete_inst_ptr);
+ }
+
+ TR_ASSERT(NULL, "[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(Ucs_AmsTx_Msg_t *self)
+{
+ TR_ASSERT(NULL, "[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(Ucs_AmsTx_Msg_t *self, Ucs_MsgTxStatus_t result)
+{
+ if (result != UCS_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(Ucs_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
+ */
+Ucs_AmsTx_Result_t Amsg_TxGetResultCode(Ucs_AmsTx_Msg_t *self)
+{
+ Ucs_AmsTx_Result_t res = UCS_AMSTX_RES_SUCCESS; /* success is the expected result */
+
+ switch (SELF_TX->temp_result)
+ {
+ case UCS_MSG_STAT_OK:
+ if (SELF_TX->internal_status == AMSG_TX_INTRES_ERRBUF)
+ {
+ res = UCS_AMSTX_RES_ERR_BUF_INTERNAL; /* internal transmission error overrules network success */
+ }
+ break;
+ case UCS_MSG_STAT_ERROR_BF:
+ case UCS_MSG_STAT_ERROR_CRC:
+ case UCS_MSG_STAT_ERROR_ID:
+ case UCS_MSG_STAT_ERROR_ACK:
+ case UCS_MSG_STAT_ERROR_TIMEOUT:
+ res = UCS_AMSTX_RES_ERR_RETRIES_EXP; /* transmission failed, retries are possible */
+ break;
+ case UCS_MSG_STAT_ERROR_FATAL_WT:
+ case UCS_MSG_STAT_ERROR_FATAL_OA:
+ if (SELF_TX->internal_status == AMSG_TX_INTRES_ERRBUF)
+ {
+ res = UCS_AMSTX_RES_ERR_BUF_INTERNAL; /* internal transmission error and network node not found */
+ }
+ else if (SELF_TX->internal_status == AMSG_TX_INTRES_NONE)
+ {
+ res = UCS_AMSTX_RES_ERR_INVALID_TGT; /* not transmitted internally and no network node found */
+ }
+ /* else -> internal success -> target node was found locally */
+ break;
+ case UCS_MSG_STAT_ERROR_NA_TRANS:
+ case UCS_MSG_STAT_ERROR_NA_OFF:
+ if (SELF_TX->internal_status != AMSG_TX_INTRES_SUCCESS)
+ {
+ res = UCS_AMSTX_RES_ERR_NOT_AVAILABLE; /* successful if internal transmission succeeded, otherwise "not available" */
+ }
+ break;
+ case UCS_MSG_STAT_ERROR_SYNC:
+ res = UCS_AMSTX_RES_ERR_NOT_AVAILABLE;
+ break;
+ default:
+ res = UCS_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
+ */
+Ucs_AmsTx_Info_t Amsg_TxGetResultInfo(Ucs_AmsTx_Msg_t *self)
+{
+ Ucs_AmsTx_Info_t res = (Ucs_AmsTx_Info_t)SELF_TX->temp_result;
+
+ if ((SELF_TX->temp_result == UCS_MSG_STAT_ERROR_FATAL_WT) && (SELF_TX->ignore_wrong_target != false))
+ {
+ res = UCS_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(Ucs_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(Ucs_AmsTx_Msg_t *self)
+{
+ return SELF_TX->next_segment_cnt;
+}
+
+/*! \brief Increments the next segment count
+ * \param self The instance
+ */
+void Amsg_TxIncrementNextSegmCnt(Ucs_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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
+ */
+Ucs_AmsTx_Msg_t* Amsg_TxPeek(CDlList* list_ptr)
+{
+ Ucs_AmsTx_Msg_t *msg_ptr = NULL;
+ CDlNode *node_ptr = Dl_PeekHead(list_ptr);
+
+ if (node_ptr != NULL)
+ {
+ msg_ptr = (Ucs_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
+ */
+Ucs_AmsTx_Msg_t* Amsg_TxDequeue(CDlList* list_ptr)
+{
+ Ucs_AmsTx_Msg_t *msg_ptr = NULL;
+ CDlNode *node_ptr = Dl_PopHead(list_ptr);
+
+ if (node_ptr != NULL)
+ {
+ msg_ptr = (Ucs_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(Ucs_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(Ucs_AmsRx_Msg_t *self, Ucs_AmsTx_Msg_t *tx_ptr, uint16_t source_address)
+{
+ TR_ASSERT(NULL,"[AMSG]", (SELF_RX->memory_sz >= tx_ptr->data_size));
+
+ self->receive_type = Amsg_RxGetReceiveType(tx_ptr->destination_address);
+ self->source_address = source_address;
+ self->msg_id = tx_ptr->msg_id;
+ 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(Ucs_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(Ucs_AmsRx_Msg_t *self, Msg_MostTel_t *tel_ptr)
+{
+ bool result = false;
+ uint16_t msg_id = Msg_GetAltMsgId((CMessage*)(void*)tel_ptr);
+
+ if ((self->source_address == tel_ptr->source_addr)
+ && (self->msg_id == msg_id))
+ {
+ result = true;
+ }
+
+ 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(Ucs_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->msg_id = Msg_GetAltMsgId((CMessage*)(void*)src_ptr);
+}
+
+/*! \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(Ucs_AmsRx_Msg_t *self, Msg_MostTel_t* target_ptr)
+{
+ target_ptr->source_addr = self->source_address;
+ target_ptr->destination_addr = UCS_ADDR_DEBUG;
+ Msg_SetAltMsgId((CMessage*)(void*)target_ptr, self->msg_id);
+}
+
+/*! \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 Ucs_AmsRx_ReceiveType_t Amsg_RxGetReceiveType(uint16_t destination_address)
+{
+ Ucs_AmsRx_ReceiveType_t ret = UCS_AMSRX_RCT_SINGLECAST;
+
+ if ((destination_address == UCS_ADDR_BROADCAST_BLOCKING) ||
+ (destination_address == UCS_ADDR_BROADCAST_UNBLOCKING))
+ {
+ ret = UCS_AMSRX_RCT_BROADCAST;
+ }
+ else if ((destination_address >= 0x0300U) && /* 0x300..0x3FF is reserved for group cast */
+ (destination_address < 0x0400U))
+ {
+ ret = UCS_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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(Ucs_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
+ */
+Ucs_AmsRx_Msg_t* Amsg_RxPeek(CDlList* list_ptr)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = NULL;
+ CDlNode *node_ptr = Dl_PeekHead(list_ptr);
+
+ if (node_ptr != NULL)
+ {
+ msg_ptr = (Ucs_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
+ */
+Ucs_AmsRx_Msg_t* Amsg_RxDequeue(CDlList* list_ptr)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = NULL;
+ CDlNode *node_ptr = Dl_PopHead(list_ptr);
+
+ if (node_ptr != NULL)
+ {
+ msg_ptr = (Ucs_AmsRx_Msg_t*)Dln_GetData(node_ptr);
+ }
+
+ return msg_ptr;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_amspool.c b/ucs2-lib/src/ucs_amspool.c
new file mode 100644
index 0000000..5c20d22
--- /dev/null
+++ b/ucs2-lib/src/ucs_amspool.c
@@ -0,0 +1,335 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Application Message Pool
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_AMSPOOL
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_amspool.h"
+#include "ucs_amsmessage.h"
+#include "ucs_misc.h"
+#include "ucs_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, Ucs_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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Amsp_Ctor(CAmsMsgPool *self, Ams_MemAllocator_t *mem_allocator_ptr, void *ucs_user_ptr)
+{
+ self->ucs_user_ptr = ucs_user_ptr;
+ 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->ucs_user_ptr);
+ Sub_Ctor(&self->rx_freed_subject, self->ucs_user_ptr);
+
+ TR_ASSERT(self->ucs_user_ptr, "[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->ucs_user_ptr, "[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, AMS_MU_RX_PAYLOAD, msg_ptr->memory_info_ptr);
+ self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, AMS_MU_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.
+ */
+Ucs_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;
+ Ucs_AmsTx_Msg_t *msg_ptr = (Ucs_AmsTx_Msg_t*)self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, AMSG_TX_OBJECT_SZ, AMS_MU_TX_OBJECT, &obj_info_ptr);
+ TR_INFO((self->ucs_user_ptr, "[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, AMS_MU_TX_PAYLOAD, &payload_info_ptr);
+ TR_INFO((self->ucs_user_ptr, "[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->ucs_user_ptr, "[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, AMS_MU_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, Ucs_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_->ucs_user_ptr, "[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, AMS_MU_TX_PAYLOAD, obj_ptr->memory_info_ptr);
+ Amsg_TxSetInternalPayload(msg_ptr, NULL, 0U, NULL);
+ }
+
+ TR_INFO((self_->ucs_user_ptr, "[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, AMS_MU_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.
+ */
+Ucs_AmsRx_Msg_t* Amsp_AllocRxObj(CAmsMsgPool *self, uint16_t payload_sz)
+{
+ void *info_ptr = NULL;
+ Ucs_AmsRx_Msg_t *msg_ptr = (Ucs_AmsRx_Msg_t*)self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, AMSG_RX_OBJECT_SZ, AMS_MU_RX_OBJECT, &info_ptr);
+
+ TR_INFO((self->ucs_user_ptr, "[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.
+ */
+Ucs_AmsRx_Msg_t* Amsp_AllocRxRsvd(CAmsMsgPool *self)
+{
+ Ucs_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->ucs_user_ptr, "[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, Ucs_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, AMS_MU_RX_PAYLOAD, &info_ptr);
+
+ TR_INFO((self->ucs_user_ptr, "[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->ucs_user_ptr, "[AMSP]", (msg_ptr != NULL)); /* message reference is required */
+ TR_ASSERT(self->ucs_user_ptr, "[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, Ucs_AmsRx_Msg_t* msg_ptr)
+{
+ if (msg_ptr == self->rx_rsvd_msg_ref)
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[AMSP]", (self->rx_rsvd_msg_ptr == NULL)); /* before freeing, message shall be reserved */
+ TR_INFO((self->ucs_user_ptr, "[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->ucs_user_ptr, "[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, AMS_MU_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, Ucs_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->ucs_user_ptr, "[AMSP]", (self->rx_rsvd_msg_ptr == NULL)); /* release payload before object */
+ TR_INFO((self->ucs_user_ptr, "[AMSP]", "Restoring reserved RxPayload: msg_ptr=0x%p", 1U, msg_ptr));
+ }
+ else if (obj_ptr->memory_ptr != NULL)
+ {
+ TR_INFO((self->ucs_user_ptr, "[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, AMS_MU_RX_PAYLOAD, obj_ptr->memory_info_ptr);
+ Amsg_RxHandleSetMemory(msg_ptr, NULL, 0U, NULL);
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_amtp.c b/ucs2-lib/src/ucs_amtp.c
new file mode 100644
index 0000000..e4550d6
--- /dev/null
+++ b/ucs2-lib/src/ucs_amtp.c
@@ -0,0 +1,114 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Application Message Tx Pool
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_AMTP
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_amtp.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Amtp_OnMsgFreed(void *self, Ucs_AmsTx_Msg_t* msg_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of class CAmtp
+ * \param self The instance
+ * \param msg_ptr Reference to an array of Amsg_IntMsgTx_t objects
+ * \param data_ptr Reference to payload data which is required for the payload of all messages.
+ * The data size must be the product of message_cnt and payload_cnt.
+ * \param msg_cnt The number of message objects in the array
+ * \param payload_sz The payload size for each message. The size must be a multiple of "4".
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Amtp_Ctor(CAmtp *self, Amsg_IntMsgTx_t msg_ptr[], uint8_t data_ptr[], uint8_t msg_cnt, uint16_t payload_sz, void *ucs_user_ptr)
+{
+ uint8_t i = 0U;
+ uint32_t mem_idx = 0U;
+ Ucs_AmsTx_Msg_t *tx_ptr;
+
+ self->ucs_user_ptr = ucs_user_ptr;
+ TR_ASSERT(self->ucs_user_ptr, "[AMTP]", ((payload_sz % 4U) == 0U)); /* payload_sz shall be rounded to full quadlet */
+ TR_ASSERT(self->ucs_user_ptr, "[AMTP]", ((payload_sz * msg_cnt) <= 65535U)); /* total data shall be referenced by uint32_t index */
+
+ Dl_Ctor(&self->msg_queue, self->ucs_user_ptr);
+
+ for (i = 0U; i < msg_cnt; i++)
+ {
+ tx_ptr = (Ucs_AmsTx_Msg_t*)(void*)&(msg_ptr[i]);
+ Amsg_TxCtor(tx_ptr, NULL, &Amtp_OnMsgFreed, self);
+
+ if (payload_sz > 0U)
+ {
+ Amsg_TxSetInternalPayload(tx_ptr, &data_ptr[mem_idx], payload_sz, NULL);
+ mem_idx += payload_sz;
+ }
+
+ Amsg_TxEnqueue(tx_ptr, &self->msg_queue);
+ }
+
+}
+
+/*! \brief Retrieves a Tx application message object
+ * \param self The instance
+ * \return Retrieves the reference to a Tx application message object if the allocation
+ * succeeded, otherwise \c NULL.
+ */
+Ucs_AmsTx_Msg_t* Amtp_AllocMsg(CAmtp *self)
+{
+ return Amsg_TxDequeue(&self->msg_queue);
+}
+
+/*! \brief Callback function which is invoked if the message object is freed
+ * by the AMS
+ * \param self The instance
+ * \param msg_ptr Reference to the freed application Tx message object
+ */
+static void Amtp_OnMsgFreed(void *self, Ucs_AmsTx_Msg_t* msg_ptr)
+{
+ CAmtp *self_ = (CAmtp*)self;
+
+ Amsg_TxReuse(msg_ptr);
+ Amsg_TxEnqueue(msg_ptr, &self_->msg_queue);
+}
+
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_attach.c b/ucs2-lib/src/ucs_attach.c
new file mode 100644
index 0000000..33604ef
--- /dev/null
+++ b/ucs2-lib/src/ucs_attach.c
@@ -0,0 +1,607 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of CAttachService class
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_ATS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_attach.h"
+#include "ucs_pmevent.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the ATS service used by scheduler */
+static const uint8_t ATS_SRV_PRIO = 254U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! Main event for the ATS service */
+static const Srv_Event_t ATS_EVENT_SERVICE = 1U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Initialization timeout in milliseconds (t = 3s) */
+static const uint16_t ATS_INIT_TIMEOUT = 3000U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal definitions */
+/*------------------------------------------------------------------------------------------------*/
+#define ATS_NUM_STATES 11U /*!< \brief Number of state machine states */
+#define ATS_NUM_EVENTS 5U /*!< \brief Number of state machine events */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal enumerators */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Possible events of the attach state machine */
+typedef enum Ats_Events_
+{
+ ATS_E_NIL = 0, /*!< \brief NIL Event */
+ ATS_E_NEXT = 1, /*!< \brief Go to next state */
+ ATS_E_RETRY = 2, /*!< \brief Retry current action */
+ ATS_E_ERROR = 3, /*!< \brief An error has been occurred */
+ ATS_E_TIMEOUT = 4 /*!< \brief An timeout has been occurred */
+
+} Ats_Events_t;
+
+/*! \brief States of the attach state machine */
+typedef enum Ats_State_
+{
+ ATS_S_START = 0, /*!< \brief Start state */
+ ATS_S_PMS_UNSYNC = 1, /*!< \brief Initially un-synchronizes all FIFOs */
+ ATS_S_PMS_INIT = 2, /*!< \brief PMS initialization state */
+ ATS_S_VERS_CHK = 3, /*!< \brief Version check state */
+ ATS_S_INIC_OVHL = 4, /*!< \brief INIC overhaul state */
+ ATS_S_DEV_ATT_STAGE_1 = 5, /*!< \brief Device attach state 1 (wait for first condition) */
+ ATS_S_DEV_ATT_STAGE_2 = 6, /*!< \brief Device attach state 2 (wait for second condition) */
+ ATS_S_DEV_ATT_STAGE_3 = 7, /*!< \brief Device attach state 3 (wait for third condition) */
+ ATS_S_NW_CONFIG = 8, /*!< \brief Retrieve network configuration */
+ ATS_S_INIT_CPL = 9, /*!< \brief Initialization complete state */
+ ATS_S_ERROR = 10 /*!< \brief Error state */
+
+} Ats_State_t;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Ats_TimeoutCb(void *self);
+static void Ats_Service(void *self);
+static void Ats_ResetObservers(CAttachService *self);
+static void Ats_StartPmsUnsync(void *self);
+static void Ats_StartPmsInit(void *self);
+static void Ats_StartVersChk(void *self);
+static void Ats_StartInicOvhl(void *self);
+static void Ats_StartDevAtt(void *self);
+static void Ats_StartNwConfig(void *self);
+static void Ats_InitCpl(void *self);
+static void Ats_HandleInternalErrors(void *self, void *error_code_ptr);
+static void Ats_HandleError(void *self);
+static void Ats_HandleTimeout(void *self);
+static void Ats_InvalidTransition(void *self);
+static void Ats_CheckPmsUnsyncResult(void *self, void *result_ptr);
+static void Ats_CheckPmsInitResult(void *self, void *result_ptr);
+static void Ats_CheckVersChkResult(void *self, void *result_ptr);
+static void Ats_CheckNetworkStatusReceived(void *self, void *result_ptr);
+static void Ats_CheckDeviceStatusReceived(void *self, void *data_ptr);
+static void Ats_CheckDevAttResult(void *self, void *result_ptr);
+static void Ats_CheckNwConfigStatus(void *self, void *result_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* State transition table (used by finite state machine) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief State transition table */
+static const Fsm_StateElem_t ats_trans_tab[ATS_NUM_STATES][ATS_NUM_EVENTS] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+/* |--------------------------------|------------------------------------------------|------------------------------------------------|--------------------------------------|----------------------------------------|
+ * | ATS_E_NIL | ATS_E_NEXT | ATS_E_RETRY | ATS_E_ERROR | ATS_E_TIMEOUT |
+ * |--------------------------------|------------------------------------------------|------------------------------------------------|--------------------------------------|----------------------------------------|
+ */
+ { {NULL, ATS_S_START }, {&Ats_StartPmsUnsync, ATS_S_PMS_UNSYNC }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_InvalidTransition, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_PMS_UNSYNC }, {&Ats_StartPmsInit, ATS_S_PMS_INIT }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_PMS_INIT }, {&Ats_StartVersChk, ATS_S_VERS_CHK }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_VERS_CHK }, {&Ats_StartInicOvhl, ATS_S_INIC_OVHL }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_INIC_OVHL }, {&Ats_StartDevAtt, ATS_S_DEV_ATT_STAGE_1}, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_DEV_ATT_STAGE_1}, {NULL, ATS_S_DEV_ATT_STAGE_2}, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_DEV_ATT_STAGE_2}, {NULL, ATS_S_DEV_ATT_STAGE_3}, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_DEV_ATT_STAGE_3}, {&Ats_StartNwConfig, ATS_S_NW_CONFIG }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_NW_CONFIG }, {&Ats_InitCpl, ATS_S_INIT_CPL }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_HandleError, ATS_S_ERROR}, {&Ats_HandleTimeout, ATS_S_ERROR} },
+ { {NULL, ATS_S_INIT_CPL }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_InvalidTransition, ATS_S_ERROR}, {&Ats_InvalidTransition, ATS_S_ERROR} },
+ { {NULL, ATS_S_ERROR }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_InvalidTransition, ATS_S_ERROR }, {&Ats_InvalidTransition, ATS_S_ERROR}, {&Ats_InvalidTransition, ATS_S_ERROR} }
+};
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CAttachService */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the attach service class
+ * \param self Instance pointer
+ * \param init_ptr Reference to the initialization data
+ */
+void Ats_Ctor(CAttachService *self, Ats_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ T_Ctor(&self->timer);
+ self->report_result = UCS_INIT_RES_SUCCESS;
+ self->init_data = *init_ptr;
+ Ssub_Ctor(&self->ats_result_subject, self->init_data.base_ptr->ucs_user_ptr);
+ Fsm_Ctor(&self->fsm, self, &(ats_trans_tab[0][0]), ATS_NUM_EVENTS, ATS_S_START);
+ /* Initialize ATS service */
+ Srv_Ctor(&self->ats_srv, ATS_SRV_PRIO, self, &Ats_Service);
+ /* Add ATS service to scheduler */
+ (void)Scd_AddService(&self->init_data.base_ptr->scd, &self->ats_srv);
+}
+
+/*! \brief Starts the attach process and the initialization timeout.
+ * \param self Instance pointer
+ * \param obs_ptr Reference to result observer
+ */
+void Ats_Start(void *self, CSingleObserver *obs_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ /* Observe internal errors during the attach process */
+ Mobs_Ctor(&self_->internal_error_obs, self_, (EH_E_BIST_FAILED | EH_E_SYNC_LOST), &Ats_HandleInternalErrors);
+ Eh_AddObsrvInternalEvent(&self_->init_data.base_ptr->eh, &self_->internal_error_obs);
+ /* Set first event of attach state machine */
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ /* Start timeout timer used for attach process */
+ Tm_SetTimer(&self_->init_data.base_ptr->tm,
+ &self_->timer,
+ &Ats_TimeoutCb,
+ self_,
+ ATS_INIT_TIMEOUT,
+ 0U);
+ (void)Ssub_AddObserver(&self_->ats_result_subject, obs_ptr);
+}
+
+/*! \brief Timer callback used for initialization timeout.
+ * \param self Instance pointer
+ */
+static void Ats_TimeoutCb(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Fsm_SetEvent(&self_->fsm, ATS_E_TIMEOUT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+}
+
+/*! \brief Service function of the attach service.
+ * \param self Instance pointer
+ */
+static void Ats_Service(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->ats_srv, &event_mask);
+ if (ATS_EVENT_SERVICE == (event_mask & ATS_EVENT_SERVICE)) /* Is event pending? */
+ {
+ Fsm_State_t result;
+ Srv_ClearEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ result = Fsm_Service(&self_->fsm);
+ TR_ASSERT(self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", (result != FSM_STATE_ERROR));
+ MISC_UNUSED(result);
+ }
+}
+
+/*! \brief Resets all module internal observers.
+ * \param self Instance pointer
+ */
+static void Ats_ResetObservers(CAttachService *self)
+{
+ Eh_DelObsrvInternalEvent(&self->init_data.base_ptr->eh, &self->internal_error_obs);
+ Sobs_Ctor(&self->sobs, NULL, NULL);
+ Obs_Ctor(&self->obs, NULL, NULL);
+ Obs_Ctor(&self->obs2, NULL, NULL);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* State machine actions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief un-synchronizes PMS and observes PM events
+ * \param self Instance pointer
+ */
+static void Ats_StartPmsUnsync(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Obs_Ctor(&self_->obs, self_, &Ats_CheckPmsUnsyncResult);
+ Fifos_ConfigureSyncParams(self_->init_data.fifos_ptr, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT);
+ Fifos_Unsynchronize(self_->init_data.fifos_ptr, true, true);
+ Fifos_AddEventObserver(self_->init_data.fifos_ptr, &self_->obs);
+}
+
+/*! \brief Synchronizes PMS and observes PM events
+ * \param self Instance pointer
+ */
+static void Ats_StartPmsInit(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Obs_Ctor(&self_->obs, self_, &Ats_CheckPmsInitResult);
+ Pmev_Start(self_->init_data.pme_ptr); /* enables failure reporting to all modules */
+ Fifos_Synchronize(self_->init_data.fifos_ptr, false, true); /* now synchronizes, counter is not reset to "0" */
+ Fifos_AddEventObserver(self_->init_data.fifos_ptr, &self_->obs);
+}
+
+/*! \brief Starts the request of the INIC firmware and hardware revisions.
+ * \param self Instance pointer
+ */
+static void Ats_StartVersChk(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Sobs_Ctor(&self_->sobs, self_, &Ats_CheckVersChkResult);
+ if (Inic_DeviceVersion_Get(self_->init_data.inic_ptr,
+ &self_->sobs) != UCS_RET_SUCCESS)
+ {
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "INIC device version check failed!", 0U));
+ self_->report_result = UCS_INIT_RES_ERR_BUF_OVERFLOW;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ }
+}
+
+/*! \brief Starts the overhaul process of the INIC.
+ * \param self Instance pointer
+ */
+static void Ats_StartInicOvhl(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+}
+
+/*! \brief Starts the attach process between EHC and INIC.
+ * \param self Instance pointer
+ */
+static void Ats_StartDevAtt(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_StartDevAtt() called", 0U));
+ /* Assign observer to monitor the initial receipt of INIC message INIC.MOSTNetworkStatus */
+ Obs_Ctor(&self_->obs, self_, &Ats_CheckNetworkStatusReceived);
+ Inic_AddObsrvNwStatus(self_->init_data.inic_ptr, &self_->obs);
+ /* Assign observer to monitor the initial receipt of INIC message INIC.DeviceStatus */
+ Obs_Ctor(&self_->obs2, self_, &Ats_CheckDeviceStatusReceived);
+ Inic_AddObsvrDeviceStatus(self_->init_data.inic_ptr, &self_->obs2);
+
+ /* Start device attach process */
+ Sobs_Ctor(&self_->sobs, self_, &Ats_CheckDevAttResult);
+ if (Inic_DeviceAttach(self_->init_data.inic_ptr, &self_->sobs) != UCS_RET_SUCCESS)
+ {
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "INIC device attach failed!", 0U));
+ self_->report_result = UCS_INIT_RES_ERR_BUF_OVERFLOW;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ }
+}
+
+/*! \brief Starts request of network configuration property required
+ * to retrieve the own group address.
+ * \param self Instance pointer
+ */
+static void Ats_StartNwConfig(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+
+ /* Assign observer to monitor the initial receipt of INIC message INIC.MOSTNetworkConfigurarion */
+ Sobs_Ctor(&self_->sobs, self_, &Ats_CheckNwConfigStatus);
+
+ if (Inic_NwConfig_Get(self_->init_data.inic_ptr, &self_->sobs) != UCS_RET_SUCCESS)
+ {
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "INIC network configuration failed!", 0U));
+ self_->report_result = UCS_INIT_RES_ERR_BUF_OVERFLOW;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ }
+}
+
+/*! \brief This method is called when the initialization has been completed.
+ * \param self Instance pointer
+ */
+static void Ats_InitCpl(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ self_->report_result = UCS_INIT_RES_SUCCESS;
+ /* Attach process finished -> Reset observers and terminate state machine */
+ Ats_ResetObservers(self_);
+ Tm_ClearTimer(&self_->init_data.base_ptr->tm, &self_->timer);
+ Fsm_End(&self_->fsm);
+ Eh_ReportEvent(&self_->init_data.base_ptr->eh, EH_E_INIT_SUCCEEDED);
+ Ssub_Notify(&self_->ats_result_subject, &self_->report_result, true);
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_InitCpl() called", 0U));
+}
+
+/*! \brief Handles internal errors during the attach process.
+ * \param self Instance pointer
+ * \param error_code_ptr Reference to reported error code
+ */
+static void Ats_HandleInternalErrors(void *self, void *error_code_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ uint32_t error_code = *((uint32_t *)error_code_ptr);
+ switch (error_code)
+ {
+ case EH_E_SYNC_LOST:
+ self_->report_result = UCS_INIT_RES_ERR_INIC_SYNC;
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "A control FiFo synchronization lost!", 0U));
+ break;
+ case EH_E_BIST_FAILED:
+ self_->report_result = UCS_INIT_RES_ERR_INIC_SYSTEM;
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "INIC Build-In-Self-Test failed!", 0U));
+ break;
+ default:
+ self_->report_result = UCS_INIT_RES_ERR_INTERNAL;
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Unknown internal error occurred! Error code: 0x%04X", 1U, error_code));
+ break;
+ }
+ /* Error occurred -> Reset observers and terminate state machine */
+ Ats_ResetObservers(self_);
+ Tm_ClearTimer(&self_->init_data.base_ptr->tm, &self_->timer);
+ Fsm_End(&self_->fsm);
+ Eh_ReportEvent(&self_->init_data.base_ptr->eh, EH_E_INIT_FAILED);
+ Ssub_Notify(&self_->ats_result_subject, &self_->report_result, true);
+}
+
+/*! \brief Handles general errors during the attach process.
+ * \param self Instance pointer
+ */
+static void Ats_HandleError(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ /* Error occurred -> Reset observers and terminate state machine */
+ Ats_ResetObservers(self_);
+ Tm_ClearTimer(&self_->init_data.base_ptr->tm, &self_->timer);
+ Fsm_End(&self_->fsm);
+ Eh_ReportEvent(&self_->init_data.base_ptr->eh, EH_E_INIT_FAILED);
+ Ssub_Notify(&self_->ats_result_subject, &self_->report_result, true);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Error occurred during initialization!", 0U));
+}
+
+/*! \brief Handles timeouts during the attach process.
+ * \param self Instance pointer
+ */
+static void Ats_HandleTimeout(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ self_->report_result = UCS_INIT_RES_ERR_TIMEOUT;
+ /* Error occurred -> Reset observers and terminate state machine */
+ Ats_ResetObservers(self_);
+ Fsm_End(&self_->fsm);
+ Eh_ReportEvent(&self_->init_data.base_ptr->eh, EH_E_INIT_FAILED);
+ Ssub_Notify(&self_->ats_result_subject, &self_->report_result, true);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Initialization timeout occurred!", 0U));
+}
+
+/*! \brief This method is invoked if an invalid state machine transition is executed.
+ * \param self Instance pointer
+ */
+static void Ats_InvalidTransition(void *self)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ self_->report_result = UCS_INIT_RES_ERR_INTERNAL;
+ /* Invalid Transition -> Reset observers and terminate state machine */
+ Ats_ResetObservers(self_);
+ Tm_ClearTimer(&self_->init_data.base_ptr->tm, &self_->timer);
+ Fsm_End(&self_->fsm);
+ Eh_ReportEvent(&self_->init_data.base_ptr->eh, EH_E_INIT_FAILED);
+ Ssub_Notify(&self_->ats_result_subject, &self_->report_result, true);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Invalid transition within ATS state machine!", 0U));
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of the observer results */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Result callback for action "PMS Initialization". This function is part of an
+ * observer object and is invoked by Sub_Notify().
+ * \param self Instance pointer
+ * \param result_ptr Reference to the received PMS event. The pointer must be casted into
+ * data type Fifos_Event_t.
+ */
+static void Ats_CheckPmsUnsyncResult(void *self, void *result_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Fifos_Event_t pms_event = *((Fifos_Event_t *)result_ptr);
+
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckPmsUnsyncResult() called", 0U));
+
+ if (pms_event == FIFOS_EV_UNSYNC_COMPLETE)
+ {
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ }
+ else
+ {
+ self_->report_result = UCS_INIT_RES_ERR_INIC_SYNC;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckPmsUnsyncResult(): un-sync failed, event=0x%02X", 1U, pms_event));
+ }
+ Fifos_RemoveEventObserver(self_->init_data.fifos_ptr, &self_->obs);
+}
+
+/*! \brief Result callback for action "PMS Initialization". This function is part of an
+ * observer object and is invoked by Sub_Notify().
+ * \param self Instance pointer
+ * \param result_ptr Reference to the received PMS event. The pointer must be casted into
+ * data type Fifos_Event_t.
+ */
+static void Ats_CheckPmsInitResult(void *self, void *result_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Fifos_Event_t pms_event = *((Fifos_Event_t *)result_ptr);
+
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckPmsInitResult() called", 0U));
+
+ if (pms_event == FIFOS_EV_SYNC_ESTABLISHED)
+ {
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ }
+ else
+ {
+ self_->report_result = UCS_INIT_RES_ERR_INIC_SYNC;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckPmsInitResult(): sync failed, event=0x%02X", 1U, pms_event));
+ }
+ Fifos_RemoveEventObserver(self_->init_data.fifos_ptr, &self_->obs);
+}
+
+/*! \brief Result callback for action "Version Check". This function is part of a single
+ * observer object and is invoked by Ssub_Notify().
+ * \param self Instance pointer
+ * \param result_ptr Reference to the received version check result. The pointer must be casted
+ * into data type Inic_StdResult_t.
+ */
+static void Ats_CheckVersChkResult(void *self, void *result_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ }
+ else
+ {
+ self_->report_result = UCS_INIT_RES_ERR_INIC_VERSION;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "INIC version check failed!", 0U));
+ }
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckVersChkResult() called", 0U));
+}
+
+/*! \brief Result callback which handles one of three conditions for action "Device Attach". The
+ * function is called if INIC message INIC.MOSTNetworkStatus was received. The function is
+ * part of an observer object and is invoked by Sub_Notify(). The property
+ * INIC.MOSTNetworkStatus.Status() is notified. Thus, there is no error condition available.
+ * \param self Instance pointer
+ * \param result_ptr Reference to the MOST Network Status. The pointer must be casted into data
+ * type Inic_StdResult_t.
+ */
+static void Ats_CheckNetworkStatusReceived(void *self, void *result_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Inic_DelObsrvNwStatus(self_->init_data.inic_ptr, &self_->obs);
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ MISC_UNUSED(result_ptr);
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckNetworkStatusReceived() called", 0U));
+}
+
+/*! \brief Observer callback that is notified on received INIC.DeviceStatus
+ * \param self Instance pointer
+ * \param data_ptr The pointer to the current INIC.DeviceStatus structure
+ */
+static void Ats_CheckDeviceStatusReceived(void *self, void *data_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Inic_DelObsvrDeviceStatus(self_->init_data.inic_ptr, &self_->obs2);
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ MISC_UNUSED(data_ptr);
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckDeviceStatusReceived() called", 0U));
+}
+
+/*! \brief Result callback which handles one of two conditions for action "Device Attach". The
+ * function handles the result of the INIC method INIC.DeviceAttach. This function is part
+ * of a single-observer object and is invoked by Ssub_Notify().
+ * \param self Instance pointer
+ * \param result_ptr Reference to the received device attach result. The pointer must be casted
+ * into data type Inic_StdResult_t.
+ */
+static void Ats_CheckDevAttResult(void *self, void *result_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Inic_StdResult_t error_data = *((Inic_StdResult_t *)result_ptr);
+ switch (error_data.result.code)
+ {
+ case UCS_RES_SUCCESS:
+ /* Operation succeeded */
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ break;
+ case UCS_RES_ERR_CONFIGURATION:
+ /* Configuration error occurred -> attach process failed! */
+ self_->report_result = UCS_INIT_RES_ERR_DEV_ATT_CFG;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Device attach failed due to an configuration error!", 0U));
+ TR_ERROR_INIC_RESULT(self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", error_data.result.info_ptr, error_data.result.info_size);
+ break;
+ case UCS_RES_ERR_SYSTEM:
+ /* INIC is still attached -> attach process failed! */
+ self_->report_result = UCS_INIT_RES_ERR_DEV_ATT_PROC;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "EHC is already attached to the INIC!", 0U));
+ break;
+ default:
+ /* INIC reports an unexpected error -> attach process failed! */
+ self_->report_result = UCS_INIT_RES_ERR_DEV_ATT_PROC;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Device attach failed! Unexpected error code = 0x%02X", 1U, error_data.result.code));
+ TR_ERROR_INIC_RESULT(self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", error_data.result.info_ptr, error_data.result.info_size);
+ break;
+ }
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckDevAttResult() called", 0U));
+}
+
+/*! \brief Result callback for INIC network configuration
+ * \param self Instance pointer
+ * \param result_ptr Reference to the received network configuration status event.
+ * The pointer must be casted into data type Inic_StdResult_t.
+ */
+static void Ats_CheckNwConfigStatus(void *self, void *result_ptr)
+{
+ CAttachService *self_ = (CAttachService *)self;
+ Inic_StdResult_t error_data = *((Inic_StdResult_t *)result_ptr);
+
+ if (error_data.result.code == UCS_RES_SUCCESS)
+ {
+ /* Operation succeeded */
+ Fsm_SetEvent(&self_->fsm, ATS_E_NEXT);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ }
+ else
+ {
+ /* INIC reports an unexpected error -> attach process failed! */
+ self_->report_result = UCS_INIT_RES_ERR_NET_CFG;
+ Fsm_SetEvent(&self_->fsm, ATS_E_ERROR);
+ Srv_SetEvent(&self_->ats_srv, ATS_EVENT_SERVICE);
+ TR_ERROR((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Network configuration failed! Unexpected error code = 0x%02X", 1U, error_data.result.code));
+ TR_ERROR_INIC_RESULT(self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", error_data.result.info_ptr, error_data.result.info_size);
+ }
+ TR_INFO((self_->init_data.base_ptr->ucs_user_ptr, "[ATS]", "Ats_CheckNwConfigStatus() called", 0U));
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_base.c b/ucs2-lib/src/ucs_base.c
new file mode 100644
index 0000000..583dd80
--- /dev/null
+++ b/ucs2-lib/src/ucs_base.c
@@ -0,0 +1,69 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Base class.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_BASE
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_base.h"
+#include "ucs_misc.h"
+#include "ucs_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 instance ID and user pointer */
+ self->ucs_inst_id = init_ptr->ucs_inst_id;
+ self->ucs_user_ptr = init_ptr->ucs_user_ptr;
+ /* Create the scheduler instance */
+ Scd_Ctor(&self->scd, &init_ptr->scd, init_ptr->ucs_user_ptr);
+ /* Create the timer management instance */
+ Tm_Ctor(&self->tm, &self->scd, &init_ptr->tm, init_ptr->ucs_user_ptr);
+ /* Create the event handler instance */
+ Eh_Ctor(&self->eh, init_ptr->ucs_user_ptr);
+ /* Create the API locking manager instance */
+ Alm_Ctor(&self->alm, &self->tm, &self->eh, init_ptr->ucs_user_ptr);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_bc_diag.c b/ucs2-lib/src/ucs_bc_diag.c
new file mode 100644
index 0000000..c932c90
--- /dev/null
+++ b/ucs2-lib/src/ucs_bc_diag.c
@@ -0,0 +1,784 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the BackChannel Diagnosis.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_BACKCHANNEL_DIAG
+ * @{
+
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_inic_pb.h"
+#include "ucs_bc_diag.h"
+#include "ucs_misc.h"
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+#define BCD_NUM_STATES 7U /*!< \brief Number of state machine states */
+#define BCD_NUM_EVENTS 12U /*!< \brief Number of state machine events */
+
+#define BCD_TIMEOUT_COMMAND 100U /*!< \brief supervise EXC commands */
+
+#define BCD_SIGNATURE_VERSION 1U /*!< \brief signature version used for BackChannel Diagnosis */
+
+#define BCD_T_SEND 0x0100U
+#define BCD_T_WAIT4DUT 0x1000U
+#define BCD_T_SWITCH 0x0100U
+#define BCD_T_BACK 0x2000U
+#define BCD_TIMEOUT2 0x3000U
+#define BCD_T_SIGNAL_ON 100U
+#define BCD_T_LOCK 100U
+#define BCD_T_LIGHT_PROGRESS 20U
+#define BCD_AUTOBACK (true)
+#define ADMIN_BASE_ADDR 0x0F00U
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the BackChannel Diagnosis used by scheduler */
+static const uint8_t BCD_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! Main event for the BackChannel Diagnosis */
+static const Srv_Event_t BCD_EVENT_SERVICE = 1U;
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal enumerators */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Possible events of the BackChannel Diagnosis state machine */
+typedef enum Bcd_Events_
+{
+ BCD_E_NIL = 0U, /*!< \brief NIL Event */
+ BCD_E_START = 1U, /*!< \brief API start command was called. */
+ BCD_E_DIAGMODE_END = 2U, /*!< \brief INIC.BCDiagEnd.Result successful. */
+ BCD_E_DIAG_MODE_STARTED = 3U, /*!< \brief INIC.BCDiag.Result successful. */
+ BCD_E_DIAG_MODE_FAILED = 4U, /*!< \brief INIC.BCDiag.Error received. */
+ BCD_E_TX_ENABLE_SUCCESS = 5U, /*!< \brief EXC.BCEnableTx successful */
+ BCD_E_TX_ENABLE_FAILED = 6U, /*!< \brief EXC.BCEnableTx failed. */
+ BCD_E_DIAG_RESULT_OK = 7U, /*!< \brief EXC.BCDIAG.Result Ok received. */
+ BCD_E_DIAG_RESULT_NOTOK = 8U, /*!< \brief EXC.BCDIAG.Result NotOk received. */
+ BCD_E_NET_OFF = 9U, /*!< \brief NetOff occurred. */
+ BCD_E_TIMEOUT = 10U, /*!< \brief Timeout occurred. */
+ BCD_E_ERROR = 11U /*!< \brief An unexpected error occurred. */
+
+} Bcd_Events_t;
+
+
+/*! \brief States of the BackChannel Diagnosis state machine */
+typedef enum Bcd_State_
+{
+ BCD_S_IDLE = 0U, /*!< \brief Idle state */
+ BCD_S_STARTED = 1U, /*!< \brief BackChannel Diagnosis started */
+ BCD_S_WAIT_ENABLED = 2U, /*!< \brief Wait for BCEnableTx.Result */
+ BCD_S_WAIT_SIG_PROP = 3U, /*!< \brief Wait for signal propagating through the following nodes */
+ BCD_S_WAIT_SIGNAL_ON = 4U, /*!< \brief Wait for t_SignalOn to expire. */
+ BCD_S_WAIT_RESULT = 5U, /*!< \brief Wait for ENC.BCDiag.Result */
+ BCD_S_END = 6U /*!< \brief BackChannel Diagnosis ends. */
+} Bcd_State_t;
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Bcd_Service(void *self);
+
+static void Bcd_InicBcdStartCb(void *self, void *result_ptr);
+static void Bcd_EnableTxResultCb(void *self, void *result_ptr);
+static void Bcd_DiagnosisResultCb(void *self, void *result_ptr);
+static void Bcd_InicBcdEndCb(void *self, void *result_ptr);
+
+static void Bcd_OnTerminateEventCb(void *self, void *result_ptr);
+static void Bcd_NetworkStatusCb(void *self, void *result_ptr);
+
+static void Bcd_A_Start(void *self);
+static void Bcd_A_EnableTx(void *self);
+static void Bcd_A_DiagStart(void *self);
+static void Bcd_A_NextSeg(void *self);
+static void Bcd_A_StopDiag(void *self);
+static void Bcd_A_Error(void *self);
+static void Bcd_A_EndDiag(void *self);
+static void Bcd_A_Timeout2(void *self);
+static void Bcd_A_WaitLight(void *self);
+
+
+static Ucs_Return_t Bcd_EnableTx(void *self, uint8_t port);
+
+static void Bcd_TimerCb(void *self);
+
+/*------------------------------------------------------------------------------------------------*/
+/* State transition table (used by finite state machine) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief State transition table */
+static const Fsm_StateElem_t bcd_trans_tab[BCD_NUM_STATES][BCD_NUM_EVENTS] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+ { /* State BCD_S_IDLE */
+ /* BCD_E_NIL */ {NULL, BCD_S_IDLE },
+ /* BCD_E_START */ {Bcd_A_Start, BCD_S_STARTED },
+ /* BCD_E_DIAGMODE_END */ {NULL, BCD_S_IDLE },
+ /* BCD_E_DIAG_MODE_STARTED */ {NULL, BCD_S_IDLE },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_IDLE },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {NULL, BCD_S_IDLE },
+ /* BCD_E_TX_ENABLE_FAILED */ {NULL, BCD_S_IDLE },
+ /* BCD_E_DIAG_RESULT_OK */ {NULL, BCD_S_IDLE },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {NULL, BCD_S_IDLE },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_IDLE },
+ /* BCD_E_TIMEOUT */ {NULL, BCD_S_IDLE },
+ /* BCD_E_ERROR */ {NULL, BCD_S_IDLE }
+ },
+ { /* State BCD_S_STARTED */
+ /* BCD_E_NIL */ {NULL, BCD_S_STARTED },
+ /* BCD_E_START */ {NULL, BCD_S_STARTED },
+ /* BCD_E_DIAGMODE_END */ {NULL, BCD_S_STARTED },
+ /* BCD_E_DIAG_MODE_STARTED */ {Bcd_A_EnableTx, BCD_S_WAIT_ENABLED },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_STARTED },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {NULL, BCD_S_STARTED },
+ /* BCD_E_TX_ENABLE_FAILED */ {NULL, BCD_S_STARTED },
+ /* BCD_E_DIAG_RESULT_OK */ {NULL, BCD_S_STARTED },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {NULL, BCD_S_STARTED },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_STARTED },
+ /* BCD_E_TIMEOUT */ {Bcd_A_Timeout2, BCD_S_IDLE },
+ /* BCD_E_ERROR */ {Bcd_A_Error, BCD_S_IDLE }
+ },
+ { /* State BCD_S_WAIT_ENABLED */
+ /* BCD_E_NIL */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_START */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_DIAGMODE_END */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_DIAG_MODE_STARTED */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {Bcd_A_WaitLight, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_TX_ENABLE_FAILED */ {Bcd_A_Error, BCD_S_IDLE },
+ /* BCD_E_DIAG_RESULT_OK */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_WAIT_ENABLED },
+ /* BCD_E_TIMEOUT */ {Bcd_A_Timeout2, BCD_S_IDLE },
+ /* BCD_E_ERROR */ {Bcd_A_Error, BCD_S_IDLE }
+ },
+ { /* State BCD_S_WAIT_SIG_PROP */
+ /* BCD_E_NIL */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_START */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_DIAGMODE_END */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_DIAG_MODE_STARTED */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_TX_ENABLE_FAILED */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_DIAG_RESULT_OK */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_WAIT_SIG_PROP },
+ /* BCD_E_TIMEOUT */ {Bcd_A_DiagStart, BCD_S_WAIT_RESULT },
+ /* BCD_E_ERROR */ {Bcd_A_Error, BCD_S_IDLE }
+ },
+ { /* State BCD_S_WAIT_SIGNAL_ON */
+ /* BCD_E_NIL */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_START */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_DIAGMODE_END */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_DIAG_MODE_STARTED */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_TX_ENABLE_FAILED */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_DIAG_RESULT_OK */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_TIMEOUT */ {Bcd_A_EnableTx, BCD_S_WAIT_ENABLED },
+ /* BCD_E_ERROR */ {Bcd_A_Error, BCD_S_IDLE }
+ },
+ { /* State BCD_S_WAIT_RESULT */
+ /* BCD_E_NIL */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_START */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_DIAGMODE_END */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_DIAG_MODE_STARTED */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_TX_ENABLE_FAILED */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_DIAG_RESULT_OK */ {Bcd_A_NextSeg, BCD_S_WAIT_SIGNAL_ON },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {Bcd_A_StopDiag, BCD_S_END },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_WAIT_RESULT },
+ /* BCD_E_TIMEOUT */ {Bcd_A_Timeout2, BCD_S_IDLE },
+ /* BCD_E_ERROR */ {Bcd_A_Error, BCD_S_IDLE }
+ },
+ { /* State BCD_S_END */
+ /* BCD_E_NIL */ {NULL, BCD_S_END },
+ /* BCD_E_START */ {NULL, BCD_S_END },
+ /* BCD_E_DIAGMODE_END */ {Bcd_A_EndDiag, BCD_S_IDLE },
+ /* BCD_E_DIAG_MODE_STARTED */ {NULL, BCD_S_END },
+ /* BCD_E_DIAG_MODE_FAILED */ {NULL, BCD_S_END },
+ /* BCD_E_TX_ENABLE_SUCCESS */ {NULL, BCD_S_END },
+ /* BCD_E_TX_ENABLE_FAILED */ {NULL, BCD_S_END },
+ /* BCD_E_DIAG_RESULT_OK */ {NULL, BCD_S_END },
+ /* BCD_E_DIAG_RESULT_NOTOK */ {NULL, BCD_S_END },
+ /* BCD_E_NET_OFF */ {NULL, BCD_S_END },
+ /* BCD_E_TIMEOUT */ {Bcd_A_Timeout2, BCD_S_IDLE },
+ /* BCD_E_ERROR */ {Bcd_A_Error, BCD_S_IDLE }
+ }
+};
+
+
+/*! \brief Constructor of class CBackChannelDiag.
+ * \param self Reference to CBackChannelDiag instance
+ * \param inic Reference to CInic instance
+ * \param base Reference to CBase instance
+ * \param exc Reference to CExc instance
+ */
+ /* \param init_ptr Report callback function*/
+void Bcd_Ctor(CBackChannelDiag *self, CInic *inic, CBase *base, CExc *exc)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self));
+
+ self->inic = inic;
+ self->exc = exc;
+ self->base = base;
+
+ Fsm_Ctor(&self->fsm, self, &(bcd_trans_tab[0][0]), BCD_NUM_EVENTS, BCD_E_NIL);
+
+
+ Sobs_Ctor(&self->bcd_inic_bcd_start, self, &Bcd_InicBcdStartCb);
+ Sobs_Ctor(&self->bcd_inic_bcd_end, self, &Bcd_InicBcdEndCb);
+ Sobs_Ctor(&self->bcd_enabletx, self, &Bcd_EnableTxResultCb);
+ Sobs_Ctor(&self->bcd_diagnosis, self, &Bcd_DiagnosisResultCb);
+
+
+ /* register termination events */
+ Mobs_Ctor(&self->bcd_terminate, self, EH_M_TERMINATION_EVENTS, &Bcd_OnTerminateEventCb);
+ Eh_AddObsrvInternalEvent(&self->base->eh, &self->bcd_terminate);
+
+ /* Register NetOn and MPR events */
+ Obs_Ctor(&self->bcd_nwstatus, self, &Bcd_NetworkStatusCb);
+ Inic_AddObsrvNwStatus(self->inic, &self->bcd_nwstatus);
+ self->neton = false;
+
+ /* Initialize Node Discovery service */
+ Srv_Ctor(&self->service, BCD_SRV_PRIO, self, &Bcd_Service);
+ /* Add Node Discovery service to scheduler */
+ (void)Scd_AddService(&self->base->scd, &self->service);
+
+}
+
+
+/*! \brief Service function of the Node Discovery service.
+ * \param self Reference to Node Discovery object
+ */
+static void Bcd_Service(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->service, &event_mask);
+ if(BCD_EVENT_SERVICE == (event_mask & BCD_EVENT_SERVICE)) /* Is event pending? */
+ {
+ Fsm_State_t result;
+ Srv_ClearEvent(&self_->service, BCD_EVENT_SERVICE);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "FSM __ %d %d", 2U, self_->fsm.current_state, self_->fsm.event_occured));
+ result = Fsm_Service(&self_->fsm);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[BCD]", (result != FSM_STATE_ERROR));
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "FSM -> %d", 1U, self_->fsm.current_state));
+ MISC_UNUSED(result);
+ }
+}
+
+
+/**************************************************************************************************/
+/* API functions */
+/**************************************************************************************************/
+/*! \brief Program a node
+ *
+ * \param *self Reference to BackChannel Diagnosis object
+ * \param *report_fptr Reference to result callback used by BackChannel Diagnosis
+*/
+void Bcd_Start(CBackChannelDiag *self, Ucs_Bcd_ReportCb_t report_fptr)
+{
+ self->report_fptr = report_fptr;
+
+ Fsm_SetEvent(&self->fsm, BCD_E_START);
+ Srv_SetEvent(&self->service, BCD_EVENT_SERVICE);
+
+ TR_INFO((self->base->ucs_user_ptr, "[BCD]", "Bcd_Start", 0U));
+
+}
+
+
+
+/**************************************************************************************************/
+/* FSM Actions */
+/**************************************************************************************************/
+static void Bcd_A_Start(void *self)
+{
+ Ucs_Return_t ret_val;
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ /* send INIC.BCDiag.StartResult */
+ ret_val = Inic_BCDiagnosis(self_->inic, &self_->bcd_inic_bcd_start);
+
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Bcd_TimerCb,
+ self_,
+ BCD_TIMEOUT_COMMAND,
+ 0U);
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_ERROR);
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+ }
+
+ self_->current_segment = 0U;
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[BCD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+static void Bcd_A_EnableTx(void *self)
+{
+ Ucs_Return_t ret_val;
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ /* send ENC.EnableTx */
+ ret_val = Bcd_EnableTx(self, 0U);
+
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Bcd_TimerCb,
+ self_,
+ BCD_TIMEOUT_COMMAND,
+ 0U);
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_ERROR);
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+ }
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[BCD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+/*! Starts the diagnosis command for one certain segment.
+ *
+ * \param *self The instance
+ */
+static void Bcd_A_DiagStart(void *self)
+{
+ Ucs_Return_t ret_val;
+ uint16_t t_send = BCD_T_SEND;
+ uint16_t t_wait4dut = BCD_T_WAIT4DUT;
+ uint16_t t_switch = BCD_T_SWITCH;
+ uint16_t t_back = BCD_T_BACK;
+ bool autoback = BCD_AUTOBACK;
+
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ ret_val = Exc_BCDiag_Start(self_->exc,
+ self_->current_segment,
+ ADMIN_BASE_ADDR + self_->current_segment,
+ t_send,
+ t_wait4dut,
+ t_switch,
+ t_back,
+ autoback,
+ &self_->bcd_diagnosis);
+
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Bcd_TimerCb,
+ self_,
+ BCD_TIMEOUT2,
+ 0U);
+
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_ERROR);
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+ }
+
+
+ MISC_UNUSED(ret_val);
+}
+
+
+static void Bcd_A_NextSeg(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ self_->report_fptr(UCS_BCD_RES_SUCCESS,
+ (uint8_t)(self_->bcd_result.admin_addr - ADMIN_BASE_ADDR),
+ self_->base->ucs_user_ptr);
+ self_->current_segment += 1U; /* switch to next segment. */
+
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Bcd_TimerCb,
+ self_,
+ BCD_T_SIGNAL_ON,
+ 0U);
+}
+
+static void Bcd_A_StopDiag(void *self)
+{
+ Ucs_Return_t ret_val;
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ switch(self_->bcd_result.diag_result)
+ {
+ case DUT_MASTER:
+ self_->report_fptr(UCS_BCD_RES_NO_RING_BREAK,
+ (uint8_t)(self_->bcd_result.admin_addr - ADMIN_BASE_ADDR),
+ self_->base->ucs_user_ptr);
+ break;
+
+ case DUT_NO_ANSWER:
+ self_->report_fptr(UCS_BCD_RES_RING_BREAK,
+ (uint8_t)(self_->bcd_result.admin_addr - ADMIN_BASE_ADDR),
+ self_->base->ucs_user_ptr);
+ break;
+
+ case DUT_TIMEOUT:
+ self_->report_fptr(UCS_BCD_RES_TIMEOUT1,
+ (uint8_t)(self_->bcd_result.admin_addr - ADMIN_BASE_ADDR),
+ self_->base->ucs_user_ptr);
+ break;
+
+ default:
+ break;
+ }
+
+ /* finish Back Channel Diagnosis Mode: send INIC.BCDiagEnd.StartResult */
+ ret_val = Inic_BCDiagEnd(self_->inic, &self_->bcd_inic_bcd_end);
+
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Bcd_TimerCb,
+ self_,
+ BCD_TIMEOUT_COMMAND,
+ 0U);
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_ERROR);
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+ }
+
+ MISC_UNUSED(ret_val);
+}
+
+
+static void Bcd_A_EndDiag(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_BCD_RES_END, UCS_BCD_DUMMY_SEGMENT, self_->base->ucs_user_ptr);
+ }
+}
+
+static void Bcd_A_Timeout2(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_BCD_RES_TIMEOUT2, UCS_BCD_DUMMY_SEGMENT, self_->base->ucs_user_ptr);
+ }
+}
+
+static void Bcd_A_WaitLight(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Bcd_TimerCb,
+ self_,
+ BCD_T_LOCK + (BCD_T_LIGHT_PROGRESS * (self_->current_segment + 1U)),
+ 0U);
+}
+
+
+
+
+/*! \brief An unecpected error occurred
+ *
+ * \param *self Reference to BackChannelDiagnosis object
+ */
+static void Bcd_A_Error(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_BCD_RES_ERROR, UCS_BCD_DUMMY_SEGMENT, self_->base->ucs_user_ptr);
+ }
+
+}
+
+
+/**************************************************************************************************/
+/* Callback functions */
+/**************************************************************************************************/
+
+/*! \brief Function is called on reception of the Welcome.Result messsage
+ * \param self Reference to BackChannelDiagnosis object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Bcd_InicBcdStartCb(void *self, void *result_ptr)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_DIAG_MODE_STARTED);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_InicBcdStartCb BCD_E_DIAG_MODE_STARTED", 0U));
+ }
+ else
+ {
+ uint8_t i;
+
+ Fsm_SetEvent(&self_->fsm, BCD_E_DIAG_MODE_FAILED);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_InicBcdStartCb Error (code) 0x%x", 1U, result_ptr_->result.code));
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_InicBcdStartCb Error (info) 0x%x", 1U, result_ptr_->result.info_ptr[i]));
+ }
+ }
+
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+}
+
+
+
+/*! \brief Function is called on reception of the BCEnableTx.Result messsage
+ * \param self Reference to BackChannelDiagnosis object
+ * \param result_ptr Pointer to the result of the BCEnableTx message
+ */
+static void Bcd_EnableTxResultCb(void *self, void *result_ptr)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ /* self_->signature_status = *(Exc_SignatureStatus_t *)(result_ptr_->data_info);*/
+ Fsm_SetEvent(&self_->fsm, BCD_E_TX_ENABLE_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_EnableTxResultCb BCD_E_TX_ENABLE_SUCCESS", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_TX_ENABLE_FAILED);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Bcd_EnableTxResultCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+}
+
+
+/*! \brief Function is called on reception of the ENC.BCDiag.Result messsage
+ * \param self Reference to BackChannelDiagnosis object
+ * \param result_ptr Pointer to the result of the BCDiag message
+ */
+static void Bcd_DiagnosisResultCb(void *self, void *result_ptr)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ self_->bcd_result = *((Exc_BCDiagResult *)(result_ptr_->data_info));
+ switch (self_->bcd_result.diag_result)
+ {
+ case DUT_SLAVE:
+ /* node reported working segment */
+ Fsm_SetEvent(&self_->fsm, BCD_E_DIAG_RESULT_OK);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_DiagnosisResultCb DUT_SLAVE", 0U));
+ break;
+
+ case DUT_MASTER: /* all segments are ok */
+ case DUT_NO_ANSWER: /* ring break found */
+ case DUT_TIMEOUT: /* no communication on back channel */
+ Fsm_SetEvent(&self_->fsm, BCD_E_DIAG_RESULT_NOTOK);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_DiagnosisResultCb others", 0U));
+ break;
+
+ default:
+ /* report error */
+ break;
+ }
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Bcd_DiagnosisResultCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+}
+
+
+/*! \brief Function is called on reception of the INIC.BCDiagEnd.Result messsage
+ * \param self Reference to BackChannel Diagnosis object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Bcd_InicBcdEndCb(void *self, void *result_ptr)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_DIAGMODE_END);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_InicBcdEndCb BCD_E_DIAGMODE_END", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, BCD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Bcd_InicBcdEndCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+}
+
+
+/*! Function is called on severe internal errors
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Reference to data
+ */
+static void Bcd_OnTerminateEventCb(void *self, void *result_ptr)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ MISC_UNUSED(result_ptr);
+
+ if (self_->fsm.current_state != BCD_S_IDLE)
+ {
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_BCD_RES_ERROR, UCS_BCD_DUMMY_SEGMENT, self_->base->ucs_user_ptr);
+ }
+ }
+}
+
+
+/*! \brief Callback function for the INIC.NetworkStatus status and error messages
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Pointer to the result of the INIC.NetworkStatus message
+ */
+static void Bcd_NetworkStatusCb(void *self, void *result_ptr)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_NetworkStatusCb 0x%x", 1U, result_ptr_->result.code));
+ /* check for NetOn/NetOff events */
+ if ( (self_->neton == true)
+ && ((((Inic_NetworkStatus_t *)(result_ptr_->data_info))->availability) == UCS_NW_NOT_AVAILABLE) )
+ {
+ self_->neton = false;
+ Fsm_SetEvent(&self_->fsm, BCD_E_NET_OFF);
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+ }
+ /* check for NetOn/NetOff events */
+ else if ( (self_->neton == false)
+ && ((((Inic_NetworkStatus_t *)(result_ptr_->data_info))->availability) == UCS_NW_AVAILABLE) )
+ {
+/* self_->neton = true;
+ self_->hello_neton_request = true;
+ Fsm_SetEvent(&self_->fsm, BCD_E_CHECK);*/
+ }
+ /* check for MPR event */
+ else if ( (((Inic_NetworkStatus_t *)(result_ptr_->data_info))->events & UCS_NETWORK_EVENT_NCE)
+ == UCS_NETWORK_EVENT_NCE)
+ {
+/* self_->hello_mpr_request = true;
+ Fsm_SetEvent(&self_->fsm, BCD_E_CHECK);*/
+ }
+ }
+
+}
+
+
+/*! \brief Timer callback used for supervising INIC command timeouts.
+ * \param self Reference to Node Discovery object
+ */
+static void Bcd_TimerCb(void *self)
+{
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ Fsm_SetEvent(&self_->fsm, BCD_E_TIMEOUT);
+ TR_INFO((self_->base->ucs_user_ptr, "[BCD]", "Bcd_TimerCb BCD_E_TIMEOUT", 0U));
+
+ Srv_SetEvent(&self_->service, BCD_EVENT_SERVICE);
+}
+
+
+/**************************************************************************************************/
+/* Helper functions */
+/**************************************************************************************************/
+static Ucs_Return_t Bcd_EnableTx(void *self, uint8_t port)
+{
+ Ucs_Return_t ret_val;
+ CBackChannelDiag *self_ = (CBackChannelDiag *)self;
+
+ /* send INIC.BCDiag.StartResult */
+ ret_val = Exc_BCEnableTx_StartResult(self_->exc, port, &self_->bcd_enabletx);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[BCD]", ret_val == UCS_RET_SUCCESS);
+ return ret_val;
+}
+
+
+
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_class.c b/ucs2-lib/src/ucs_class.c
new file mode 100644
index 0000000..f999d43
--- /dev/null
+++ b/ucs2-lib/src/ucs_class.c
@@ -0,0 +1,1790 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the UNICENS API.
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_class.h"
+#include "ucs_misc.h"
+#include "ucs_trace.h"
+#include "ucs_ams.h"
+#include "ucs_cmd.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Macros */
+/*------------------------------------------------------------------------------------------------*/
+/*! \def UCS_NUM_INSTANCES
+ * \brief Number of API instances which can be created by function Ucs_CreateInstance().
+ * \details One API instance is used to communicate with one local INIC. In this case the application
+ * is connected to one network.
+ * It is possible access multiple networks by having multiple API instances. Each API instance
+ * requires communication with an exclusive INIC.
+ * Valid values: 1..10. Default Value: 1.
+ * \ingroup G_UCS_INIT_AND_SRV
+ */
+#ifndef UCS_NUM_INSTANCES
+# define UCS_NUM_INSTANCES 1
+# define UCS_API_INSTANCES 1U /* default value */
+#elif (UCS_NUM_INSTANCES > 10)
+# define UCS_API_INSTANCES 10U
+#elif (UCS_NUM_INSTANCES < 1)
+# define UCS_API_INSTANCES 1U
+#else
+# define UCS_API_INSTANCES ((uint8_t)UCS_NUM_INSTANCES)
+#endif
+
+/*! \cond UCS_INTERNAL_DOC
+ * \addtogroup G_UCS_CLASS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static bool Ucs_CheckInitData(const Ucs_InitData_t *init_ptr);
+static void Ucs_Ctor(CUcs* self, uint8_t ucs_inst_id, void *api_user_ptr);
+static void Ucs_InitComponents(CUcs* self);
+static void Ucs_InitFactoryComponent(CUcs *self);
+static void Ucs_InitBaseComponent(CUcs *self);
+static void Ucs_InitPmsComponentConfig(CUcs *self);
+static void Ucs_InitNetComponent(CUcs *self);
+static void Ucs_InitLocalInicComponent(CUcs *self);
+static void Ucs_InitRoutingComponent(CUcs *self);
+static void Ucs_InitAtsClass(CUcs *self);
+static void Ucs_InitExcComponent(CUcs *self);
+static void Ucs_InitSysDiagComponent(CUcs *self);
+static void Ucs_InitNodeDiscovery(CUcs *self);
+static void Ucs_InitBackChannelDiagnosis(CUcs *self);
+static void Ucs_InitProgramming(CUcs *self);
+static void Ucs_InitManager(CUcs *self);
+static void Ucs_InitResultCb(void *self, void *result_ptr);
+static void Ucs_UninitResultCb(void *self, void *error_code_ptr);
+static void Ucs_OnRxRcm(void *self, Msg_MostTel_t *tel_ptr);
+static bool Ucs_OnRxMsgFilter(void *self, Msg_MostTel_t *tel_ptr);
+static void Ucs_OnGetTickCount(void *self, void *tick_count_value_ptr);
+static void Ucs_OnSetApplicationTimer(void *self, void *new_time_value_ptr);
+static void Ucs_OnServiceRequest(void *self, void *result_ptr);
+static void Ucs_OnGeneralError(void *self, void *result_ptr);
+static void Ucs_Most_PortStatusCb(void *self, void *result_ptr);
+static void Ucs_StartAppNotification(CUcs *self);
+static void Ucs_StopAppNotification(CUcs *self);
+static void Ucs_Inic_OnDeviceStatus(void *self, void *data_ptr);
+static void Ucs_NetworkStartupResult(void *self, void *result_ptr);
+static void Ucs_NetworkShutdownResult(void *self, void *result_ptr);
+static void Ucs_NetworkForceNAResult(void *self, void *result_ptr);
+static void Ucs_NetworkFrameCounterResult(void *self, void *result_ptr);
+static void Ucs_NetworkStatus(void *self, void *result_ptr);
+static void Ucs_InitPmsComponent(CUcs *self);
+static void Ucs_InitPmsComponentApp(CUcs *self);
+static void Ucs_InitAmsComponent(CUcs *self);
+static void Ucs_AmsRx_Callback(void *self);
+static void Ucs_AmsTx_FreedCallback(void *self, void *data_ptr);
+static bool Ucs_McmRx_FilterCallback(void *self, Msg_MostTel_t *tel_ptr);
+static Ucs_Nd_CheckResult_t Ucs_OnNdEvaluate(void *self, Ucs_Signature_t *signature_ptr);
+static void Ucs_OnNdReport(void *self, Ucs_Nd_ResCode_t code, Ucs_Signature_t *signature_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Public Methods */
+/*------------------------------------------------------------------------------------------------*/
+extern Ucs_Inst_t* Ucs_CreateInstance(void)
+{
+ static CUcs api_instances[UCS_API_INSTANCES];
+ static uint8_t next_index = 0U;
+ Ucs_Inst_t *inst_ptr = NULL;
+
+ if (next_index < UCS_API_INSTANCES)
+ {
+ CUcs *ucs_ptr = &api_instances[next_index];
+ ucs_ptr->ucs_inst_id = next_index + 1U; /* start with instance id "1" */
+ TR_INFO((ucs_ptr->ucs_user_ptr, "[API]", "Ucs_CreateInstance(): returns 0x%p", 1U, ucs_ptr));
+ inst_ptr = (Ucs_Inst_t*)(void*)ucs_ptr; /* convert API pointer to abstract data type */
+ next_index++;
+ }
+ else
+ {
+ TR_INFO((0U, "[API]", "Ucs_CreateInstance(): failed!", 0U));
+ }
+
+ return inst_ptr;
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization structure */
+/*------------------------------------------------------------------------------------------------*/
+extern Ucs_Return_t Ucs_SetDefaultConfig(Ucs_InitData_t *init_ptr)
+{
+ Ucs_Return_t ret = UCS_RET_ERR_PARAM;
+
+ if (init_ptr != NULL)
+ {
+ MISC_MEM_SET(init_ptr, 0, sizeof(*init_ptr));
+ /* -- add default values here -- */
+ init_ptr->general.inic_watchdog_enabled = true;
+ init_ptr->ams.enabled = true;
+ init_ptr->network.status.notification_mask = 0xFFFFU; /* Initialize notification masks for NET callbacks */
+ init_ptr->mgr.packet_bw = MGR_PACKET_BW_DEFAULT;
+ init_ptr->mgr.enabled = false;
+ ret = UCS_RET_SUCCESS;
+ }
+
+ TR_INFO((0U, "[API]", "Ucs_SetDefaultConfig(init_ptr: 0x%p): called", 1U, init_ptr));
+ return ret;
+}
+
+/*! \brief Checks if the given initialization data is valid
+ * \param init_ptr Reference to initialization data
+ * \return Returns \c true if the given initialization data is valid, otherwise \c false.
+ */
+static bool Ucs_CheckInitData(const Ucs_InitData_t *init_ptr)
+{
+ bool ret_val = true;
+
+ if ((init_ptr == NULL) || /* General NULL pointer checks */
+ (init_ptr->general.get_tick_count_fptr == NULL) ||
+ (init_ptr->lld.start_fptr == NULL) ||
+ (init_ptr->lld.stop_fptr == NULL) ||
+ (init_ptr->lld.tx_transmit_fptr == NULL)
+ )
+ {
+ TR_ERROR((0U, "[API]", "Initialization failed. Required initialization data contains a NULL pointer.", 0U));
+ ret_val = false;
+ }
+ else if (((init_ptr->general.set_application_timer_fptr == NULL) && (init_ptr->general.request_service_fptr != NULL)) ||
+ ((init_ptr->general.set_application_timer_fptr != NULL) && (init_ptr->general.request_service_fptr == NULL)))
+ {
+ TR_ERROR((0U, "[API]", "Initialization failed. To run UCS in event driven service mode, both callback functions must be assigned.", 0U));
+ ret_val = false;
+ }
+ else if ((init_ptr->mgr.enabled != false) && ((init_ptr->nd.eval_fptr != NULL) || (init_ptr->nd.report_fptr != NULL)))
+ {
+ TR_INFO((0U, "[API]", "Ambiguous initialization structure. NodeDiscovery callback functions are not effective if 'mgr.enabled' is 'true'.", 0U));
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Class initialization */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of API. Values are reset, initialization must be triggered via Ucs_Init().
+ * \param self The instance
+ * \param ucs_inst_id The ID of the instance
+ * \param api_user_ptr The user reference for API callback functions
+ */
+static void Ucs_Ctor(CUcs* self, uint8_t ucs_inst_id, void *api_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self)); /* reset memory and backup/restore instance id */
+ self->ucs_inst_id = ucs_inst_id;
+ self->ucs_user_ptr = api_user_ptr;
+}
+
+extern Ucs_Return_t Ucs_Init(Ucs_Inst_t* self, const Ucs_InitData_t *init_ptr, Ucs_InitResultCb_t init_result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret = UCS_RET_ERR_PARAM;
+
+ /* Note: "self_->ucs_inst_id" is already set to the correct value in Ucs_CreateInstance(), do not overwrite it */
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_Init(init_ptr: 0x%p): called", 1U, init_ptr));
+
+ if (Ucs_CheckInitData(init_ptr))
+ {
+ Ucs_Ctor(self_, self_->ucs_inst_id, init_ptr->user_ptr);/* initialize object */
+ self_->init_result_fptr = init_result_fptr; /* backup result callback function */
+
+ self_->init_data = *init_ptr; /* backup init data */
+ Ucs_InitComponents(self_); /* call constructors and link all components */
+ /* create init-complete observer */
+ Sobs_Ctor(&self_->init_result_obs, self, &Ucs_InitResultCb);
+ Ats_Start(&self_->inic.attach, &self_->init_result_obs);/* Start attach process */
+ ret = UCS_RET_SUCCESS;
+ }
+ /* register observer related to Ucs_Stop() */
+ Mobs_Ctor(&self_->uninit_result_obs, self, (EH_E_UNSYNC_COMPLETE | EH_E_UNSYNC_FAILED), &Ucs_UninitResultCb);
+ return ret;
+}
+
+extern void Ucs_Service(Ucs_Inst_t* self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ bool pending_events = false;
+
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_Service(): called", 0U));
+ Scd_Service(&self_->general.base.scd); /* Run the scheduler */
+ pending_events = Scd_AreEventsPending(&self_->general.base.scd); /* Check if events are still pending? */
+
+ if (pending_events != false) /* At least one event is pending? */
+ {
+ if (self_->general.request_service_fptr != NULL)
+ {
+ self_->general.request_service_fptr(self_->ucs_user_ptr); /* Trigger UCS service call immediately */
+ }
+ }
+
+ Tm_CheckForNextService(&self_->general.base.tm); /* If UCS timers are running: What is the next time that
+ * the timer management must be serviced again? */
+}
+
+extern void Ucs_ReportTimeout(Ucs_Inst_t* self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_ReportTimeout(): called", 0U));
+ Tm_TriggerService(&self_->general.base.tm); /* Trigger TM service call */
+}
+
+extern Ucs_Return_t Ucs_Stop(Ucs_Inst_t* self, Ucs_StdResultCb_t stopped_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_Stop() called", 0U));
+
+ if ((self_->uninit_result_fptr == NULL) && (self_->init_complete != false))
+ {
+ if (stopped_fptr != NULL)
+ {
+ self_->uninit_result_fptr = stopped_fptr;
+ Eh_DelObsrvPublicError(&self_->general.base.eh);
+ Eh_AddObsrvInternalEvent(&self_->general.base.eh, &self_->uninit_result_obs);
+ ret_val = UCS_RET_SUCCESS;
+ Fifos_ConfigureSyncParams(&self_->fifos, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT);
+ Fifos_Unsynchronize(&self_->fifos, true, false);
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_API_LOCKED; /* termination is already running */
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Connection Routing Management */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Rm_Start(Ucs_Inst_t *self, Ucs_Rm_Route_t *routes_list, uint16_t list_size)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (self_->init_complete != false)
+ {
+ ret_val = Rtm_StartProcess (&self_->rtm, routes_list, list_size);
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Rm_SetRouteActive (Ucs_Inst_t *self, Ucs_Rm_Route_t *route_ptr, bool active)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if ((self_ != NULL) && (route_ptr != NULL))
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ if (!active)
+ {
+ ret_val = Rtm_DeactivateRoute(&self_->rtm, route_ptr);
+ }
+ else
+ {
+ ret_val = Rtm_ActivateRoute(&self_->rtm, route_ptr);
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Xrm_Stream_SetPortConfig(Ucs_Inst_t *self,
+ uint16_t destination_address,
+ uint8_t index,
+ Ucs_Stream_PortOpMode_t op_mode,
+ Ucs_Stream_PortOption_t port_option,
+ Ucs_Stream_PortClockMode_t clock_mode,
+ Ucs_Stream_PortClockDataDelay_t clock_data_delay,
+ Ucs_Xrm_Stream_PortCfgResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Xrm_Stream_SetPortConfig(Fac_GetXrmLegacy(&self_->factory, destination_address, self_->init_data.rm.xrm.check_unmute_fptr),
+ index,
+ op_mode,
+ port_option,
+ clock_mode,
+ clock_data_delay,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Xrm_Stream_GetPortConfig(Ucs_Inst_t *self, uint16_t destination_address, uint8_t index,
+ Ucs_Xrm_Stream_PortCfgResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Xrm_Stream_GetPortConfig(Fac_GetXrmLegacy(&self_->factory, destination_address, self_->init_data.rm.xrm.check_unmute_fptr),
+ index, result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Node Management */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Rm_SetNodeAvailable(Ucs_Inst_t *self, Ucs_Rm_Node_t *node_ptr, bool available)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if ((self_ != NULL) && (node_ptr != NULL))
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Rtm_SetNodeAvailable(&self_->rtm, node_ptr, available);
+ }
+ }
+
+ return ret_val;
+}
+
+bool Ucs_Rm_GetNodeAvailable (Ucs_Inst_t *self, Ucs_Rm_Node_t *node_ptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ bool ret_val = false;
+
+ if ((self_ != NULL) && (node_ptr != NULL))
+ {
+ ret_val = Rtm_GetNodeAvailable(&self_->rtm, node_ptr);
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Rm_GetAttachedRoutes (Ucs_Inst_t *self, Ucs_Rm_EndPoint_t * ep_inst,
+ Ucs_Rm_Route_t * ls_found_routes[], uint16_t ls_size)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Rtm_GetAttachedRoutes(&self_->rtm, ep_inst, ls_found_routes, ls_size);
+ }
+ }
+
+ return ret_val;
+}
+
+uint16_t Ucs_Rm_GetConnectionLabel(Ucs_Inst_t *self, Ucs_Rm_Route_t *route_ptr)
+{
+ uint16_t ret_value = 0U;
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ if ((self_ != NULL) && (self_->init_complete != false) && (route_ptr != NULL))
+ {
+ ret_value = Rtm_GetConnectionLabel(&self_->rtm, route_ptr);
+ }
+
+ return ret_value;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Node Scripting Management */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Ns_Run (Ucs_Inst_t *self, Ucs_Rm_Node_t * node_ptr, Ucs_Ns_ResultCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if ((self_ != NULL) && (node_ptr != NULL) && (node_ptr->signature_ptr != NULL))
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ CNodeScriptManagement * nsm_inst = Fac_GetNsm(&self_->factory, node_ptr->signature_ptr->node_address);
+
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ if (nsm_inst != NULL)
+ {
+ ret_val = Nsm_Run_Pb(nsm_inst, node_ptr, result_fptr);
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* GPIO and I2C Peripheral Bus Interfaces */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Gpio_CreatePort(Ucs_Inst_t *self, uint16_t destination_address, uint8_t index, uint16_t debounce_time, Ucs_Gpio_CreatePortResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Gpio_CreatePort(Fac_GetGpio(&self_->factory, destination_address, self_->init_data.gpio.trigger_event_status_fptr),
+ index,
+ debounce_time,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Gpio_SetPinMode(Ucs_Inst_t *self, uint16_t destination_address, uint16_t gpio_port_handle,
+ uint8_t pin, Ucs_Gpio_PinMode_t mode, Ucs_Gpio_ConfigPinModeResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Gpio_SetPinModeConfig(Fac_GetGpio(&self_->factory, destination_address, self_->init_data.gpio.trigger_event_status_fptr),
+ gpio_port_handle,
+ pin,
+ mode,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Gpio_GetPinMode(Ucs_Inst_t *self, uint16_t destination_address, uint16_t gpio_port_handle, Ucs_Gpio_ConfigPinModeResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Gpio_GetPinModeConfig(Fac_GetGpio(&self_->factory, destination_address, self_->init_data.gpio.trigger_event_status_fptr),
+ gpio_port_handle,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Gpio_WritePort(Ucs_Inst_t *self, uint16_t destination_address, uint16_t gpio_port_handle,
+ uint16_t mask, uint16_t data, Ucs_Gpio_PinStateResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Gpio_SetPinStateConfig(Fac_GetGpio(&self_->factory, destination_address, self_->init_data.gpio.trigger_event_status_fptr),
+ gpio_port_handle,
+ mask,
+ data,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_Gpio_ReadPort(Ucs_Inst_t *self, uint16_t destination_address, uint16_t gpio_port_handle, Ucs_Gpio_PinStateResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Gpio_GetPinStateConfig(Fac_GetGpio(&self_->factory, destination_address, self_->init_data.gpio.trigger_event_status_fptr),
+ gpio_port_handle,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_I2c_CreatePort(Ucs_Inst_t *self, uint16_t destination_address, uint8_t index, Ucs_I2c_Speed_t speed,
+ uint8_t i2c_int_mask, Ucs_I2c_CreatePortResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = I2c_CreatePort(Fac_GetI2c(&self_->factory, destination_address, self_->init_data.i2c.interrupt_status_fptr),
+ index,
+ speed,
+ i2c_int_mask,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_I2c_WritePort(Ucs_Inst_t *self, uint16_t destination_address, uint16_t port_handle, Ucs_I2c_TrMode_t mode, uint8_t block_count,
+ uint8_t slave_address, uint16_t timeout, uint8_t data_len, uint8_t * data_ptr,
+ Ucs_I2c_WritePortResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = I2c_WritePort(Fac_GetI2c(&self_->factory, destination_address, self_->init_data.i2c.interrupt_status_fptr),
+ port_handle,
+ mode,
+ block_count,
+ slave_address,
+ timeout,
+ data_len,
+ data_ptr,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+Ucs_Return_t Ucs_I2c_ReadPort(Ucs_Inst_t *self, uint16_t destination_address, uint16_t port_handle, uint8_t slave_address, uint8_t data_len,
+ uint16_t timeout, Ucs_I2c_ReadPortResCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if (self_ != NULL)
+ {
+ ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = I2c_ReadPort(Fac_GetI2c(&self_->factory, destination_address, self_->init_data.i2c.interrupt_status_fptr),
+ port_handle,
+ slave_address,
+ data_len,
+ timeout,
+ result_fptr);
+ }
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Components */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Initializes all UCS core components
+ * \param self The instance
+ */
+static void Ucs_InitComponents(CUcs* self)
+{
+ Ucs_InitBaseComponent(self);
+ Ucs_InitFactoryComponent(self);
+ Ucs_InitLocalInicComponent(self);
+ Ucs_InitNetComponent(self);
+ Ucs_InitPmsComponent(self);
+ Ucs_InitAmsComponent(self);
+ Ucs_InitRoutingComponent(self);
+ Ucs_InitAtsClass(self);
+
+ Ucs_InitExcComponent(self);
+ Ucs_InitSysDiagComponent(self);
+ Ucs_InitNodeDiscovery(self);
+ Ucs_InitBackChannelDiagnosis(self);
+ Ucs_InitProgramming(self);
+ Ucs_InitManager(self); /* shall be called as last one due to re-configuration work */
+}
+
+/*! \brief Initializes the factory component
+ * \param self The instance
+ */
+static void Ucs_InitFactoryComponent(CUcs *self)
+{
+ Fac_InitData_t fac_init_data;
+ fac_init_data.base_ptr = &self->general.base;
+ fac_init_data.net_ptr = &self->net.inst;
+ fac_init_data.xrmp_ptr = &self->xrmp;
+ fac_init_data.icm_transceiver = &self->icm_transceiver;
+ fac_init_data.rcm_transceiver = &self->rcm_transceiver;
+ Fac_Ctor(&self->factory, &fac_init_data);
+}
+
+/*! \brief Initializes the the base component
+ * \param self The instance
+ */
+static void Ucs_InitBaseComponent(CUcs *self)
+{
+ Base_InitData_t base_init_data;
+
+ if (self->init_data.general.request_service_fptr != NULL) /* pointer may be NULL for termination */
+ {
+ self->general.request_service_fptr = self->init_data.general.request_service_fptr;
+ Sobs_Ctor(&self->general.service_request_obs, self, &Ucs_OnServiceRequest);
+ base_init_data.scd.service_request_obs_ptr = &self->general.service_request_obs;
+ }
+ else
+ {
+ base_init_data.scd.service_request_obs_ptr = NULL;
+ }
+
+ self->general.get_tick_count_fptr = self->init_data.general.get_tick_count_fptr;
+ Sobs_Ctor(&self->general.get_tick_count_obs, self, &Ucs_OnGetTickCount);
+ base_init_data.tm.get_tick_count_obs_ptr = &self->general.get_tick_count_obs;
+ if (self->init_data.general.set_application_timer_fptr != NULL)
+ {
+ self->general.set_application_timer_fptr = self->init_data.general.set_application_timer_fptr;
+ Sobs_Ctor(&self->general.set_application_timer_obs, self, &Ucs_OnSetApplicationTimer);
+ base_init_data.tm.set_application_timer_obs_ptr = &self->general.set_application_timer_obs;
+ }
+ else
+ {
+ base_init_data.tm.set_application_timer_obs_ptr = NULL;
+ }
+ base_init_data.ucs_inst_id = self->ucs_inst_id;
+ base_init_data.ucs_user_ptr = self->ucs_user_ptr;
+ Base_Ctor(&self->general.base, &base_init_data);
+}
+
+/*! \brief Initializes the port message service
+ * \param self The instance
+ */
+static void Ucs_InitPmsComponent(CUcs *self)
+{
+ CPmFifo * mcm_fifo_ptr = NULL;
+
+ if (self->init_data.ams.enabled == true)
+ {
+ mcm_fifo_ptr = &self->msg.mcm_fifo;
+ }
+
+ Ucs_InitPmsComponentConfig(self);
+ Ucs_InitPmsComponentApp(self);
+
+ Fifos_Ctor(&self->fifos, &self->general.base, &self->pmch, &self->icm_fifo, mcm_fifo_ptr, &self->rcm_fifo);
+ Pmev_Ctor(&self->pme, &self->general.base, &self->fifos); /* initialize event handler */
+}
+
+/*! \brief Initializes the port message service
+ * \param self The instance
+ */
+static void Ucs_InitPmsComponentConfig(CUcs *self)
+{
+ Pmch_InitData_t pmch_init_data;
+ Fifo_InitData_t icm_init;
+ Fifo_Config_t icm_config;
+ Fifo_InitData_t rcm_init;
+ Fifo_Config_t rcm_config;
+
+ /* Initialize port message service */
+ pmch_init_data.ucs_user_ptr = self->ucs_user_ptr;
+ pmch_init_data.tx_release_fptr = &Fifo_TxOnRelease;
+ pmch_init_data.lld_iface = self->init_data.lld;
+ Pmch_Ctor(&self->pmch, &pmch_init_data);
+
+ /* Initialize the ICM channel */
+ icm_init.base_ptr = &self->general.base;
+ icm_init.channel_ptr = &self->pmch;
+ icm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete;
+ icm_init.rx_cb_inst = &self->icm_transceiver;
+ icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
+ icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
+ icm_config.fifo_id = PMP_FIFO_ID_ICM;
+ icm_config.tx_wd_timeout = 0U;
+ icm_config.tx_wd_timer_value = 0U;
+ icm_config.rx_ack_timeout = 10U;
+ icm_config.rx_busy_allowed = 0xFU;
+ icm_config.rx_credits = PMCH_FIFO_CREDITS;
+ icm_config.rx_threshold = PMCH_FIFO_THRESHOLD;
+ if (self->init_data.general.inic_watchdog_enabled == false)
+ {
+ icm_config.rx_ack_timeout = 0U;
+ }
+ Fifo_Ctor(&self->icm_fifo, &icm_init, &icm_config);
+
+ /* Initialize the RCM channel */
+ rcm_init.base_ptr = &self->general.base;
+ rcm_init.channel_ptr = &self->pmch;
+ rcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete;
+ rcm_init.rx_cb_inst = &self->rcm_transceiver;
+ rcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
+ rcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
+ rcm_config.fifo_id = PMP_FIFO_ID_RCM;
+ rcm_config.tx_wd_timeout = 10U; /* Watchdog timeout: 1s */
+ rcm_config.tx_wd_timer_value = 600U; /* Watchdog trigger every 600 ms */
+ rcm_config.rx_ack_timeout = 10U; /* Acknowledge timeout: 10 ms */
+ rcm_config.rx_busy_allowed = 0xFU;
+ rcm_config.rx_credits = PMCH_FIFO_CREDITS;
+ rcm_config.rx_threshold = PMCH_FIFO_THRESHOLD;
+ if (self->init_data.general.inic_watchdog_enabled == false)
+ {
+ /* Disable INIC watchdog */
+ rcm_config.tx_wd_timeout = 0U; /* Watchdog timeout: 0 -> infinite */
+ rcm_config.tx_wd_timer_value = 0U; /* Watchdog timer: 0 -> no timer */
+ rcm_config.rx_ack_timeout = 0U; /* Acknowledge timeout: 0 -> infinite */
+ }
+ Fifo_Ctor(&self->rcm_fifo, &rcm_init, &rcm_config);
+#if 0
+ Fifos_Ctor(&self->fifos, &self->general.base, &self->pmch, &self->icm_fifo, NULL/*MCM*/, &self->rcm_fifo);
+ Pmev_Ctor(&self->pme, &self->general.base, &self->fifos); /* initialize event handler */
+#endif
+
+ /* initialize transceivers and set reference to FIFO instance */
+ Trcv_Ctor(&self->icm_transceiver, &self->icm_fifo, MSG_ADDR_EHC_CFG, self->ucs_user_ptr, PMP_FIFO_ID_ICM);
+ Trcv_RxAssignFilter(&self->icm_transceiver, &Ucs_OnRxMsgFilter, self);
+ Trcv_RxAssignReceiver(&self->icm_transceiver, &Inic_OnIcmRx, self->inic.local_inic);
+ Trcv_Ctor(&self->rcm_transceiver, &self->rcm_fifo, MSG_ADDR_EHC_CFG, self->ucs_user_ptr, PMP_FIFO_ID_RCM);
+ /* Assign RX filter and receiver function to the RCM transceiver */
+ Trcv_RxAssignFilter(&self->rcm_transceiver, &Ucs_OnRxMsgFilter, self);
+ Trcv_RxAssignReceiver(&self->rcm_transceiver, &Ucs_OnRxRcm, self);
+}
+
+/*! \brief Initializes the network management component
+ * \param self The instance
+ */
+static void Ucs_InitNetComponent(CUcs *self)
+{
+ Net_InitData_t net_init_data;
+
+ Sobs_Ctor(&self->net.startup_obs, self, &Ucs_NetworkStartupResult);
+ Sobs_Ctor(&self->net.shutdown_obs, self, &Ucs_NetworkShutdownResult);
+ Sobs_Ctor(&self->net.force_na_obs, self, &Ucs_NetworkForceNAResult);
+ Sobs_Ctor(&self->net.frame_counter_obs, self, &Ucs_NetworkFrameCounterResult);
+ net_init_data.base_ptr = &self->general.base;
+ net_init_data.inic_ptr = self->inic.local_inic;
+ Net_Ctor(&self->net.inst, &net_init_data);
+}
+
+/*! \brief Initializes the FBlock INIC
+ * \param self The instance
+ */
+static void Ucs_InitLocalInicComponent(CUcs *self)
+{
+ self->inic.local_inic = Fac_GetInic(&self->factory, UCS_ADDR_LOCAL_INIC);
+ Obs_Ctor(&self->inic.device_status_obs, self, &Ucs_Inic_OnDeviceStatus);
+}
+
+/*! \brief Initializes the Routing components
+ * \param self The instance
+ */
+static void Ucs_InitRoutingComponent(CUcs *self)
+{
+ Epm_InitData_t epm_init;
+ Rtm_InitData_t rtm_init;
+
+ /* Initialize the unique XRM Pool Instance */
+ Xrmp_Ctor(&self->xrmp);
+
+ /* Initialize the EndPoint Management Instance */
+ epm_init.base_ptr = &self->general.base;
+ epm_init.fac_ptr = &self->factory;
+ epm_init.res_debugging_fptr = self->init_data.rm.debug_resource_status_fptr;
+ epm_init.check_unmute_fptr = self->init_data.rm.xrm.check_unmute_fptr;
+ Epm_Ctor (&self->epm, &epm_init);
+
+ /* Initialize the Routes Management Instance */
+ rtm_init.base_ptr = &self->general.base;
+ rtm_init.epm_ptr = &self->epm;
+ rtm_init.net_ptr = &self->net.inst;
+ rtm_init.report_fptr = self->init_data.rm.report_fptr;
+ Rtm_Ctor(&self->rtm, &rtm_init);
+}
+
+/*! \brief Initializes the attach service
+ * \param self The instance
+ */
+static void Ucs_InitAtsClass(CUcs *self)
+{
+ Ats_InitData_t ats_init_data;
+ ats_init_data.base_ptr = &self->general.base;
+ ats_init_data.fifos_ptr = &self->fifos;
+ ats_init_data.inic_ptr = self->inic.local_inic;
+ ats_init_data.pme_ptr = &self->pme;
+ Ats_Ctor(&self->inic.attach, &ats_init_data);
+}
+
+/*! \brief Initializes the FBlock ExtendedNetworkControl API
+ * \param self The instance
+ */
+static void Ucs_InitExcComponent(CUcs *self)
+{
+ /* Create the FBlock ExtendedNetworkControl instance */
+ Exc_Ctor(&self->exc, &self->general.base, &self->rcm_transceiver);
+}
+
+/*! \brief Initializes the SystemDiagnosis component
+ * \param self The instance
+ */
+static void Ucs_InitSysDiagComponent(CUcs *self)
+{
+ /* Create the System Diagnosis instance */
+ SysDiag_Ctor(&self->sys_diag, self->inic.local_inic, &self->general.base, &self->exc);
+}
+
+
+static void Ucs_InitNodeDiscovery(CUcs *self)
+{
+ Nd_InitData_t nd_init_data;
+
+ if (self->init_data.mgr.enabled == false)
+ {
+ nd_init_data.inst_ptr = self;
+ nd_init_data.report_fptr = &Ucs_OnNdReport;
+ nd_init_data.eval_fptr = &Ucs_OnNdEvaluate;
+ }
+ else
+ {
+ nd_init_data.inst_ptr = &self->nobs;
+ nd_init_data.report_fptr = &Nobs_OnNdReport;
+ nd_init_data.eval_fptr = &Nobs_OnNdEvaluate;
+ }
+
+ Nd_Ctor(&self->nd, self->inic.local_inic, &self->general.base, &self->exc, &nd_init_data);
+
+}
+
+static void Ucs_InitBackChannelDiagnosis(CUcs *self)
+{
+ Bcd_Ctor(&self->bcd, self->inic.local_inic, &self->general.base, &self->exc);
+}
+
+static void Ucs_InitProgramming(CUcs *self)
+{
+ Prg_Ctor(&self->prg, self->inic.local_inic, &self->general.base, &self->exc);
+}
+
+
+/*! \brief Initializes the Manager class
+ * \details This function shall be called as the latest initialization function since
+ * it may disable some of the conventional API.
+ * \param self The instance
+ */
+static void Ucs_InitManager(CUcs *self)
+{
+ if (self->init_data.mgr.enabled == true)
+ {
+ Mgr_Ctor(&self->mgr, &self->general.base, self->inic.local_inic, &self->net.inst, &self->nd, self->init_data.mgr.packet_bw);
+ Nobs_Ctor(&self->nobs, &self->general.base, &self->nd, &self->rtm, &self->init_data.mgr);
+ }
+}
+
+
+/*! \brief Callback function which announces the result of the attach process
+ * \param self The instance
+ * \param result_ptr Result of the initialization process. Result must be casted into data type
+ * Ucs_InitResult_t. Possible return values are shown in the table below.
+ * Result Code | Description
+ * ----------------------------- | ----------------------------------------------------
+ * UCS_INIT_RES_SUCCESS | Initialization succeeded
+ * UCS_INIT_RES_ERR_BUF_OVERFLOW | No message buffer available
+ * UCS_INIT_RES_ERR_PMS_INIT | PMS Initialization failed
+ * UCS_INIT_RES_ERR_INIC_VERSION | INIC device version check failed
+ * UCS_INIT_RES_ERR_DEV_ATT_CFG | Device attach failed due to an configuration error
+ * UCS_INIT_RES_ERR_DEV_ATT_PROC | Device attach failed due to a system error
+ * UCS_INIT_RES_ERR_NET_CFG | Network configuration failed
+ * UCS_INIT_RES_ERR_TIMEOUT | Initialization timeout occurred
+ */
+static void Ucs_InitResultCb(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Ucs_InitResult_t *result_ptr_ = (Ucs_InitResult_t *)result_ptr;
+
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_InitResultCb(): Ucs_Init() completed, internal event code: %u", 1U, *result_ptr_));
+ if (*result_ptr_ != UCS_INIT_RES_SUCCESS)
+ {
+ Ucs_StopAppNotification(self_);
+ }
+
+ if (self_->init_result_fptr != NULL)
+ {
+ self_->init_result_fptr(*result_ptr_, self_->ucs_user_ptr);
+ }
+
+ /* Start notification if initialization succeeded */
+ if (*result_ptr_ == UCS_INIT_RES_SUCCESS)
+ {
+ self_->init_complete = true;
+ Ucs_StartAppNotification(self_);
+ }
+}
+
+/*! \brief Callback function which announces the result of Ucs_Stop()
+ * \param self The instance
+ * \param error_code_ptr Reference to the error code
+ */
+static void Ucs_UninitResultCb(void *self, void *error_code_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ uint32_t error_code = *((uint32_t *)error_code_ptr);
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_UninitResultCb(): Ucs_Stop() completed, internal event code: %u", 1U, error_code));
+
+ self_->init_complete = false;
+ Eh_DelObsrvInternalEvent(&self_->general.base.eh, &self_->uninit_result_obs);
+
+ Ucs_StopAppNotification(self_);
+
+ if (self_->uninit_result_fptr != NULL)
+ {
+ Ucs_StdResult_t result;
+
+ result.code = UCS_RES_SUCCESS;
+ result.info_ptr = NULL;
+ result.info_size = 0U;
+
+ if (error_code != EH_E_UNSYNC_COMPLETE)
+ {
+ result.code = UCS_RES_ERR_TIMEOUT;
+ }
+
+ self_->uninit_result_fptr(result, self_->ucs_user_ptr);
+ self_->uninit_result_fptr = NULL;
+ }
+}
+
+/*! \brief Starts the notification after the initialization has succeeded
+ * \param self The instance
+ */
+static void Ucs_StartAppNotification(CUcs *self)
+{
+ self->general.general_error_fptr = self->init_data.general.error_fptr; /* assign general error notification */
+ Sobs_Ctor(&self->general.general_error_obs, self, &Ucs_OnGeneralError);
+ Eh_AddObsrvPublicError(&self->general.base.eh, &self->general.general_error_obs);
+
+ if (self->init_data.network.status.cb_fptr != NULL) /* Start notification of Network Status */
+ {
+ self->net.status_fptr = self->init_data.network.status.cb_fptr;
+ Mobs_Ctor(&self->net.status_obs,
+ self,
+ (uint32_t)self->init_data.network.status.notification_mask,
+ &Ucs_NetworkStatus);
+ Net_AddObserverNetworkStatus(&self->net.inst, &self->net.status_obs);
+ }
+
+ if ((self->init_data.ams.tx.message_freed_fptr != NULL) && (self->msg.ams_tx_alloc_failed != false))
+ {
+ self->msg.ams_tx_alloc_failed = false;
+ self->init_data.ams.tx.message_freed_fptr(self->ucs_user_ptr);
+ }
+
+ if (self->init_data.inic.power_state_fptr != NULL)
+ {
+ self->inic.power_state = Inic_GetDevicePowerState(self->inic.local_inic); /* remember the current value */
+ self->init_data.inic.power_state_fptr(self->inic.power_state, self->ucs_user_ptr);
+ Inic_AddObsvrDeviceStatus(self->inic.local_inic, &self->inic.device_status_obs);
+ }
+
+ if(self->init_data.rm.xrm.most_port_status_fptr != NULL) /* Initialize callback pointer for MOST port status callback */
+ {
+ self->xrm.most_port_status_fptr = self->init_data.rm.xrm.most_port_status_fptr;
+ Obs_Ctor(&self->xrm.most_port_status_obs, self, &Ucs_Most_PortStatusCb);
+ Inic_AddObsrvMostPortStatus(self->inic.local_inic, &self->xrm.most_port_status_obs);
+ }
+}
+
+/*! \brief Stops application events for timer management and event service
+ * \param self The instance
+ */
+static void Ucs_StopAppNotification(CUcs *self)
+{
+ self->general.request_service_fptr = NULL; /* clear service request to avoid any pending events to be called again */
+ Tm_StopService(&self->general.base.tm); /* stop timer service */
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Message Routing */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Callback function to receive RCM Rx messages
+ * \param self The UCS instance
+ * \param tel_ptr The received RCM Rx message object
+ * \return Returns \c true to discard the message and free it to the pool (no-pass).
+ * Otherwise, returns \c false (pass).
+ */
+static void Ucs_OnRxRcm(void *self, Msg_MostTel_t *tel_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+
+ if (tel_ptr->id.fblock_id == FB_EXC)
+ {
+ Exc_OnRcmRxFilter(&(self_->exc), tel_ptr);
+ }
+ else if (tel_ptr->id.fblock_id == FB_INIC)
+ {
+ if (!Nsm_OnRcmRxFilter(Fac_FindNsm(&self_->factory, tel_ptr->source_addr), tel_ptr))
+ {
+ CInic * inic_ptr = Fac_FindInic(&self_->factory, tel_ptr->source_addr);
+ if (inic_ptr != NULL)
+ {
+ Inic_OnRcmRxFilter(inic_ptr, tel_ptr);
+ }
+ }
+ }
+
+ Trcv_RxReleaseMsg(&self_->rcm_transceiver, tel_ptr); /* free Rx telegram */
+}
+
+/*! \brief Callback function which filters Control Rx messages
+ * \param self The UCS instance
+ * \param tel_ptr The received Rx message object
+ * \return Returns \c true to discard the message and free it to the pool (no-pass).
+ * Otherwise, returns \c false (pass).
+ */
+static bool Ucs_OnRxMsgFilter(void *self, Msg_MostTel_t *tel_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ bool ret = false; /* just pass - do not discard message */
+
+ if (self_->rx_filter_fptr != NULL)
+ {
+ ret = self_->rx_filter_fptr(tel_ptr, self_->ucs_user_ptr);
+ }
+
+ if (ret == false)
+ {
+ if ((tel_ptr->id.op_type == UCS_OP_ERROR) || (tel_ptr->id.op_type == UCS_OP_ERRORACK))
+ {
+ if (self_->init_data.general.debug_error_msg_fptr != NULL)
+ {
+ self_->init_data.general.debug_error_msg_fptr(tel_ptr, self_->ucs_user_ptr);
+ }
+ }
+ }
+ else
+ {
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_OnRxMsgFilter(): message discarded by unit test", 0U));
+ }
+
+ return ret;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Observers / Basic API */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Callback function which is invoked to request the current tick count value
+ * \param self The instance
+ * \param tick_count_value_ptr Reference to the requested tick count value. The pointer must
+ * be casted into data type uint16_t.
+ */
+static void Ucs_OnGetTickCount(void *self, void *tick_count_value_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ *((uint16_t *)tick_count_value_ptr) = self_->general.get_tick_count_fptr(self_->ucs_user_ptr);
+}
+
+/*! \brief Callback function which is invoked to start the application timer when the UNICENS 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 Ucs_OnSetApplicationTimer(void *self, void *new_time_value_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_OnSetApplicationTimer(%d)", 1U, *((uint16_t *)new_time_value_ptr)));
+ self_->general.set_application_timer_fptr(*((uint16_t *)new_time_value_ptr), self_->ucs_user_ptr);
+}
+
+/*! \brief Callback function which is invoked to announce a request for service
+ * \param self The instance
+ * \param result_ptr Result pointer (not used)
+ */
+static void Ucs_OnServiceRequest(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+
+ TR_ASSERT(self_->ucs_user_ptr, "[API]", self_->init_data.general.request_service_fptr != NULL);
+ self_->general.request_service_fptr(self_->ucs_user_ptr); /* Call application callback */
+ MISC_UNUSED(result_ptr);
+}
+
+/*! \brief Callback function which announces a general error
+ * \param self The instance
+ * \param result_ptr Reference to the result. Must be casted into Eh_PublicErrorData_t.
+ */
+static void Ucs_OnGeneralError(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Ucs_Error_t error_code = *((Ucs_Error_t *)result_ptr);
+
+ self_->init_complete = false; /* General error occurred -> Lock UCS API */
+ Ucs_StopAppNotification(self_);
+
+ if (self_->general.general_error_fptr != NULL) /* callback is not assigned during initialization */
+ {
+ self_->general.general_error_fptr(error_code, self_->ucs_user_ptr);
+ }
+}
+
+/*! \brief Observer callback for Inic_MostPortStatus_Status/Error(). Casts the result and
+ * invokes the application result callback.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result
+ */
+static void Ucs_Most_PortStatusCb(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ if(self_->xrm.most_port_status_fptr != NULL)
+ {
+ Inic_MostPortStatus_t status = *((Inic_MostPortStatus_t *)result_ptr);
+ self_->xrm.most_port_status_fptr(status.most_port_handle,
+ status.availability,
+ status.avail_info,
+ status.freestreaming_bw,
+ self_->ucs_user_ptr);
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* INIC */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Observer callback function for INICs device status
+ * \param self The instance
+ * \param data_ptr Pointer to structure Inic_DeviceStatus_t
+ */
+static void Ucs_Inic_OnDeviceStatus(void *self, void *data_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Ucs_Inic_PowerState_t pws = ((Inic_DeviceStatus_t *)data_ptr)->power_state;
+
+ if ((self_->init_data.inic.power_state_fptr != NULL) && (pws != self_->inic.power_state))
+ {
+ self_->init_data.inic.power_state_fptr(pws, self_->ucs_user_ptr);
+ }
+
+ self_->inic.power_state = pws;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Network Management */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Network_Startup(Ucs_Inst_t* self, uint16_t packet_bw, uint16_t forced_na_timeout,
+ Ucs_StdResultCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Inic_NwStartup(self_->inic.local_inic, forced_na_timeout,
+ packet_bw, &self_->net.startup_obs);
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ self_->net.startup_fptr = result_fptr;
+ }
+ }
+ return ret_val;
+}
+
+/*! \brief Callback function which announces the result of Ucs_Network_Startup()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t.
+ */
+static void Ucs_NetworkStartupResult(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (self_->net.startup_fptr != NULL)
+ {
+ self_->net.startup_fptr(result_ptr_->result, self_->ucs_user_ptr);
+ }
+}
+
+Ucs_Return_t Ucs_Network_Shutdown(Ucs_Inst_t *self, Ucs_StdResultCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Inic_NwShutdown(self_->inic.local_inic, &self_->net.shutdown_obs);
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ self_->net.shutdown_fptr = result_fptr;
+ }
+ }
+ return ret_val;
+}
+
+/*! \brief Callback function which announces the result of Ucs_Network_Shutdown()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t.
+ */
+static void Ucs_NetworkShutdownResult(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (self_->net.shutdown_fptr != NULL)
+ {
+ self_->net.shutdown_fptr(result_ptr_->result, self_->ucs_user_ptr);
+ }
+}
+
+Ucs_Return_t Ucs_Network_ForceNotAvailable(Ucs_Inst_t *self, bool force, Ucs_StdResultCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Inic_NwForceNotAvailable(self_->inic.local_inic, force, &self_->net.force_na_obs);
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ self_->net.force_na_fptr = result_fptr;
+ }
+ }
+ return ret_val;
+}
+
+/*! \brief Callback function which announces the result of Network_ForceNotAvailable()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t.
+ */
+static void Ucs_NetworkForceNAResult(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (self_->net.force_na_fptr != NULL)
+ {
+ self_->net.force_na_fptr(result_ptr_->result, self_->ucs_user_ptr);
+ }
+}
+
+Ucs_Return_t Ucs_Network_GetFrameCounter(Ucs_Inst_t *self, uint32_t reference, Ucs_Network_FrameCounterCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+ if (self_->init_complete != false)
+ {
+ ret_val = Inic_NwFrameCounter_Get(self_->inic.local_inic, reference, &self_->net.frame_counter_obs);
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ self_->net.frame_counter_fptr = result_fptr;
+ }
+ }
+ return ret_val;
+}
+
+
+/*! \brief Callback function which announces the result of Ucs_Network_GetFrameCounter()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t and data_info
+ * must be casted into Inic_FrameCounterStatus_t.
+ */
+static void Ucs_NetworkFrameCounterResult(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+
+ if (self_->net.frame_counter_fptr != NULL)
+ {
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ uint32_t reference;
+ uint32_t frame_counter;
+ uint8_t lock;
+
+ if (result_ptr_->data_info != NULL)
+ {
+ Inic_FrameCounterStatus_t *frame_counter_result_data_ptr = (Inic_FrameCounterStatus_t *)result_ptr_->data_info;
+ reference = frame_counter_result_data_ptr->reference;
+ frame_counter = frame_counter_result_data_ptr->frame_counter;
+ lock = frame_counter_result_data_ptr->lock;
+ }
+ else
+ {
+ reference = 0U;
+ frame_counter = 0U;
+ lock = 0U;
+ }
+
+ self_->net.frame_counter_fptr(reference, frame_counter, lock, result_ptr_->result, self_->ucs_user_ptr);
+ }
+}
+
+/*! \brief Observer callback which monitors the network status
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Net_NetworkStatusParam_t.
+ */
+static void Ucs_NetworkStatus(void *self, void *result_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ Net_NetworkStatusParam_t *result_ptr_ = (Net_NetworkStatusParam_t *)result_ptr;
+
+ if (self_->net.status_fptr != NULL)
+ {
+ self_->net.status_fptr( result_ptr_->change_mask,
+ result_ptr_->events,
+ result_ptr_->availability,
+ result_ptr_->avail_info,
+ result_ptr_->avail_trans_cause,
+ result_ptr_->node_address,
+ result_ptr_->node_position,
+ result_ptr_->max_position,
+ result_ptr_->packet_bw,
+ self_->ucs_user_ptr);
+ }
+}
+
+uint8_t Ucs_Network_GetNodesCount(Ucs_Inst_t *self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ return Inic_GetNumberOfNodes(self_->inic.local_inic);
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Node Discovery */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Nd_Start(Ucs_Inst_t* self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (self_->init_complete != false)
+ {
+ ret_val = Nd_Start(&self_->nd);
+ }
+ return ret_val;
+}
+
+
+Ucs_Return_t Ucs_Nd_Stop(Ucs_Inst_t* self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (self_->init_complete != false)
+ {
+ ret_val = Nd_Stop(&self_->nd);
+ }
+ return ret_val;
+}
+
+
+Ucs_Return_t Ucs_Nd_InitAll(Ucs_Inst_t* self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (self_->init_complete != false)
+ {
+ Nd_InitAll(&self_->nd);
+ ret_val = UCS_RET_SUCCESS;
+ }
+ return ret_val;
+
+}
+
+/*! \brief Callback function to proxy the user callback for node evaluation
+ * \param self The instance
+ * \param signature_ptr Reference to the node signature
+ * \return The evaluation return value which defines how to proceed with the node.
+ */
+static Ucs_Nd_CheckResult_t Ucs_OnNdEvaluate(void *self, Ucs_Signature_t *signature_ptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Nd_CheckResult_t ret_val = UCS_ND_CHK_UNKNOWN;
+
+ if (self_->init_data.nd.eval_fptr != NULL)
+ {
+ ret_val = self_->init_data.nd.eval_fptr(signature_ptr, self_->ucs_user_ptr);
+ }
+
+ return ret_val;
+}
+
+/*! \brief Callback function to proxy the user callback for node evaluation
+ * \param self The instance
+ * \param code The report code
+ * \param signature_ptr Reference to the node signature or NULL if no signature applies.
+ */
+static void Ucs_OnNdReport(void *self, Ucs_Nd_ResCode_t code, Ucs_Signature_t *signature_ptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ if (self_->init_data.nd.report_fptr != NULL)
+ {
+ self_->init_data.nd.report_fptr(code, signature_ptr, self_->ucs_user_ptr);
+ }
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* BackChannel Diagnosis */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Bcd_Start(Ucs_Inst_t* self, Ucs_Bcd_ReportCb_t report_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (report_fptr == NULL)
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+ else if (self_->init_complete != false)
+ {
+ Bcd_Start(&self_->bcd, report_fptr);
+ ret_val = UCS_RET_SUCCESS;
+ }
+ return ret_val;
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Programming service */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Return_t Ucs_Prog_Start(Ucs_Inst_t *self,
+ uint16_t node_id,
+ Ucs_Signature_t *signature,
+ Ucs_Prg_SessionType_t session_type,
+ Ucs_Prg_Command_t* command_list,
+ Ucs_Prg_ReportCb_t result_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (result_fptr == NULL)
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+ else if (self_->init_complete != false)
+ {
+ Prg_Start(&self_->prg, node_id, signature, session_type, command_list, result_fptr);
+ ret_val = UCS_RET_SUCCESS;
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Message Handling */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Initializes the port message service for application interface (MCM)
+ * \param self The instance
+ */
+static void Ucs_InitPmsComponentApp(CUcs *self)
+{
+ Fifo_InitData_t mcm_init;
+ Fifo_Config_t mcm_config;
+
+ /* Initialize the MCM channel */
+ mcm_init.base_ptr = &self->general.base;
+ mcm_init.channel_ptr = &self->pmch;
+ mcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete;
+ mcm_init.rx_cb_inst = &self->msg.mcm_transceiver;
+ mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
+ mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
+
+ /* Enable INIC watchdog */
+ mcm_config.fifo_id = PMP_FIFO_ID_MCM;
+ mcm_config.tx_wd_timeout = 10U; /* Watchdog timeout: 1s */
+ mcm_config.tx_wd_timer_value = 600U; /* Watchdog trigger every 600 ms */
+ mcm_config.rx_ack_timeout = 10U; /* Acknowledge timeout: 10 ms */
+ mcm_config.rx_busy_allowed = 0xFU;
+ mcm_config.rx_credits = PMCH_MCM_CREDITS;
+ mcm_config.rx_threshold = PMCH_MCM_THRESHOLD;
+ if (self->init_data.general.inic_watchdog_enabled == false)
+ {
+ /* Disable INIC watchdog */
+ mcm_config.tx_wd_timeout = 0U; /* Watchdog timeout: 0 -> infinite */
+ mcm_config.tx_wd_timer_value = 0U; /* Watchdog timer: 0 -> no timer */
+ mcm_config.rx_ack_timeout = 0U; /* Acknowledge timeout: 0 -> infinite */
+ }
+ Fifo_Ctor(&self->msg.mcm_fifo,&mcm_init, &mcm_config);
+#if 0
+ Fifos_Ctor(&self->fifos, &self->general.base, &self->pmch, NULL, &self->msg.mcm_fifo, NULL);
+ Pmev_Ctor(&self->pme, &self->general.base, &self->fifos); /* initialize event handler */
+#endif
+
+ /* initialize transceivers and set reference to FIFO instance */
+ Trcv_Ctor(&self->msg.mcm_transceiver, &self->msg.mcm_fifo, MSG_ADDR_EHC_APP, self->ucs_user_ptr, PMP_FIFO_ID_MCM);
+ Trcv_RxAssignFilter(&self->msg.mcm_transceiver, &Ucs_McmRx_FilterCallback, self);
+}
+
+static void Ucs_InitAmsComponent(CUcs *self)
+{
+ Smm_Ctor(&self->msg.smm, self->ucs_user_ptr);
+ (void)Smm_LoadPlugin(&self->msg.smm, &self->msg.ams_allocator, SMM_SIZE_RX_MSG);
+
+ TR_ASSERT(self->ucs_user_ptr, "[API]", (self->msg.ams_allocator.alloc_fptr != NULL));
+ TR_ASSERT(self->ucs_user_ptr, "[API]", (self->msg.ams_allocator.free_fptr != NULL));
+
+ Amsp_Ctor(&self->msg.ams_pool, &self->msg.ams_allocator, self->ucs_user_ptr);
+ Ams_Ctor(&self->msg.ams, &self->general.base, &self->msg.mcm_transceiver, NULL, &self->msg.ams_pool,
+ SMM_SIZE_RX_MSG);
+ Ams_TxSetDefaultRetries(&self->msg.ams, self->init_data.ams.tx.default_llrbc);
+
+ Amd_Ctor(&self->msg.amd, &self->general.base, &self->msg.ams);
+ Amd_AssignReceiver(&self->msg.amd, &Ucs_AmsRx_Callback, self);
+ /* Amd_RxAssignModificator(&self->amd, &Mnsa_AmdRx_Modificator, self); */
+
+ self->msg.ams_tx_alloc_failed = false;
+ Obs_Ctor(&self->msg.ams_tx_freed_obs, self, &Ucs_AmsTx_FreedCallback);
+ if (self->init_data.ams.tx.message_freed_fptr != NULL)
+ {
+ Ams_TxAssignMsgFreedObs(&self->msg.ams, &self->msg.ams_tx_freed_obs);
+ }
+
+ Cmd_Ctor(&self->msg.cmd, &self->general.base);
+}
+
+extern Ucs_AmsTx_Msg_t* Ucs_AmsTx_AllocMsg(Ucs_Inst_t *self, uint16_t data_size)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_AmsTx_Msg_t *ret_ptr = NULL;
+
+ if ((self_->init_complete != false) && (self_->init_data.ams.enabled == true))
+ {
+ ret_ptr = Ams_TxGetMsg(&self_->msg.ams, data_size);
+ }
+
+ self_->msg.ams_tx_alloc_failed = (ret_ptr == NULL) ? true : false;
+ return ret_ptr;
+}
+
+extern Ucs_Return_t Ucs_AmsTx_SendMsg(Ucs_Inst_t *self, Ucs_AmsTx_Msg_t *msg_ptr, Ucs_AmsTx_CompleteCb_t tx_complete_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if ((self_->init_complete != false) && (self_->init_data.ams.enabled == true))
+ {
+ ret_val = Ams_TxSendMsg(&self_->msg.ams, msg_ptr, NULL, tx_complete_fptr, self_->ucs_user_ptr);
+ }
+
+ return ret_val;
+}
+
+extern void Ucs_AmsTx_FreeUnusedMsg(Ucs_Inst_t *self, Ucs_AmsTx_Msg_t *msg_ptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ if (msg_ptr != NULL)
+ {
+ Ams_TxFreeUnusedMsg(&self_->msg.ams, msg_ptr);
+ }
+}
+
+extern Ucs_AmsRx_Msg_t* Ucs_AmsRx_PeekMsg(Ucs_Inst_t *self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ Ucs_AmsRx_Msg_t *ret = NULL;
+
+ if ((self_->init_complete != false) && (self_->init_data.ams.enabled == true))
+ {
+ ret = Amd_RxPeekMsg(&self_->msg.amd);
+ }
+
+ return ret;
+}
+
+extern void Ucs_AmsRx_ReleaseMsg(Ucs_Inst_t *self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ if ((self_->init_complete != false) && (self_->init_data.ams.enabled == true))
+ {
+ Amd_RxReleaseMsg(&self_->msg.amd);
+ }
+}
+
+extern uint16_t Ucs_AmsRx_GetMsgCnt(Ucs_Inst_t *self)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ uint16_t ret = 0U;
+
+ if ((self_->init_complete != false) && (self_->init_data.ams.enabled == true))
+ {
+ ret = Amd_RxGetMsgCnt(&self_->msg.amd);
+ }
+ return ret;
+}
+
+/*! \brief Callback function which announces that a new application message
+ * is added to the Rx queue
+ * \param self The instance
+ */
+static void Ucs_AmsRx_Callback(void *self)
+{
+ CUcs *self_ = (CUcs*)self;
+
+ if (self_->init_data.ams.rx.message_received_fptr != NULL)
+ {
+ self_->init_data.ams.rx.message_received_fptr(self_->ucs_user_ptr);
+ }
+}
+
+/*! \brief Callback function which announces that the AMS Tx Pool provides again a Tx message object
+ * after a prior allocation has failed.
+ * \param self The instance
+ * \param data_ptr Not used (always \c NULL)
+ */
+static void Ucs_AmsTx_FreedCallback(void *self, void *data_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ MISC_UNUSED(data_ptr);
+
+ if ((self_->msg.ams_tx_alloc_failed != false) && (self_->init_complete != false))
+ {
+ self_->msg.ams_tx_alloc_failed = false;
+ self_->init_data.ams.tx.message_freed_fptr(self_->ucs_user_ptr);
+ }
+}
+
+/*! \brief Callback function which filters MCM Rx messages
+ * \param self The instance
+ * \param tel_ptr The received Rx message object
+ * \return Returns \c true to discard the message and free it to the pool (no-pass).
+ * Otherwise, returns \c false (pass).
+ */
+static bool Ucs_McmRx_FilterCallback(void *self, Msg_MostTel_t *tel_ptr)
+{
+ CUcs *self_ = (CUcs*)self;
+ bool ret = false; /* default: pass the message */
+
+ if ((tel_ptr->id.fblock_id != MSG_DEF_FBLOCK_ID) || (tel_ptr->id.op_type != MSG_DEF_OP_TYPE) ||
+ ((tel_ptr->id.function_id & (uint16_t)0x000FU) != MSG_DEF_FUNC_ID_LSN))
+ {
+ TR_INFO((self_->ucs_user_ptr, "[API]", "Ucs_McmRx_FilterCallback(): discarding Rx message with signature %02X.%02X.%03X.%X ", 4U, tel_ptr->id.fblock_id, tel_ptr->id.instance_id, tel_ptr->id.function_id, tel_ptr->id.op_type));
+ ret = true;
+ }
+
+ MISC_UNUSED(self_);
+
+ return ret;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Message decoding */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Cmd_Return_t Ucs_Cmd_AddMsgIdTable(Ucs_Inst_t *self, Ucs_Cmd_MsgId_t *msg_id_tab_ptr)
+{
+ Ucs_Cmd_Return_t ret_val;
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ if (msg_id_tab_ptr != NULL)
+ {
+ ret_val = Cmd_AddMsgIdTable(&(self_->msg.cmd), msg_id_tab_ptr);
+ }
+ else
+ {
+ ret_val = UCS_CMD_RET_ERR_NULL_PTR;
+ }
+
+ return ret_val;
+}
+
+
+Ucs_Cmd_Return_t Ucs_Cmd_RemoveMsgIdTable(Ucs_Inst_t *self)
+{
+ Ucs_Cmd_Return_t ret_val;
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ ret_val = Cmd_RemoveMsgIdTable(&(self_->msg.cmd));
+
+ return ret_val;
+}
+
+
+Ucs_Cmd_Return_t Ucs_Cmd_DecodeMsg(Ucs_Inst_t *self, Ucs_AmsRx_Msg_t *msg_rx_ptr)
+{
+ Ucs_Cmd_Return_t ret_val;
+ CUcs *self_ = (CUcs*)(void*)self;
+
+ if(msg_rx_ptr != NULL)
+ {
+ ret_val = Cmd_DecodeMsg(&(self_->msg.cmd), msg_rx_ptr);
+ }
+ else
+ {
+ ret_val = UCS_CMD_RET_ERR_NULL_PTR;
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Unit tests only */
+/*------------------------------------------------------------------------------------------------*/
+extern void Ucs_AssignRxFilter(Ucs_Inst_t *self, Ucs_RxFilterCb_t callback_fptr)
+{
+ CUcs *self_ = (CUcs*)(void*)self;
+ self_->rx_filter_fptr = callback_fptr;
+}
+
+
+/*!
+ * @}
+ * \endcond
+ */
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_cmd.c b/ucs2-lib/src/ucs_cmd.c
new file mode 100644
index 0000000..4865b88
--- /dev/null
+++ b/ucs2-lib/src/ucs_cmd.c
@@ -0,0 +1,191 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Command Interpreter.
+ *
+ * \cond UCS_INTERNAL_DOC
+ *
+ * \addtogroup G_UCS_CMD_INT
+ * @{
+ */
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_cmd.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+
+
+static Ucs_Cmd_Return_t Cmd_SearchMsgId(Ucs_Cmd_MsgId_t msg_id_tab[], uint16_t *index_ptr,
+ uint16_t message_id);
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+void Cmd_Ctor(CCmd *self, CBase *base_ptr)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */
+
+ self->msg_id_tab_ptr = NULL;
+ self->ucs_user_ptr = base_ptr->ucs_user_ptr;
+}
+
+
+/*! \brief Add a MessageId Table to the Command Interpreter.
+ * \param self Instance pointer
+ * \param msg_id_tab_ptr Reference to a MessageId Table
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------------- | ------------------------------------
+ * UCS_CMD_RET_SUCCESS | MessageId Table was successfully added
+ * UCS_CMD_RET_ERR_ALREADY_ENTERED | MessageId Table already added
+ */
+Ucs_Cmd_Return_t Cmd_AddMsgIdTable(CCmd *self, Ucs_Cmd_MsgId_t *msg_id_tab_ptr)
+{
+ Ucs_Cmd_Return_t ret_val = UCS_CMD_RET_SUCCESS;
+
+
+ if (self->msg_id_tab_ptr != NULL)
+ {
+ ret_val = UCS_CMD_RET_ERR_ALREADY_ENTERED;
+ }
+ else
+ {
+ self->msg_id_tab_ptr = msg_id_tab_ptr;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Remove an MessageId Table from the Command Interpreter.
+ * \param self Instance pointer of Cmd
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ---------------------------- | ------------------------------------
+ * UCS_CMD_RET_SUCCESS | MessageId Table was successfully removed
+ */
+Ucs_Cmd_Return_t Cmd_RemoveMsgIdTable(CCmd *self)
+{
+ Ucs_Cmd_Return_t ret_val = UCS_CMD_RET_SUCCESS;
+
+ self->msg_id_tab_ptr = NULL;
+
+ return ret_val;
+}
+
+
+/*! \brief Decode an MCM message
+ * \param self Instance pointer
+ * \param msg_rx_ptr Pointer to the message to decode
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * -------------------------------- | ------------------------------------
+ * UCS_CMD_RET_SUCCESS | decoding was successful
+ * UCS_CMD_RET_ERR_MSGID_NOTAVAIL | MessageId not found
+ * UCS_CMD_RET_ERR_TX_BUSY | no Tx Buffer available
+ * UCS_CMD_RET_ERR_APPL | error happened in handler function
+ * UCS_CMD_RET_ERR_NULL_PTR | No MessageId Table available
+ */
+Ucs_Cmd_Return_t Cmd_DecodeMsg(CCmd *self, Ucs_AmsRx_Msg_t *msg_rx_ptr)
+{
+ Ucs_Cmd_Return_t result = UCS_CMD_RET_SUCCESS;
+ uint16_t index;
+
+ result = Cmd_SearchMsgId(self->msg_id_tab_ptr, &index, msg_rx_ptr->msg_id);
+
+ if (result == UCS_CMD_RET_SUCCESS)
+ {
+ /* call handler function */
+ result = (Ucs_Cmd_Return_t)(self->msg_id_tab_ptr[index].handler_function_ptr(msg_rx_ptr, self->ucs_user_ptr));
+ }
+
+ return result;
+}
+
+
+/*! \brief Search in a MessageId Table for matching MessageId
+ * \details Function expects that the MessageId Table ends with a termination entry
+ * (handler_function_ptr == NULL). If this entry is not present, the search may end in an
+ * endless loop.
+ * \param msg_id_tab MessageId Table
+ * \param index_ptr pointer to the matching element
+ * \param message_id MessageId
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------------- | ------------------------------------
+ * UCS_CMD_RET_SUCCESS | decoding was successful
+ * UCS_CMD_RET_ERR_MSGID_NOTAVAIL | MessageId not found
+ * UCS_CMD_RET_ERR_NULL_PTR | No MessageId Table available
+ */
+static Ucs_Cmd_Return_t Cmd_SearchMsgId(Ucs_Cmd_MsgId_t msg_id_tab[], uint16_t *index_ptr,
+ uint16_t message_id)
+{
+ Ucs_Cmd_Return_t ret_val = UCS_CMD_RET_SUCCESS;
+ uint16_t i = 0U;
+
+ if (msg_id_tab == NULL)
+ {
+ ret_val = UCS_CMD_RET_ERR_NULL_PTR;
+ }
+ else
+ {
+ while (msg_id_tab[i].handler_function_ptr != NULL) /* last entry */
+ {
+ if (msg_id_tab[i].msg_id != message_id)
+ {
+ ++i; /* goto next list element */
+ }
+ else
+ {
+ *index_ptr = i;
+ break;
+ }
+ }
+
+ if (msg_id_tab[i].handler_function_ptr == NULL) /* no match found */
+ {
+ ret_val = UCS_CMD_RET_ERR_MSGID_NOTAVAIL;
+ }
+ }
+ return ret_val;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_dec.c b/ucs2-lib/src/ucs_dec.c
new file mode 100644
index 0000000..b8aef2f
--- /dev/null
+++ b/ucs2-lib/src/ucs_dec.c
@@ -0,0 +1,131 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Command Interpreter Module.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_DEC_INT
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_dec.h"
+#include "ucs_misc.h"
+#include "ucs_ret_pb.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Search in a FktOp table for matching FktID and OPType. This function is used for
+ * incoming ICM messages.
+ * \param list FktOp table
+ * \param index_ptr Reference to array index of the matching array element
+ * \param function_id FktID
+ * \param op_type OPType
+ * \return DEC_RET_SUCCESS Decoding was successful
+ * DEC_RET_FKTID_NOT_FOUND FktID/OPType not found
+ */
+Dec_Return_t Dec_SearchFktOpIcm(Dec_FktOpIcm_t const list[], uint16_t *index_ptr,
+ uint16_t function_id, Ucs_OpType_t op_type)
+{
+ uint16_t fktop;
+ uint16_t i = 0U;
+ Dec_Return_t ret_val = DEC_RET_FKTID_NOT_FOUND;
+ bool loop = true;
+
+ fktop = DEC_FKTOP(function_id, op_type);
+ *index_ptr = 0U;
+
+ while ((list[i].handler_function_ptr != NULL) && (loop != false))
+ {
+ if(list[i].fkt_op == fktop)
+ {
+ ret_val = DEC_RET_SUCCESS;
+ *index_ptr = i;
+ loop = false;
+ }
+ else if (list[i].fkt_op > fktop)
+ {
+ loop = false;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Search in a FktOp table for matching FktID and OPType. This function is used for
+ * MCM messages coming from FBlocks inside the INIC.
+ * \param list FktOp table
+ * \param index_ptr Reference to array index of the matching array element
+ * \param function_id FktID
+ * \param op_type OPType
+ * \return DEC_RET_SUCCESS Decoding was successful
+ * DEC_RET_FKTID_NOT_FOUND FktID/OPType not found
+ */
+Dec_Return_t Dec_SearchFktOpIsh(Dec_FktOpIsh_t const list[], uint16_t *index_ptr,
+ uint16_t function_id, Ucs_OpType_t op_type)
+{
+ uint16_t fktop;
+ uint16_t i = 0U;
+ Dec_Return_t ret_val = DEC_RET_FKTID_NOT_FOUND;
+ bool loop = true;
+
+ fktop = DEC_FKTOP(function_id, op_type);
+ *index_ptr = 0U;
+
+ while ((list[i].handler_function_ptr != NULL) && (loop != false))
+ {
+ if(list[i].fkt_op == fktop)
+ {
+ ret_val = DEC_RET_SUCCESS;
+ *index_ptr = i;
+ loop = false;
+ }
+ else if (list[i].fkt_op > fktop)
+ {
+ loop = false;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ return ret_val;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_dl.c b/ucs2-lib/src/ucs_dl.c
new file mode 100644
index 0000000..7bb106f
--- /dev/null
+++ b/ucs2-lib/src/ucs_dl.c
@@ -0,0 +1,390 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the doubly linked list.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_DL
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_dl.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CDlList */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the doubly linked list class.
+ * \param self Instance pointer
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Dl_Ctor(CDlList *self, void *ucs_user_ptr)
+{
+ self->head = NULL;
+ self->tail = NULL;
+ self->size = 0U;
+ self->ucs_user_ptr = ucs_user_ptr;
+}
+
+/*! \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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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(node != NULL) /* Is list not empty? */
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[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(node != NULL) /* Is list not empty? */
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[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(node != NULL) /* 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(current_node != NULL) /* 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->ucs_user_ptr, "[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/ucs2-lib/src/ucs_eh.c b/ucs2-lib/src/ucs_eh.c
new file mode 100644
index 0000000..bc11847
--- /dev/null
+++ b/ucs2-lib/src/ucs_eh.c
@@ -0,0 +1,153 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the event handler.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_EH
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_eh.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static bool Eh_EncodeEvent(uint32_t event_code, Ucs_Error_t *public_error_code_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CEventHandler */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the event handler class.
+ * \param self Instance pointer
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Eh_Ctor(CEventHandler *self, void * ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ /* Save UNICENS instance ID */
+ self->ucs_user_ptr = ucs_user_ptr;
+ /* Initialize subject for internal events */
+ Sub_Ctor(&self->internal_event_subject, self->ucs_user_ptr);
+ /* Initialize subject for public error reporting */
+ Ssub_Ctor(&self->public_error_subject, self->ucs_user_ptr);
+}
+
+/*! \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)
+{
+ Ucs_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, Ucs_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 = UCS_GEN_ERR_INIC;
+ break;
+ case EH_E_UNSYNC_COMPLETE:
+ case EH_E_UNSYNC_FAILED:
+ *public_error_code_ptr = UCS_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/ucs2-lib/src/ucs_encoder.c b/ucs2-lib/src/ucs_encoder.c
new file mode 100644
index 0000000..46d90b5
--- /dev/null
+++ b/ucs2-lib/src/ucs_encoder.c
@@ -0,0 +1,253 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+
+/*!
+ * \file
+ * \brief Implementation of message encoder
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_ENCODER
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_encoder.h"
+#include "ucs_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 = (Ucs_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 = (Ucs_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 = (Ucs_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/ucs2-lib/src/ucs_epm.c b/ucs2-lib/src/ucs_epm.c
new file mode 100644
index 0000000..adc5aab
--- /dev/null
+++ b/ucs2-lib/src/ucs_epm.c
@@ -0,0 +1,495 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the EndPoint Management.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_EPM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_epm.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Epm_XrmReportCb (uint16_t node_address, uint16_t connection_label, Ucs_Xrm_Result_t result, void * user_arg);
+static bool Epm_RsmReportSyncLost (Fac_Inst_t inst_type, void * inst_ptr, void *ud_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CEndpointManagement */
+/*------------------------------------------------------------------------------------------------*/
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Remote Sync Manager class.
+ * \param self Instance pointer
+ * \param init_ptr init data_ptr
+ */
+void Epm_Ctor(CEndpointManagement *self, Epm_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CEndpointManagement));
+
+ /* Init all instances */
+ self->fac_ptr = init_ptr->fac_ptr;
+ self->base_ptr = init_ptr->base_ptr;
+ self->res_debugging_fptr = init_ptr->res_debugging_fptr;
+ self->check_unmute_fptr = init_ptr->check_unmute_fptr;
+}
+
+/*! \brief Initializes the internal information of the given endpoint object.
+ *
+ * Initialization is performed only if the magic number is not set.
+ *
+ * \param self Instance pointer
+ * \param ep_ptr Reference to the endpoint to be looked for
+ */
+void Epm_InitInternalInfos(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ if ((self != NULL) && (ep_ptr != NULL))
+ {
+ if (ep_ptr->internal_infos.magic_number != (uint32_t)0x0BADC0DE)
+ {
+ MISC_MEM_SET(&ep_ptr->internal_infos, 0, sizeof(Ucs_Rm_EndPointInt_t));
+
+ ep_ptr->internal_infos.magic_number = (uint32_t)0x0BADC0DE;
+ Sub_Ctor(&ep_ptr->internal_infos.subject_obj, self->base_ptr->ucs_user_ptr);
+ /* Set the EndpointManagement instance */
+ ep_ptr->internal_infos.epm_inst = (Epm_Inst_t *)(void *)self;
+ }
+ }
+}
+
+/*! \brief Clears the internal information of the given endpoint object.
+ *
+ * Resetting the magic number of the given endpoint will enforce Its Re-Initialization.
+ *
+ * \param self Instance pointer
+ * \param ep_ptr Reference to the endpoint to be cleared.
+ */
+void Epm_ClearIntInfos(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ MISC_UNUSED (self);
+ if (ep_ptr != NULL)
+ {
+ ep_ptr->internal_infos.magic_number = 0x0U;
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Add an observer to the Endpoint's subject.
+ * \param ep_ptr Reference to the endpoint instance
+ * \param obs_ptr Reference to the observer object
+ */
+void Epm_AddObserver(Ucs_Rm_EndPoint_t * ep_ptr, CObserver * obs_ptr)
+{
+ Sub_Ret_t ret_val = SUB_UNKNOWN_OBSERVER;
+
+ ret_val = Sub_AddObserver(&ep_ptr->internal_infos.subject_obj, obs_ptr);
+ if (ret_val == SUB_OK)
+ {
+ if ((ep_ptr != NULL) && (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE))
+ {
+ if ((ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT) && (ep_ptr->internal_infos.reference_cnt > 0U))
+ {
+ ep_ptr->internal_infos.reference_cnt++;
+ }
+ }
+ }
+}
+
+/*! \brief Removes an observer registered by Epm_AddObserver
+ * \param ep_ptr Reference to the endpoint instance
+ * \param obs_ptr Reference to the observer object
+ */
+void Epm_DelObserver(Ucs_Rm_EndPoint_t * ep_ptr, CObserver * obs_ptr)
+{
+ (void)Sub_RemoveObserver(&ep_ptr->internal_infos.subject_obj, obs_ptr);
+}
+
+/*! \brief Processes the construction of the given endpoint
+ * \param self Instance pointer
+ * \param ep_ptr reference to an endpoint
+ * \return Possible return values are
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked. Endpoint is currently being processed.
+ * - \c UCS_RET_SUCCESS the build process was set successfully
+ * - \c UCS_RET_ERR_PARAM NULL pointer detected in the parameter list
+ * - \c UCS_RET_ERR_ALREADY_SET the endpoint has already been set
+ */
+Ucs_Return_t Epm_SetBuildProcess(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((self != NULL) && (ep_ptr != NULL))
+ {
+ /* Process Endpoint construction by XRM */
+ result = Xrm_Process(Fac_GetXrm(self->fac_ptr, ep_ptr->node_obj_ptr->signature_ptr->node_address, &Epm_XrmResDebugCb, self->check_unmute_fptr),
+ ep_ptr->jobs_list_ptr, ep_ptr->internal_infos.connection_label,
+ (void *)ep_ptr, &Epm_XrmReportCb);
+ if (result == UCS_RET_SUCCESS)
+ {
+ if (ep_ptr->internal_infos.endpoint_state != UCS_RM_EP_BUILT)
+ {
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_XRMPROCESSING;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "XRM has been ordered to create following Endpoint: %X", 1U, ep_ptr));
+ }
+ else
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been built", 1U, ep_ptr));
+ }
+ }
+ else if (result == UCS_RET_ERR_ALREADY_SET)
+ {
+ if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_IDLE)
+ {
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_BUILT;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been built", 1U, ep_ptr));
+ }
+ }
+ else if (result == UCS_RET_ERR_NOT_AVAILABLE)
+ {
+ /* Set the internal error */
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ ep_ptr->internal_infos.xrm_result.code = UCS_XRM_RES_ERR_BUILD;
+ ep_ptr->internal_infos.xrm_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ ep_ptr->internal_infos.xrm_result.details.int_result = result;
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Processes the destruction of the given endpoint
+ * \param self Instance pointer
+ * \param ep_ptr reference to an endpoint
+ * \return Possible return values are
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked. Endpoint is currently being processed.
+ * - \c UCS_RET_SUCCESS the build process was set successfully
+ * - \c UCS_RET_ERR_PARAM At least one parameter is not correct, either NULL pointer in the param list or reference_cnt of the endpoint is NULL.
+ * - \c UCS_RET_ERR_ALREADY_SET the endpoint has already been set
+ * - \c UCS_RET_ERR_NOT_AVAILABLE the endpoint cannot be destroyed since its reference_cnt is greater than 1, i.e. it's in use.
+ * - \c UCS_RET_ERR_INVALID_SHADOW the endpoint cannot be destroyed since its reference_cnt is greater than 1, i.e. it's in use.
+ */
+Ucs_Return_t Epm_SetDestroyProcess(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+ bool can_be_destroyed = true;
+
+ if ((self != NULL) && (ep_ptr != NULL) )
+ {
+ if (UCS_RM_EP_SOURCE == ep_ptr->endpoint_type)
+ {
+ if (ep_ptr->internal_infos.reference_cnt == 0U)
+ {
+ can_be_destroyed = false;
+ result = UCS_RET_ERR_PARAM;
+ }
+ else if (ep_ptr->internal_infos.reference_cnt > 1U)
+ {
+ ep_ptr->internal_infos.reference_cnt--;
+ can_be_destroyed = false;
+ result = UCS_RET_ERR_INVALID_SHADOW;
+ }
+ }
+
+ if (can_be_destroyed)
+ {
+ result = Xrm_Destroy(Fac_GetXrmByJobList(self->fac_ptr, ep_ptr->jobs_list_ptr), ep_ptr->jobs_list_ptr);
+ if (result == UCS_RET_SUCCESS)
+ {
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_XRMPROCESSING;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "XRM has been ordered to destroy following Endpoint {%X}", 1U, ep_ptr));
+ }
+ else if (result == UCS_RET_ERR_ALREADY_SET)
+ {
+ if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT)
+ {
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been destroyed", 1U, ep_ptr));
+ }
+ }
+ else if (result == UCS_RET_ERR_NOT_AVAILABLE)
+ {
+ if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT)
+ {
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has already been destroyed", 1U, ep_ptr));
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Returns the state (idle, processing or built) of the given endpoint.
+ * \param self Instance pointer.
+ * \param ep_ptr Reference to the endpoint to be looked for
+ * \return state of the endpoint.
+ */
+Ucs_Rm_EndPointState_t Epm_GetState(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ MISC_UNUSED (self);
+
+ return (ep_ptr != NULL) ? ep_ptr->internal_infos.endpoint_state:UCS_RM_EP_IDLE;
+}
+
+/*! \brief Forces EPM to reset the state of this endpoint.
+ * \param self Instance pointer.
+ * \param ep_ptr Reference to the endpoint to be looked for.
+ */
+void Epm_ResetState(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ MISC_UNUSED (self);
+
+ if (ep_ptr != NULL)
+ {
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ ep_ptr->internal_infos.xrm_result.code = UCS_XRM_RES_UNKNOWN;
+ }
+}
+
+/*! \brief Sets the connection label of the given endpoint.
+ * \param self Instance pointer.
+ * \param ep_ptr Reference to the endpoint to be looked for
+ * \param conn_label connection label to be set
+ */
+void Epm_SetConnectionLabel(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr, uint16_t conn_label)
+{
+ MISC_UNUSED (self);
+
+ if (ep_ptr != NULL)
+ {
+ ep_ptr->internal_infos.connection_label = conn_label;
+ }
+}
+
+/*! \brief Returns the connection label of the given endpoint.
+ * \param self Instance pointer.
+ * \param ep_ptr Reference to the endpoint to be looked for
+ * \return connection label of the endpoint.
+ */
+uint16_t Epm_GetConnectionLabel(CEndpointManagement * self, Ucs_Rm_EndPoint_t * ep_ptr)
+{
+ MISC_UNUSED (self);
+
+ return (ep_ptr != NULL) ? ep_ptr->internal_infos.connection_label:0U;
+}
+
+/*! \brief This function must be called when a device get invalid.
+ * \param self Reference to the MNS instance.
+ * \param destination_address MOST device address of the target.
+ */
+void Epm_ReportInvalidDevice(CEndpointManagement *self, uint16_t destination_address)
+{
+ if (MSG_ADDR_INIC != destination_address)
+ {
+ CRemoteSyncManagement * rsm_inst = Fac_FindRsm(self->fac_ptr, destination_address);
+ if (NULL != rsm_inst)
+ {
+ Rsm_ReportSyncLost(rsm_inst);
+ }
+ }
+}
+
+/*! \brief Whenever this function has been called, the EndpointManager has to inform his sub-modules that a shutdown occurred.
+ * This function forwards the Network "NotAvailable" information
+ * \param self Instance pointer.
+ */
+void Epm_ReportShutDown(CEndpointManagement * self)
+{
+ Fac_Foreach(self->fac_ptr, FAC_INST_RSM, &Epm_RsmReportSyncLost, NULL);
+}
+
+/*! \brief Function signature used for monitoring the XRM resources.
+ * \param resource_type The XRM resource type to be looked for
+ * \param resource_ptr Reference to the resource to be looked for
+ * \param resource_infos Resource information
+ * \param endpoint_inst_ptr Reference to the endpoint object that encapsulates the given resource.
+ * \param user_ptr User reference provided in \ref Ucs_InitData_t "Ucs_InitData_t::user_ptr"
+ */
+void Epm_XrmResDebugCb (Ucs_Xrm_ResourceType_t resource_type, Ucs_Xrm_ResObject_t *resource_ptr,
+ Ucs_Xrm_ResourceInfos_t resource_infos, void *endpoint_inst_ptr, void *user_ptr)
+{
+ Ucs_Rm_EndPoint_t * ep_ptr = (Ucs_Rm_EndPoint_t *)endpoint_inst_ptr;
+ if (ep_ptr != NULL)
+ {
+ CEndpointManagement * self = (CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst;
+ if (self->res_debugging_fptr != NULL)
+ {
+ self->res_debugging_fptr(resource_type, resource_ptr, resource_infos, ep_ptr, user_ptr);
+ }
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Reports "SyncLost" to the RSM instance returned.
+ * \param inst_type The instance type to be looked for.
+ * \param inst_ptr Reference to the instance to be looked for.
+ * \param ud_ptr Reference to the user data.
+ * \return false in order to retrieve the next instance of the given type, otherwise false.
+ */
+static bool Epm_RsmReportSyncLost(Fac_Inst_t inst_type, void * inst_ptr, void *ud_ptr)
+{
+ bool ret_val = false;
+ MISC_UNUSED(ud_ptr);
+
+ switch (inst_type)
+ {
+ case FAC_INST_RSM:
+ Rsm_ReportSyncLost((CRemoteSyncManagement *)inst_ptr);
+ break;
+
+ default:
+ ret_val = true;
+ break;
+ }
+
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Callback Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief XRM report callback function.
+ * \param node_address The node address from which the results come
+ * \param connection_label Returned MOST network connection label
+ * \param result Result of the job
+ * \param user_arg Reference to the user argument
+ */
+static void Epm_XrmReportCb(uint16_t node_address, uint16_t connection_label, Ucs_Xrm_Result_t result, void * user_arg)
+{
+ Ucs_Rm_EndPoint_t * ep_ptr = (Ucs_Rm_EndPoint_t *)user_arg;
+ uint8_t handle_not_found = 0x32U;
+ uint8_t error_id = 2U;
+
+ MISC_UNUSED (node_address);
+
+ if (ep_ptr != NULL)
+ {
+ ep_ptr->internal_infos.xrm_result = result;
+ switch (result.code)
+ {
+ case UCS_XRM_RES_SUCCESS_BUILD:
+ ep_ptr->internal_infos.connection_label = connection_label;
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_BUILT;
+ if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
+ {
+ ep_ptr->internal_infos.reference_cnt++;
+ }
+ TR_INFO((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has been successfully built", 1U, ep_ptr));
+ break;
+
+ case UCS_XRM_RES_SUCCESS_DESTROY:
+ ep_ptr->internal_infos.connection_label = 0xFFFFU;
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
+ {
+ if (ep_ptr->internal_infos.reference_cnt > 0U)
+ {
+ ep_ptr->internal_infos.reference_cnt--;
+ }
+ }
+ TR_INFO((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has been successfully destroyed", 1U, ep_ptr));
+ break;
+
+ case UCS_XRM_RES_RC_AUTO_DESTROYED:
+ TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Following Endpoint {%X} has been auto destroyed.", 1U, ep_ptr));
+ ep_ptr->internal_infos.connection_label = 0xFFFFU;
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
+ {
+ ep_ptr->internal_infos.reference_cnt = 0U;
+ }
+ if(Sub_GetNumObservers(&ep_ptr->internal_infos.subject_obj) > 0U)
+ {
+ Sub_Notify(&ep_ptr->internal_infos.subject_obj, (void *)ep_ptr);
+ }
+ break;
+
+ case UCS_XRM_RES_ERR_CONFIG:
+ case UCS_XRM_RES_ERR_SYNC:
+ case UCS_XRM_RES_ERR_BUILD:
+ ep_ptr->internal_infos.connection_label = 0xFFFFU;
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Building endpoint {%X} failed. Error_Code: 0x%02X", 2U, ep_ptr, result.code));
+ break;
+
+ case UCS_XRM_RES_ERR_DESTROY:
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ if (ep_ptr->internal_infos.xrm_result.details.result_type == UCS_XRM_RESULT_TYPE_TGT)
+ {
+ if ((ep_ptr->internal_infos.xrm_result.details.inic_result.code == UCS_RES_ERR_CONFIGURATION) &&
+ (ep_ptr->internal_infos.xrm_result.details.inic_result.info_ptr != NULL) &&
+ (ep_ptr->internal_infos.xrm_result.details.inic_result.info_size > 2U))
+ {
+ if (ep_ptr->internal_infos.xrm_result.details.inic_result.info_ptr[error_id] == handle_not_found)
+ {
+ ep_ptr->internal_infos.xrm_result.code = UCS_XRM_RES_SUCCESS_DESTROY;
+ }
+ }
+ }
+ if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
+ {
+ ep_ptr->internal_infos.reference_cnt = 0U;
+ }
+ TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Destroying endpoint {%X} failed. Error_Code: 0x%02X", 2U, ep_ptr, result.code));
+ break;
+
+ case UCS_XRM_RES_ERR_INV_LIST:
+ TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Request of invalid lists on endpoint {%X} failed.", 1U, ep_ptr));
+ if (ep_ptr->internal_infos.endpoint_state == UCS_RM_EP_BUILT)
+ {
+ ep_ptr->internal_infos.connection_label = 0xFFFFU;
+ ep_ptr->internal_infos.endpoint_state = UCS_RM_EP_IDLE;
+ if(Sub_GetNumObservers(&ep_ptr->internal_infos.subject_obj) > 0U)
+ {
+ Sub_Notify(&ep_ptr->internal_infos.subject_obj, (void *)ep_ptr);
+ }
+ }
+ break;
+
+ default:
+ TR_ERROR((((CEndpointManagement *)(void *)ep_ptr->internal_infos.epm_inst)->base_ptr->ucs_user_ptr, "[EPM]", "Processing endpoint {%X} failed. Unknown Error_Code: 0x%02X", 2U, ep_ptr, result.code));
+ break;
+ }
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_exc.c b/ucs2-lib/src/ucs_exc.c
new file mode 100644
index 0000000..5f5b5db
--- /dev/null
+++ b/ucs2-lib/src/ucs_exc.c
@@ -0,0 +1,1711 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of FBlock ExtendedNetworkControl
+ * \details Contains the housekeeping functions of INIC management
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_EXC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_misc.h"
+#include "ucs_ret_pb.h"
+#include "ucs_exc.h"
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal definitions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Bitmask for API method Exc_PhyTestResult_Get() used by API locking manager */
+#define EXC_API_PHY_LAY_TEST_RESULT 0x01U
+/*! \brief Bitmask for API method Exc_MemSessionOpen_Sr() used by API locking manager */
+#define EXC_API_MEM_SESSION_OPEN 0x02U
+/*! \brief Bitmask for API method Exc_MemSessionClose_Sr() used by API locking manager */
+#define EXC_API_MEM_SESSION_CLOSE 0x04U
+/*! \brief Bitmask for API method Exc_MemoryRead_Sr() used by API locking manager */
+#define EXC_API_MEM_READ 0x08U
+/*! \brief Bitmask for API method Exc_MemoryWrite_Sr() used by API locking manager */
+#define EXC_API_MEM_WRITE 0x10U
+
+/*! \brief max. number of elements used in MemoryWrite and MemoryWrite messages */
+#define MAX_UNIT_LEN 18U
+
+/*! \brief length of signature (V1) */
+#define EXC_SIGNATURE_LEN_V1 26U
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Exc_DecodeMsg(CExc *self, Msg_MostTel_t *msg_rx_ptr);
+static void Exc_EnablePort_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_EnablePort_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_Hello_Status(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_Hello_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_Welcome_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_Welcome_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_Signature_Status(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_Signature_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_DeviceInit_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_CableLinkDiag_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_CableLinkDiag_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_NwPhyTest_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_NwPhyTestResult_Status(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_NwPhyTestResult_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_BC_Diag_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_BC_Diag_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_BC_EnableTx_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_BC_EnableTx_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemoryRead_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemoryRead_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemoryWrite_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemoryWrite_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemSessionOpen_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemSessionOpen_Error(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemSessionClose_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Exc_MemSessionClose_Error(void *self, Msg_MostTel_t *msg_ptr);
+
+static void Exc_HandleApiTimeout(void *self, void *method_mask_ptr);
+
+static Ucs_StdResult_t Exc_TranslateError(CExc *self, uint8_t error_data[], uint8_t error_size);
+static void Exc_Read_Signature(Ucs_Signature_t *dest, uint8_t source[]);
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief List of all EXC messages */
+static const Dec_FktOpIsh_t exc_handler[] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+ { DEC_FKTOP(EXC_FID_HELLO, UCS_OP_STATUS), Exc_Hello_Status },
+ { DEC_FKTOP(EXC_FID_HELLO, UCS_OP_ERROR), Exc_Hello_Error },
+ { DEC_FKTOP(EXC_FID_WELCOME, UCS_OP_RESULT), Exc_Welcome_Result },
+ { DEC_FKTOP(EXC_FID_WELCOME, UCS_OP_ERROR), Exc_Welcome_Error },
+ { DEC_FKTOP(EXC_FID_SIGNATURE, UCS_OP_STATUS), Exc_Signature_Status },
+ { DEC_FKTOP(EXC_FID_SIGNATURE, UCS_OP_ERROR), Exc_Signature_Error },
+ { DEC_FKTOP(EXC_FID_DEVICE_INIT, UCS_OP_ERROR), Exc_DeviceInit_Error },
+ { DEC_FKTOP(EXC_FID_ENABLEPORT, UCS_OP_RESULT), Exc_EnablePort_Result },
+ { DEC_FKTOP(EXC_FID_ENABLEPORT, UCS_OP_ERROR), Exc_EnablePort_Error },
+ { DEC_FKTOP(EXC_FID_CABLE_LINK_DIAG, UCS_OP_RESULT), Exc_CableLinkDiag_Result },
+ { DEC_FKTOP(EXC_FID_CABLE_LINK_DIAG, UCS_OP_ERROR), Exc_CableLinkDiag_Error },
+ { DEC_FKTOP(EXC_FID_PHY_LAY_TEST, UCS_OP_ERROR), Exc_NwPhyTest_Error },
+ { DEC_FKTOP(EXC_FID_PHY_LAY_TEST_RES, UCS_OP_STATUS), Exc_NwPhyTestResult_Status },
+ { DEC_FKTOP(EXC_FID_PHY_LAY_TEST_RES, UCS_OP_ERROR), Exc_NwPhyTestResult_Error },
+ { DEC_FKTOP(EXC_FID_BC_DIAG, UCS_OP_RESULT), Exc_BC_Diag_Result },
+ { DEC_FKTOP(EXC_FID_BC_DIAG, UCS_OP_ERROR), Exc_BC_Diag_Error },
+ { DEC_FKTOP(EXC_FID_BC_ENABLE_TX, UCS_OP_RESULT), Exc_BC_EnableTx_Result },
+ { DEC_FKTOP(EXC_FID_BC_ENABLE_TX, UCS_OP_ERROR), Exc_BC_EnableTx_Error },
+ { DEC_FKTOP(EXC_FID_MEM_SESSION_OPEN, UCS_OP_RESULT), Exc_MemSessionOpen_Result },
+ { DEC_FKTOP(EXC_FID_MEM_SESSION_OPEN, UCS_OP_ERROR), Exc_MemSessionOpen_Error },
+ { DEC_FKTOP(EXC_FID_MEM_SESSION_CLOSE, UCS_OP_RESULT), Exc_MemSessionClose_Result },
+ { DEC_FKTOP(EXC_FID_MEM_SESSION_CLOSE, UCS_OP_ERROR), Exc_MemSessionClose_Error },
+ { DEC_FKTOP(EXC_FID_MEMORY_READ, UCS_OP_RESULT), Exc_MemoryRead_Result },
+ { DEC_FKTOP(EXC_FID_MEMORY_READ, UCS_OP_ERROR), Exc_MemoryRead_Error },
+ { DEC_FKTOP(EXC_FID_MEMORY_WRITE, UCS_OP_RESULT), Exc_MemoryWrite_Result },
+ { DEC_FKTOP(EXC_FID_MEMORY_WRITE, UCS_OP_ERROR), Exc_MemoryWrite_Error },
+ { DEC_FKTOP_TERMINATION, NULL }
+};
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of class CExc.
+ * \param self Reference to CExc instance
+ * \param base_ptr Reference to a Base instance
+ * \param rcm_ptr Reference to Transceiver instance
+ */
+void Exc_Ctor(CExc *self, CBase *base_ptr, CTransceiver *rcm_ptr)
+{
+
+ MISC_MEM_SET((void *)self, 0, sizeof(*self));
+
+ self->base_ptr = base_ptr;
+ self->xcvr_ptr = rcm_ptr;
+
+ self->fkt_op_list_ptr = &exc_handler[0];
+
+
+ /* Initialize API locking mechanism */
+ Sobs_Ctor(&self->lock.observer, self, &Exc_HandleApiTimeout);
+ Al_Ctor(&self->lock.api, &self->lock.observer, self->base_ptr->ucs_user_ptr);
+ Alm_RegisterApi(&self->base_ptr->alm, &self->lock.api);
+
+}
+
+
+/*! \brief Callback function to filter RCM Rx messages
+ * \details Do not release the message object here
+ * \param self reference to INIC object
+ * \param tel_ptr received message
+ */
+void Exc_OnRcmRxFilter(void *self, Msg_MostTel_t *tel_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_DecodeMsg(self_, tel_ptr);
+
+}
+
+
+/*! \brief Decode a message for FBlock EXC
+ * \param self Instance pointer to FBlock EXC
+ * \param msg_rx_ptr pointer to the MCM message to decode
+ */
+static void Exc_DecodeMsg(CExc *self, Msg_MostTel_t *msg_rx_ptr)
+{
+ Dec_Return_t result;
+ uint16_t index;
+
+ result = Dec_SearchFktOpIsh(self->fkt_op_list_ptr, &index, msg_rx_ptr->id.function_id, msg_rx_ptr->id.op_type);
+
+ if (result == DEC_RET_SUCCESS)
+ {
+ self->fkt_op_list_ptr[index].handler_function_ptr(self, msg_rx_ptr);
+ }
+ else
+ {
+ /* no handling of decoding error for shadow OpTypes */
+ }
+}
+
+
+
+/*! \brief Handles an API timeout
+ * \param self Instance pointer
+ * \param method_mask_ptr Bitmask to signal which API method has caused the timeout
+ */
+static void Exc_HandleApiTimeout(void *self, void *method_mask_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Alm_ModuleMask_t method_mask = *((Alm_ModuleMask_t *)method_mask_ptr);
+ Exc_StdResult_t res_data;
+
+ res_data.result.code = UCS_RES_ERR_TIMEOUT;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+ res_data.data_info = NULL;
+
+ switch(method_mask)
+ {
+#if 0 /* System Diagnosis supervises timeouts for these functions */
+ case EXC_API_ENABLE_PORT:
+ Ssub_Notify(&self_->ssubs.enableport, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_EnablePort_Sr().", 0U));
+ break;
+ case EXC_API_HELLO:
+ Ssub_Notify(&self_->ssubs.hello, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_Hello_Get().", 0U));
+ break;
+ case EXC_API_WELCOME:
+ Ssub_Notify(&self_->ssubs.welcome, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_Welcome_Sr().", 0U));
+ break;
+ case EXC_API_CABLE_LINK_DIAG:
+ Ssub_Notify(&self_->ssubs.cablelinkdiag, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_CableLinkDiagnosis_Start().", 0U));
+ break;
+#endif
+ case EXC_API_PHY_LAY_TEST_RESULT:
+ Ssub_Notify(&self_->ssubs.phylaytestresult, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_PhyTestResult_Get().", 0U));
+ break;
+ case EXC_API_MEM_SESSION_OPEN:
+ Ssub_Notify(&self_->ssubs.memsessionopen, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_MemSessionOpen_Sr().", 0U));
+ break;
+ case EXC_API_MEM_SESSION_CLOSE:
+ Ssub_Notify(&self_->ssubs.memsessionclose, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_MemSessionClose_Sr().", 0U));
+ break;
+ case EXC_API_MEM_READ:
+ Ssub_Notify(&self_->ssubs.memoryread, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_MemoryRead_Sr().", 0U));
+ break;
+ case EXC_API_MEM_WRITE:
+ Ssub_Notify(&self_->ssubs.memorywrite, &res_data, false);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "API locking timeout occurred for method Exc_MemoryWrite_Sr().", 0U));
+ break;
+
+ default:
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[EXC]", "Unknown API locking bitmask detected. Mask: 0x%02X", 1U, method_mask));
+ break;
+ }
+}
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal API */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief This method sends the Hello.Get message
+ * \param self Reference to CExc instance
+ * \param target_address Target address
+ * \param version_limit Signature version limit
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_Hello_Get(CExc *self,
+ uint16_t target_address,
+ uint8_t version_limit,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ if (version_limit > UCS_EXC_SIGNATURE_VERSION_LIMIT)
+ {
+ version_limit = UCS_EXC_SIGNATURE_VERSION_LIMIT;
+ }
+
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_HELLO;
+ msg_ptr->id.op_type = UCS_OP_GET;
+ msg_ptr->tel.tel_data_ptr[0] = version_limit;
+
+ msg_ptr->info_ptr = &self->ssubs.hello;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.hello, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief This method send the Welcome.StartResult message
+ * \param self Reference to CExc instance
+ * \param target_address Target address
+ * \param admin_node_address The node address used during system diagnosis
+ * \param version Signature version
+ * \param signature Signature of the device
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_Welcome_Sr(CExc *self,
+ uint16_t target_address,
+ uint16_t admin_node_address,
+ uint8_t version,
+ Ucs_Signature_t signature,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, EXC_SIGNATURE_LEN_V1 + 3U); /* Signature v1 */
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_WELCOME;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(admin_node_address);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(admin_node_address);
+ msg_ptr->tel.tel_data_ptr[2] = version;
+ msg_ptr->tel.tel_data_ptr[3] = MISC_HB(signature.node_address);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_LB(signature.node_address);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_HB(signature.group_address);
+ msg_ptr->tel.tel_data_ptr[6] = MISC_LB(signature.group_address);
+ msg_ptr->tel.tel_data_ptr[7] = MISC_HB(signature.mac_47_32);
+ msg_ptr->tel.tel_data_ptr[8] = MISC_LB(signature.mac_47_32);
+ msg_ptr->tel.tel_data_ptr[9] = MISC_HB(signature.mac_31_16);
+ msg_ptr->tel.tel_data_ptr[10] = MISC_LB(signature.mac_31_16);
+ msg_ptr->tel.tel_data_ptr[11] = MISC_HB(signature.mac_15_0);
+ msg_ptr->tel.tel_data_ptr[12] = MISC_LB(signature.mac_15_0);
+ msg_ptr->tel.tel_data_ptr[13] = MISC_HB(signature.node_pos_addr);
+ msg_ptr->tel.tel_data_ptr[14] = MISC_LB(signature.node_pos_addr);
+ msg_ptr->tel.tel_data_ptr[15] = MISC_HB(signature.diagnosis_id);
+ msg_ptr->tel.tel_data_ptr[16] = MISC_LB(signature.diagnosis_id);
+ msg_ptr->tel.tel_data_ptr[17] = signature.num_ports;
+ msg_ptr->tel.tel_data_ptr[18] = signature.chip_id;
+ msg_ptr->tel.tel_data_ptr[19] = signature.fw_major;
+ msg_ptr->tel.tel_data_ptr[20] = signature.fw_minor;
+ msg_ptr->tel.tel_data_ptr[21] = signature.fw_release;
+ msg_ptr->tel.tel_data_ptr[22] = MISC_HB((signature.fw_build) >>16U);
+ msg_ptr->tel.tel_data_ptr[23] = MISC_LB((signature.fw_build) >>16U);
+ msg_ptr->tel.tel_data_ptr[24] = MISC_HB(signature.fw_build);
+ msg_ptr->tel.tel_data_ptr[25] = MISC_LB(signature.fw_build);
+ msg_ptr->tel.tel_data_ptr[26] = signature.cs_major;
+ msg_ptr->tel.tel_data_ptr[27] = signature.cs_minor;
+ msg_ptr->tel.tel_data_ptr[28] = signature.cs_release;
+/* msg_ptr->tel.tel_data_ptr[29] = signature.uid_persistency;
+ msg_ptr->tel.tel_data_ptr[30] = MISC_HB((signature.uid) >>16U);
+ msg_ptr->tel.tel_data_ptr[31] = MISC_LB((signature.uid) >>16U);
+ msg_ptr->tel.tel_data_ptr[32] = MISC_HB(signature.uid);
+ msg_ptr->tel.tel_data_ptr[33] = MISC_LB(signature.uid);
+*/
+
+ msg_ptr->info_ptr = &self->ssubs.welcome;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.welcome, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief This method sends the Signature.Get message
+ * \param self Reference to CExc instance
+ * \param target_address Target address
+ * \param version_limit Signature version limit
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_Signature_Get(CExc *self,
+ uint16_t target_address,
+ uint8_t version_limit,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ if (version_limit > UCS_EXC_SIGNATURE_VERSION_LIMIT)
+ {
+ version_limit = UCS_EXC_SIGNATURE_VERSION_LIMIT;
+ }
+
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_SIGNATURE;
+ msg_ptr->id.op_type = UCS_OP_GET;
+ msg_ptr->tel.tel_data_ptr[0] = version_limit;
+
+ msg_ptr->info_ptr = &self->ssubs.signature;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.signature, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief This method sends the DeviceInit.Start message
+ * \param self Reference to CExc instance
+ * \param target_address Target address
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_DeviceInit_Start(CExc *self,
+ uint16_t target_address,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_DEVICE_INIT;
+ msg_ptr->id.op_type = UCS_OP_START;
+
+ msg_ptr->info_ptr = &self->ssubs.deviceinit;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.deviceinit, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief This method enables a port
+ * \param self Reference to CExc instance
+ * \param target_address Target address
+ * \param port_number PortNumber
+ * \param enabled Enabled
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_EnablePort_Sr(CExc *self,
+ uint16_t target_address,
+ uint8_t port_number,
+ bool enabled,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_ENABLEPORT;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = port_number;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)enabled;
+
+ msg_ptr->info_ptr = &self->ssubs.enableport;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.enableport, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief This method starts the Cable Link Diagnosis
+ * \param self Reference to CExc instance
+ * \param target_address Target address
+ * \param port_number PortNumber
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_CableLinkDiagnosis_Start(CExc *self,
+ uint16_t target_address,
+ uint8_t port_number,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_CABLE_LINK_DIAG;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = port_number;
+
+ msg_ptr->info_ptr = &self->ssubs.cablelinkdiag;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.cablelinkdiag, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief This method starts the Physical Layer Test
+ * \param self Reference to CExc instance
+ * \param port_number PortNumber
+ * \param type Type
+ * \param lead_in Lead-in
+ * \param duration Duration
+ * \param lead_out Lead-out
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_PhyTest_Start(CExc *self,
+ uint8_t port_number,
+ Ucs_Diag_PhyTest_Type_t type,
+ uint16_t lead_in,
+ uint32_t duration,
+ uint16_t lead_out,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 10U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = MSG_ADDR_INIC;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_PHY_LAY_TEST;
+ msg_ptr->id.op_type = UCS_OP_START;
+ msg_ptr->tel.tel_data_ptr[0] = port_number;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)type;
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(lead_in);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(lead_in);
+ msg_ptr->tel.tel_data_ptr[4] = (uint8_t)((duration) >> 24);
+ msg_ptr->tel.tel_data_ptr[5] = (uint8_t)((duration) >> 16);
+ msg_ptr->tel.tel_data_ptr[6] = (uint8_t)((duration) >> 8);
+ msg_ptr->tel.tel_data_ptr[7] = (uint8_t)(duration & (uint32_t)0xFF);
+ msg_ptr->tel.tel_data_ptr[8] = MISC_HB(lead_out);
+ msg_ptr->tel.tel_data_ptr[9] = MISC_LB(lead_out);
+
+
+ msg_ptr->info_ptr = &self->ssubs.phylaytest;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.phylaytest, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief Requests the EXC.PhysicalLayerTestResult.Status message
+ * \param self Reference to CExc instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Exc_PhyTestResult_Get(CExc *self,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, EXC_API_PHY_LAY_TEST_RESULT) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = MSG_ADDR_INIC;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_PHY_LAY_TEST_RES;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->info_ptr = &self->ssubs.phylaytestresult;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.phylaytestresult, obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, EXC_API_PHY_LAY_TEST_RESULT);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+
+/*! Sends the BCDiag.Startresult command
+ *
+ * \param *self Reference to CExc instance
+ * \param position Position of the segment to be checked.
+ * \param admin_na Admin Node Address
+ * \param t_send Timing parameter t_Send
+ * \param t_wait4dut Timing parameter t_WaitForDUT
+ * \param t_switch Timing parameter t_Switch
+ * \param t_back Timing parameter t_Back
+ * \param autoback TBD
+ * \param *obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_BCDiag_Start(CExc *self,
+ uint8_t position,
+ uint16_t admin_na,
+ uint16_t t_send,
+ uint16_t t_wait4dut,
+ uint16_t t_switch,
+ uint16_t t_back,
+ bool autoback,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 12U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = UCS_ADDR_BROADCAST_BLOCKING;
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_BC_DIAG;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = position;
+ msg_ptr->tel.tel_data_ptr[1] = MISC_HB(admin_na);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_LB(admin_na);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_HB(t_send);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_LB(t_send);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_HB(t_wait4dut);
+ msg_ptr->tel.tel_data_ptr[6] = MISC_LB(t_wait4dut);
+ msg_ptr->tel.tel_data_ptr[7] = MISC_HB(t_switch);
+ msg_ptr->tel.tel_data_ptr[8] = MISC_LB(t_switch);
+ msg_ptr->tel.tel_data_ptr[9] = MISC_HB(t_back);
+ msg_ptr->tel.tel_data_ptr[10] = MISC_LB(t_back);
+ msg_ptr->tel.tel_data_ptr[11] = (uint8_t)autoback;
+
+
+ msg_ptr->info_ptr = &self->ssubs.bcdiag;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.bcdiag, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! Enables the signal during backChannel Diagnosis
+ *
+ * \param *self Reference to CExc instance
+ * \param port Number of port which has to be enabled.
+ * \param *obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Exc_BCEnableTx_StartResult(CExc *self,
+ uint8_t port,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = UCS_ADDR_BROADCAST_BLOCKING;
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_BC_ENABLE_TX;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = port;
+
+ msg_ptr->info_ptr = &self->ssubs.enabletx;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.enabletx, obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief This function is used to open a memory session.
+ *
+ * A memory session is used to control access to the memory resources. Before a memory could
+ * be read or written, a session of the appropriate type has to be opened.
+ * Only a single memory session is supported. Once opened, the session must be first
+ * closed before a new session of a different type could be used. Some session types
+ * (0x01, 0x02 and 0x04) require a hardware reset after they were closed.
+ * Function Exc_MemSessionOpen_Sr() also performs some preprocessing,
+ * depending on the session_type. This includes clearing of the configuration
+ * and identification strings before the error memory is programmed or erased.
+ *
+ * \param *self Reference to CExc instance
+ * \param target_address Target address
+ * \param session_type Defines the set of MemIDs and the memory access type(s) (read and/or write)
+ * \param *obs_ptr Reference to an optional observer
+ *
+ * \return UCS_RET_SUCCESS message was created and sent to INIC
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Exc_MemSessionOpen_Sr(CExc *self,
+ uint16_t target_address,
+ uint8_t session_type,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, EXC_API_MEM_SESSION_OPEN) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_MEM_SESSION_OPEN;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = session_type;
+
+ msg_ptr->info_ptr = &self->ssubs.memsessionopen;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.memsessionopen, obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, EXC_API_MEM_SESSION_OPEN);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+/*! \brief This function is used to close an active memory session that was previously opened by
+ * function Exc_MemSessionOpen_Sr().
+ *
+ * In addition, the function performs some post-processing on given session types. This includes
+ * validation of the newly programmed configuration and identification strings as well as
+ * the deactivation of the current configuration and identification strings. In these cases,
+ * the new configuration becomes active after a hardware reset.
+ *
+ * \param *self Reference to CExc instance
+ * \param target_address Target address
+ * \param session_handle Unique number assigned to the active memory session
+ * \param *obs_ptr Reference to an optional observer
+ *
+ * \return UCS_RET_SUCCESS message was created and sent to INIC
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Exc_MemSessionClose_Sr(CExc *self,
+ uint16_t target_address,
+ uint16_t session_handle,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, EXC_API_MEM_SESSION_CLOSE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_MEM_SESSION_CLOSE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(session_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(session_handle);
+
+ msg_ptr->info_ptr = &self->ssubs.memsessionclose;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.memsessionclose, obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, EXC_API_MEM_SESSION_CLOSE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+/*! \brief This function provides read access to the memories described by parameter MemID.
+ *
+ * In addition, the function can be used to retrieve the active Configuration String and
+ * Identification String.
+ * Reading the memory can only be done within an active memory session. Parameter
+ * session_handle authorizes the access to the memory resource defined by parameter
+ * MemID. The session_handle is provided by function Exc_MemSessionOpen_Sr(),
+ * which must be called in advance to memory access.
+ *
+ * \param *self Reference to CExc instance
+ * \param target_address Target address
+ * \param session_handle Unique number assigned to the active memory session
+ * \param mem_id Represents the memory resource to be read
+ * \param address Defines the memory location at which the reading operation starts
+ * \param unit_len Sets the number of memory units to be read. Memory units can be
+ * unsigned bytes, unsigned words or unsigned masked data depending
+ * on the memory type.
+ * \param *obs_ptr Reference to an optional observer
+ *
+ * \return UCS_RET_SUCCESS message was created and sent to INIC
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_PARAM parameter ubit_len ist too big
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Exc_MemoryRead_Sr(CExc *self,
+ uint16_t target_address,
+ uint16_t session_handle,
+ uint8_t mem_id,
+ uint32_t address,
+ uint8_t unit_len,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, EXC_API_MEM_READ) != false)
+ {
+ if (unit_len > MAX_UNIT_LEN)
+ {
+ result = UCS_RET_ERR_PARAM;
+ }
+
+ if (result == UCS_RET_SUCCESS)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 8U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = target_address;
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_MEMORY_READ;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(session_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(session_handle);
+ msg_ptr->tel.tel_data_ptr[2] = mem_id;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)((address) >> 24);
+ msg_ptr->tel.tel_data_ptr[4] = (uint8_t)((address) >> 16);
+ msg_ptr->tel.tel_data_ptr[5] = (uint8_t)((address) >> 8);
+ msg_ptr->tel.tel_data_ptr[6] = (uint8_t)(address & (uint32_t)0xFF);
+ msg_ptr->tel.tel_data_ptr[7] = unit_len;
+
+ msg_ptr->info_ptr = &self->ssubs.memoryread;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.memoryread, obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, EXC_API_MEM_READ);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+/*! \brief This function provides write access to the memories described by parameter MemID.
+ *
+ * In addition, the function can be used to program a new Configuration String and Identification
+ * String.
+ * Writing the memory can only be done within an active memory session. Parameter
+ * SessionHandle authorizes the access to the memory resource defined by parameter
+ * MemID. The SessionHandle is provided by function ExtendedNetworkControl.MemorySessionOpen(),
+ * which must be called in advance to memory access.
+ *
+ * \param *self Reference to CExc instance
+ * \param target_address Target address
+ * \param session_handle Unique number assigned to the active memory session
+ * \param mem_id Represents the memory resource to be read
+ * \param address Defines the memory location at which the reading operation starts
+ * \param unit_len Sets the number of memory units to be read. Memory units can be
+ * unsigned bytes, unsigned words or unsigned masked data depending
+ * on the memory type.
+ * \param *unit_data Contains the actual data written to the memory resource and formatted
+ * as memory units
+ * \param *obs_ptr Reference to an optional observer
+ *
+ * \return UCS_RET_SUCCESS message was created and sent to INIC
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_PARAM parameter ubit_len ist too big
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Exc_MemoryWrite_Sr(CExc *self,
+ uint16_t target_address,
+ uint16_t session_handle,
+ uint8_t mem_id,
+ uint32_t address,
+ uint8_t unit_len,
+ uint8_t unit_data[],
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, EXC_API_MEM_WRITE) != false)
+ {
+ if (unit_len > MAX_UNIT_LEN)
+ {
+ result = UCS_RET_ERR_PARAM;
+ }
+
+ if (result == UCS_RET_SUCCESS)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 8U + unit_len);
+
+ if (msg_ptr != NULL)
+ {
+ uint8_t i;
+
+ msg_ptr->destination_addr = target_address;
+ msg_ptr->id.fblock_id = FB_EXC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = EXC_FID_MEMORY_WRITE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(session_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(session_handle);
+ msg_ptr->tel.tel_data_ptr[2] = mem_id;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)((address) >> 24);
+ msg_ptr->tel.tel_data_ptr[4] = (uint8_t)((address) >> 16);
+ msg_ptr->tel.tel_data_ptr[5] = (uint8_t)((address) >> 8);
+ msg_ptr->tel.tel_data_ptr[6] = (uint8_t)(address & (uint32_t)0xFF);
+ msg_ptr->tel.tel_data_ptr[7] = unit_len;
+ for (i=0U; i<unit_len; ++i)
+ {
+ msg_ptr->tel.tel_data_ptr[8U+i] = *(unit_data + i);
+ }
+
+ msg_ptr->info_ptr = &self->ssubs.memorywrite;
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs.memorywrite, obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, EXC_API_MEM_WRITE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Handler functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Handler function for EXC.Hello.Status
+ * \param self Reference to EXC object
+ * \param msg_ptr Received message
+ */
+static void Exc_Hello_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_HelloStatus_t hello_data;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len >= (EXC_SIGNATURE_LEN_V1 + 1U))
+ {
+ hello_data.version = msg_ptr->tel.tel_data_ptr[0];
+ Exc_Read_Signature(&(hello_data.signature), &(msg_ptr->tel.tel_data_ptr[1]));
+
+ res_data.data_info = &hello_data;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+
+ /* Node Discovery sends the Hello.Get as broadcast message. So we will need the observer
+ several times. */
+ Ssub_Notify(&self_->ssubs.hello, &res_data, false);
+ }
+}
+
+
+/*! \brief Handler function for EXC.Hello.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_Hello_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ /* Node Discovery sends the Hello.Get as broadcast message. So we will need the observer
+ several times. */
+ Ssub_Notify(&self_->ssubs.hello, &res_data, false);
+ }
+}
+
+
+/*! \brief Handler function for EXC.Welcome.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_Welcome_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.welcome, &res_data, true);
+ }
+}
+
+/*! \brief Handler function for the EXC.Welcome.Result message
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_Welcome_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_WelcomeResult_t welcome_data;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len >= (EXC_SIGNATURE_LEN_V1 + 2U))
+ {
+ welcome_data.res = msg_ptr->tel.tel_data_ptr[0];
+ welcome_data.version = msg_ptr->tel.tel_data_ptr[1];
+ Exc_Read_Signature(&(welcome_data.signature), &(msg_ptr->tel.tel_data_ptr[2]));
+ res_data.data_info = &welcome_data;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.welcome, &res_data, true);
+ }
+}
+
+
+/*! Handler function for the EXC.Signature.Status message
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_Signature_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_SignatureStatus_t signature_data;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len >= (EXC_SIGNATURE_LEN_V1 + 1U))
+ {
+ signature_data.version = msg_ptr->tel.tel_data_ptr[0];
+ Exc_Read_Signature(&(signature_data.signature), &(msg_ptr->tel.tel_data_ptr[1]));
+
+ res_data.data_info = &signature_data;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+
+ Ssub_Notify(&self_->ssubs.signature, &res_data, true);
+ }
+}
+
+
+/*! Handler function for the EXC.Signature.Error message
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_Signature_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.signature, &res_data, true);
+ }
+}
+
+
+/*! Handler function for the EXC.DeviceInit.Error message
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_DeviceInit_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len >0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.deviceinit, &res_data, true);
+ }
+}
+
+
+/*! \brief Handler function for EXC.EnablePort.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_EnablePort_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.enableport, &res_data, true);
+ }
+}
+
+/*! \brief Handler function for EXC.EnablePort.Result
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_EnablePort_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs.enableport, &res_data, true);
+}
+
+
+/*! \brief Handler function for EXC.CableLinkDiag.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_CableLinkDiag_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.cablelinkdiag, &res_data, true);
+ }
+}
+
+/*! \brief Handler function for EXC.CableLinkDiag.Result
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_CableLinkDiag_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_CableLinkDiagResult_t cable_link_diag_result_data;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ cable_link_diag_result_data.port_number = msg_ptr->tel.tel_data_ptr[0];
+ cable_link_diag_result_data.result = msg_ptr->tel.tel_data_ptr[1];
+ res_data.data_info = &cable_link_diag_result_data;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.cablelinkdiag, &res_data, true);
+ }
+}
+
+
+/*! \brief Handler function for EXC.PhysicalLayerTest.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */static void Exc_NwPhyTest_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.phylaytest, &res_data, true);
+ }
+}
+
+
+/*! \brief Handler function for EXC.MOSTNetworkPhysicalLayerTestResult.Status
+ * \param self Reference to EXC object
+ * \param msg_ptr Received message
+ */
+static void Exc_NwPhyTestResult_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_PhyTestResult_t phy_test_result;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ phy_test_result.port_number = msg_ptr->tel.tel_data_ptr[0];
+ phy_test_result.lock_status = (msg_ptr->tel.tel_data_ptr[1] != 0U) ? true : false;
+ MISC_DECODE_WORD(&(phy_test_result.err_count), &(msg_ptr->tel.tel_data_ptr[2]));
+ res_data.data_info = &phy_test_result;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.phylaytestresult, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_PHY_LAY_TEST_RESULT);
+}
+
+
+/*! \brief Handler function for EXC.MOSTNetworkPhysicalLayerTestResult.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_NwPhyTestResult_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.phylaytestresult, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_PHY_LAY_TEST_RESULT);
+}
+
+
+
+/*! \brief Handler function for EXC.BCDiag.Status
+ * \param self Reference to EXC object
+ * \param msg_ptr Received message
+ */
+static void Exc_BC_Diag_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_BCDiagResult bcd_result;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 1U)
+ {
+ bcd_result.diag_result = (Exc_BCDiagResValue)(msg_ptr->tel.tel_data_ptr[0] >> 4U);
+ MISC_DECODE_WORD(&(bcd_result.admin_addr), &(msg_ptr->tel.tel_data_ptr[0]));
+ bcd_result.admin_addr &= 0x0FFFU;
+ res_data.data_info = &bcd_result;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.bcdiag, &res_data, true);
+ }
+}
+
+
+/*! \brief Handler function for EXC.BCDiag.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_BC_Diag_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.bcdiag, &res_data, true);
+ }
+}
+
+
+
+
+/*! \brief Handler function for EXC.BCEnableTx.Result
+ * \param self Reference to EXC object
+ * \param msg_ptr Received message
+ */
+static void Exc_BC_EnableTx_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.enabletx, &res_data, true);
+
+ MISC_UNUSED(msg_ptr);
+}
+
+
+/*! \brief Handler function for EXC.BCEnableTx.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_BC_EnableTx_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.enabletx, &res_data, true);
+ }
+}
+
+
+/*! \brief Handler function for EXC.MemorySessionOpen.Result
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemSessionOpen_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ uint16_t session_handle;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ MISC_DECODE_WORD(&(session_handle), &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &session_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.memsessionopen, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_SESSION_OPEN);
+}
+
+
+/*! \brief Handler function for EXC.MemorySessionOpen.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemSessionOpen_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.memsessionopen, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_SESSION_OPEN);
+}
+
+
+/*! \brief Handler function for EXC.MemorySessionClose.Result
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemSessionClose_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ uint8_t session_result;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ session_result = msg_ptr->tel.tel_data_ptr[0];
+ res_data.data_info = &session_result;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.memsessionclose, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_SESSION_CLOSE);
+}
+
+/*! \brief Handler function for EXC.MemorySessionClose.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemSessionClose_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.memsessionclose, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_SESSION_CLOSE);
+}
+
+/*! \brief Handler function for EXC.MemoryRead.Result
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemoryRead_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_MemReadResult_t mem_read_result;
+ Exc_StdResult_t res_data;
+ uint8_t i;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ MISC_DECODE_WORD(&(mem_read_result.session_handle), &(msg_ptr->tel.tel_data_ptr[0]));
+ mem_read_result.mem_id = msg_ptr->tel.tel_data_ptr[2];
+ MISC_DECODE_DWORD(&(mem_read_result.address), &(msg_ptr->tel.tel_data_ptr[3]));
+ mem_read_result.unit_len = msg_ptr->tel.tel_data_ptr[7];
+ for (i=0U; (i<mem_read_result.unit_len) && (i<MAX_UNIT_LEN); ++i)
+ {
+ mem_read_result.unit_data[i] = msg_ptr->tel.tel_data_ptr[8U+i];
+ }
+
+ res_data.data_info = &mem_read_result;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.memoryread, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_READ);
+}
+
+
+/*! \brief Handler function for EXC.MemoryRead.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemoryRead_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.memoryread, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_READ);
+}
+
+
+/*! \brief Handler function for EXC.MemoryWrite.Result
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemoryWrite_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_MemWriteResult_t mem_write_result;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ MISC_DECODE_WORD(&(mem_write_result.session_handle), &(msg_ptr->tel.tel_data_ptr[0]));
+ mem_write_result.mem_id = msg_ptr->tel.tel_data_ptr[2];
+
+ res_data.data_info = &mem_write_result;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs.memorywrite, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_WRITE);
+}
+
+
+/*! \brief Handler function for EXC.MemoryWrite.Error
+ * \param self reference to EXC object
+ * \param msg_ptr received message
+ */
+static void Exc_MemoryWrite_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CExc *self_ = (CExc *)self;
+ Exc_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Exc_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (uint8_t)(msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs.memorywrite, &res_data, true);
+ }
+ Al_Release(&self_->lock.api, EXC_API_MEM_WRITE);
+}
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Helper functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Translates EXC error codes into UNICENS error codes and wraps the raw INIC
+ * error data to a byte stream.
+ * \param self Instance of CExc
+ * \param error_data[] EXC error data
+ * \param error_size Size of EXC error data in bytes
+ * \return The formatted error
+ */
+static Ucs_StdResult_t Exc_TranslateError(CExc *self, uint8_t error_data[], uint8_t error_size)
+{
+ Ucs_StdResult_t ret_val;
+ MISC_UNUSED(self);
+
+ if(error_data[0] != 0x20U)
+ {
+ ret_val.code = UCS_RES_ERR_MOST_STANDARD;
+ }
+ else
+ {
+ ret_val.code = (Ucs_Result_t)(error_data[1] + 1U);
+ }
+
+ ret_val.info_ptr = &error_data[0];
+ ret_val.info_size = error_size;
+
+ return ret_val;
+}
+
+
+/*! \brief Reads a signature from a message's payload
+ *
+ * \param dest Pointer to signature
+ * \param source Pointer to start of signature inabyte array
+ */
+static void Exc_Read_Signature(Ucs_Signature_t *dest, uint8_t source[])
+{
+ MISC_DECODE_WORD(&(dest->node_address), source);
+ MISC_DECODE_WORD(&(dest->group_address), &(source[2]));
+ MISC_DECODE_WORD(&(dest->mac_47_32), &(source[4]));
+ MISC_DECODE_WORD(&(dest->mac_31_16), &(source[6]));
+ MISC_DECODE_WORD(&(dest->mac_15_0), &(source[8]));
+ MISC_DECODE_WORD(&(dest->node_pos_addr), &(source[10]));
+ MISC_DECODE_WORD(&(dest->diagnosis_id), &(source[12]));
+ dest->num_ports = source[14];
+ dest->chip_id = source[15];
+ dest->fw_major = source[16];
+ dest->fw_minor = source[17];
+ dest->fw_release = source[18];
+ MISC_DECODE_DWORD(&(dest->fw_build), &(source[19]));
+ dest->cs_major = source[23];
+ dest->cs_minor = source[24];
+ dest->cs_release = source[25];
+/* dest->uid_persistency = source[26];*/ /* Signature v1 */
+/* MISC_DECODE_DWORD(&(dest->uid), &(source[27]));*/
+
+}
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_factory.c b/ucs2-lib/src/ucs_factory.c
new file mode 100644
index 0000000..978e6d9
--- /dev/null
+++ b/ucs2-lib/src/ucs_factory.c
@@ -0,0 +1,830 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the MNS Factory.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_FAC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_factory.h"
+#include "ucs_xrm_pv.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal macros */
+/*------------------------------------------------------------------------------------------------*/
+#define IS_VALID_ADDR(addr) ((UCS_ADDR_LOCAL_DEV == (addr)) || ((0x0FU < (addr)) && (0x300U > (addr))) || ((0x04FFU < (addr)) && (0x0FF0U > (addr)))) /* parasoft-suppress MISRA2004-19_7 "common definition of type cast improves code" */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Fac_ConstructFbi (CFactory * self, CInic * fbi, uint16_t address);
+static CInic * Fac_SearchFbi(CFactory * self, uint16_t address);
+static void Fac_ConstructNsm (CFactory * self, CNodeScriptManagement * nsm, uint16_t address);
+static CRemoteSyncManagement * Fac_SearchRsm(CFactory * self, uint16_t address);
+static CExtendedResourceManager * Fac_SearchXrm(CFactory * self, uint16_t address);
+static CGpio * Fac_SearchGpio(CFactory * self, uint16_t address);
+static CI2c* Fac_SearchI2c(CFactory * self, uint16_t address);
+static CNodeScriptManagement * Fac_SearchNsm(CFactory * self, uint16_t address);
+static CInic * Fac_GetUninitializedFbi (CFactory * self);
+static CNodeScriptManagement * Fac_GetUninitializedNsm (CFactory * self);
+static CRemoteSyncManagement * Fac_GetUninitializedRsm (CFactory * self);
+static CExtendedResourceManager * Fac_GetUninitializedXrm (CFactory * self);
+static CGpio * Fac_GetUninitializedGpio (CFactory * self);
+static CI2c * Fac_GetUninitializedI2c (CFactory * self);
+static bool Fac_IsFbiUninitialized(CInic * fbi);
+static bool Fac_IsRsmUninitialized(CRemoteSyncManagement * rsm);
+static bool Fac_IsXrmUninitialized(CExtendedResourceManager * xrm);
+static bool Fac_IsGpioUninitialized(CGpio * gpio);
+static bool Fac_IsI2cUninitialized(CI2c * i2c);
+static bool Fac_IsNsmUninitialized(CNodeScriptManagement * nsm);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CFactory */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the MNS Factory class.
+ * \param self Instance pointer
+ * \param init_ptr init data_ptr
+ */
+void Fac_Ctor(CFactory * self, Fac_InitData_t * init_ptr)
+{
+ uint8_t i;
+ Rsm_InitData_t rsm_init_data;
+
+ MISC_MEM_SET(self, 0, sizeof(CFactory));
+
+ /* set base and net instances */
+ self->base_ptr = init_ptr->base_ptr;
+ self->net_ptr = init_ptr->net_ptr;
+ self->xrmp_ptr = init_ptr->xrmp_ptr;
+ self->icm_transceiver = init_ptr->icm_transceiver;
+ self->rcm_transceiver = init_ptr->rcm_transceiver;
+
+ rsm_init_data.base_ptr = self->base_ptr;
+ rsm_init_data.net_ptr = self->net_ptr;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ rsm_init_data.inic_ptr = &self->fbi_list[i];
+ Rsm_Ctor(&self->rsm_list[i], &rsm_init_data);
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Returns the XRM instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \param res_debugging_fptr The resources debugging callback function
+ * \param check_unmute_fptr The check unmute callback function
+ * \return a reference to a XRM instance or \c NULL if no appropriate instance has been found.
+ */
+CExtendedResourceManager * Fac_GetXrm(CFactory * self, uint16_t address, Ucs_Xrm_ResourceDebugCb_t res_debugging_fptr, Ucs_Xrm_CheckUnmuteCb_t check_unmute_fptr)
+{
+ CRemoteSyncManagement * rsm_inst = NULL;
+ CExtendedResourceManager * xrm_inst = NULL;
+
+ if (IS_VALID_ADDR(address))
+ {
+ xrm_inst = Fac_SearchXrm(self, address);
+ if (xrm_inst == NULL)
+ {
+ rsm_inst = Fac_GetRsm(self, address);
+ if (rsm_inst != NULL)
+ {
+ Xrm_InitData_t xrm_init_data;
+ xrm_inst = Fac_GetUninitializedXrm(self);
+ if (xrm_inst != NULL)
+ {
+ xrm_init_data.base_ptr = self->base_ptr;
+ xrm_init_data.net_ptr = self->net_ptr;
+ xrm_init_data.rsm_ptr = rsm_inst;
+ xrm_init_data.inic_ptr = rsm_inst->inic_ptr;
+ xrm_init_data.xrmp_ptr = self->xrmp_ptr;
+ xrm_init_data.check_unmute_fptr = check_unmute_fptr;
+ xrm_init_data.res_debugging_fptr = res_debugging_fptr;
+ Xrm_Ctor(xrm_inst, &xrm_init_data);
+ }
+ }
+ }
+ Xrm_SetResourceDebugCbFn(xrm_inst, res_debugging_fptr);
+ }
+
+ return xrm_inst;
+}
+
+/*! \brief Returns the XRM instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \param check_unmute_fptr The check unmute callback function
+ * \return a reference to a XRM instance or \c NULL if no appropriate instance has been found.
+ */
+CExtendedResourceManager * Fac_GetXrmLegacy(CFactory * self, uint16_t address, Ucs_Xrm_CheckUnmuteCb_t check_unmute_fptr)
+{
+ return Fac_GetXrm(self, address, NULL, check_unmute_fptr);
+}
+
+
+/*! \brief Returns the XRM instance associated with the resource list.
+ * \note <b>This function should only be used in case of Ucs_Xrm_Destroy() since it's certain in that case that the XRM instance for the given job list already exists!</b>
+ * \param self Instance pointer
+ * \param resource_object_list Reference to the job list
+ * \return a reference to a XRM instance or \c NULL if no appropriate instance has been found.
+ */
+CExtendedResourceManager * Fac_GetXrmByJobList(CFactory * self, UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_list[])
+{
+ uint8_t i;
+ CExtendedResourceManager * ret_xrm = NULL;
+
+ for(i=0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (Xrm_IsInMyJobList(&self->xrm_list[i], resource_object_list))
+ {
+ ret_xrm = &self->xrm_list[i];
+ break;
+ }
+ }
+
+ return ret_xrm;
+}
+
+/*! \brief Returns the FBlock INIC instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \return a reference to a FBI instance or \c NULL if no suitable instance has been found.
+ */
+CInic * Fac_GetInic(CFactory * self, uint16_t address)
+{
+ CInic * fbi_inst = NULL;
+
+ if (IS_VALID_ADDR(address))
+ {
+ fbi_inst = Fac_SearchFbi(self, address);
+ if (fbi_inst == NULL)
+ {
+ fbi_inst = Fac_GetUninitializedFbi(self);
+ if (fbi_inst != NULL)
+ {
+ Fac_ConstructFbi(self, fbi_inst, address);
+ }
+ }
+ }
+
+ return fbi_inst;
+}
+
+/*! \brief Returns the CNodeScriptManagement instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \return a reference to a FBI instance or \c NULL if no suitable instance has been found.
+ */
+CNodeScriptManagement * Fac_GetNsm(CFactory * self, uint16_t address)
+{
+ CNodeScriptManagement * nsm_inst = NULL;
+
+ if (IS_VALID_ADDR(address))
+ {
+ nsm_inst = Fac_SearchNsm(self, address);
+ if (nsm_inst == NULL)
+ {
+ nsm_inst = Fac_GetUninitializedNsm(self);
+ if (nsm_inst != NULL)
+ {
+ Fac_ConstructNsm(self, nsm_inst, address);
+ }
+ }
+ }
+
+ return nsm_inst;
+}
+
+/*! \brief Returns the RSM instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \return a reference to a RSM instance or \c NULL if no suitable instance has been found.
+ */
+CRemoteSyncManagement * Fac_GetRsm(CFactory * self, uint16_t address)
+{
+ CRemoteSyncManagement * rsm_inst = NULL;
+
+ if (IS_VALID_ADDR(address))
+ {
+ rsm_inst = Fac_SearchRsm(self, address);
+ if (rsm_inst == NULL)
+ {
+ rsm_inst = Fac_GetUninitializedRsm(self);
+ if (rsm_inst != NULL)
+ {
+ Fac_ConstructFbi(self, rsm_inst->inic_ptr, address);
+ }
+ }
+ }
+
+ return rsm_inst;
+}
+
+/*! \brief Returns the GPIO instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \param trigger_event_status_fptr User GPIO trigger event status callback function pointer.
+ * \return a reference to a GPIO instance or \c NULL if no suitable instance has been found.
+ */
+CGpio * Fac_GetGpio(CFactory * self, uint16_t address, Ucs_Gpio_TriggerEventResultCb_t trigger_event_status_fptr)
+{
+ CGpio * gpio_inst = NULL;
+ CNodeScriptManagement * nsm_inst = NULL;
+
+ if (IS_VALID_ADDR(address))
+ {
+ gpio_inst = Fac_SearchGpio(self, address);
+ if (NULL == gpio_inst)
+ {
+ nsm_inst = Fac_GetNsm(self, address);
+ if (NULL != nsm_inst)
+ {
+ Gpio_InitData_t gpio_init_data;
+ gpio_inst = Fac_GetUninitializedGpio(self);
+ if (NULL != gpio_inst)
+ {
+ gpio_init_data.nsm_ptr = nsm_inst;
+ gpio_init_data.inic_ptr = nsm_inst->rsm_ptr->inic_ptr;
+ gpio_init_data.trigger_event_status_fptr = trigger_event_status_fptr;
+ Gpio_Ctor(gpio_inst, &gpio_init_data);
+ }
+ }
+ }
+ }
+
+ return gpio_inst;
+}
+
+/*! \brief Returns the I2C instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address of the device associated with the instance
+ * \param i2c_interrupt_report_fptr User GPIO trigger event status callback function pointer.
+ * \return a reference to an I2C instance or \c NULL if no suitable instance has been found.
+ */
+CI2c * Fac_GetI2c(CFactory * self, uint16_t address, Ucs_I2c_IntEventReportCb_t i2c_interrupt_report_fptr)
+{
+ CI2c * i2c_inst = NULL;
+ CNodeScriptManagement * nsm_inst = NULL;
+
+ if (IS_VALID_ADDR(address))
+ {
+ i2c_inst = Fac_SearchI2c (self, address);
+ if (NULL == i2c_inst)
+ {
+ nsm_inst = Fac_GetNsm(self, address);
+ if (nsm_inst != NULL)
+ {
+ I2c_InitData_t i2c_init_data;
+ i2c_inst = Fac_GetUninitializedI2c(self);
+ if (NULL != i2c_inst)
+ {
+ i2c_init_data.nsm_ptr = nsm_inst;
+ i2c_init_data.inic_ptr = nsm_inst->rsm_ptr->inic_ptr;
+ i2c_init_data.i2c_interrupt_report_fptr = i2c_interrupt_report_fptr;
+ I2c_Ctor(i2c_inst, &i2c_init_data);
+ }
+ }
+ }
+ }
+
+ return i2c_inst;
+}
+
+/*! \brief Searches for the INIC instance associated with the given address and returns It if found.
+ * \param self Instance pointer
+ * \param address Address of the device associated with this instance
+ * \return a reference to the found instance otherwise \c NULL.
+ */
+CInic * Fac_FindInic(CFactory * self, uint16_t address)
+{
+ return Fac_SearchFbi (self, address);
+}
+
+/*! \brief Searches for the NSM instance associated with the given address and returns It if found.
+ * \param self Instance pointer
+ * \param address Address of the device associated with this instance
+ * \return a reference to the found instance otherwise \c NULL.
+ */
+CNodeScriptManagement * Fac_FindNsm(CFactory * self, uint16_t address)
+{
+ return Fac_SearchNsm (self, address);
+}
+
+/*! \brief Searches for the RSM instance associated with the given address and returns It if found.
+ * \param self Instance pointer
+ * \param address Address of the device associated with this instance
+ * \return a reference to the found instance otherwise \c NULL.
+ */
+CRemoteSyncManagement * Fac_FindRsm(CFactory * self, uint16_t address)
+{
+ return Fac_SearchRsm (self, address);
+}
+
+/*! \brief Calls the given function for each instance of inst_type type. If the func_ptr
+ * returns true the loop is stopped.
+ * \param self Reference to a Factory Instance
+ * \param inst_type The instance type to be looked for
+ * \param func_ptr Reference of the callback function which is called for each node
+ * \param user_data_ptr Reference of optional user data pass to the func_ptr
+ */
+void Fac_Foreach(CFactory * self, Fac_Inst_t inst_type, Fac_ForeachFunc_t func_ptr, void *user_data_ptr)
+{
+ uint8_t j;
+ void * curr_inst = NULL;
+ bool exit_loop = false;
+
+ for(j=0U; j<FAC_NUM_DEVICES; j++)
+ {
+ switch(inst_type)
+ {
+ case FAC_INST_INIC:
+ curr_inst = &self->fbi_list[j];
+ if (Fac_IsFbiUninitialized((CInic *)curr_inst))
+ {
+ curr_inst = NULL;
+ }
+ break;
+
+ case FAC_INST_RSM:
+ curr_inst = &self->rsm_list[j];
+ if (Fac_IsRsmUninitialized((CRemoteSyncManagement *)curr_inst))
+ {
+ curr_inst = NULL;
+ }
+ break;
+
+ case FAC_INST_XRM:
+ curr_inst = &self->xrm_list[j];
+ if (Fac_IsXrmUninitialized((CExtendedResourceManager *)curr_inst))
+ {
+ curr_inst = NULL;
+ }
+ break;
+
+ case FAC_INST_GPIO:
+ curr_inst = &self->gpio_list[j];
+ if (Fac_IsGpioUninitialized((CGpio *)curr_inst))
+ {
+ curr_inst = NULL;
+ }
+ break;
+
+ case FAC_INST_I2C:
+ curr_inst = &self->i2c_list[j];
+ if (Fac_IsI2cUninitialized((CI2c *)curr_inst))
+ {
+ curr_inst = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (curr_inst != NULL)
+ {
+ if (func_ptr(inst_type, curr_inst, user_data_ptr) != false)
+ {
+ exit_loop = true;
+ }
+ }
+ else
+ {
+ exit_loop = true;
+ }
+
+ if (exit_loop)
+ {
+ break;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Search for the FBI instance associated with the given address and return It.
+ * \param self Instance pointer
+ * \param address Address to be looked for
+ * \return a reference to the found FBI or \c NULL if no suitable instance has been found.
+ */
+static CInic * Fac_SearchFbi(CFactory * self, uint16_t address)
+{
+ CInic * found_fbi = NULL;
+ uint8_t i;
+ uint16_t tmp_addr = address;
+
+ if ((tmp_addr != UCS_ADDR_LOCAL_DEV) && (Net_IsOwnAddress(self->net_ptr, tmp_addr) == NET_IS_OWN_ADDR_NODE))
+ {
+ tmp_addr = UCS_ADDR_LOCAL_DEV;
+ }
+
+ for (i = 0U; (i<FAC_NUM_DEVICES) && (!Fac_IsFbiUninitialized(&self->fbi_list[i])); i++)
+ {
+ if (tmp_addr == Inic_GetTargetAddress(&self->fbi_list[i]))
+ {
+ found_fbi = &self->fbi_list[i];
+ break;
+ }
+ }
+
+ return found_fbi;
+}
+
+/*! \brief Search for the NSM instance associated with the given address and return It.
+ * \param self Instance pointer
+ * \param address Address to be looked for
+ * \return a reference to the found NSM or \c NULL if no suitable instance has been found.
+ */
+static CNodeScriptManagement * Fac_SearchNsm(CFactory * self, uint16_t address)
+{
+ CNodeScriptManagement * found_nsm = NULL;
+ uint8_t i;
+ uint16_t tmp_addr = address;
+
+ if ((tmp_addr != UCS_ADDR_LOCAL_DEV) && (Net_IsOwnAddress(self->net_ptr, tmp_addr) == NET_IS_OWN_ADDR_NODE))
+ {
+ tmp_addr = UCS_ADDR_LOCAL_DEV;
+ }
+
+ for (i = 0U; (i<FAC_NUM_DEVICES) && (!Fac_IsNsmUninitialized(&self->nsm_list[i])); i++)
+ {
+ if (tmp_addr == self->nsm_list[i].target_address)
+ {
+ found_nsm = &self->nsm_list[i];
+ break;
+ }
+ }
+
+ return found_nsm;
+}
+
+/*! \brief Search for the RSM instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address to be looked for
+ * \return a reference to the found RSM or \c NULL if no suitable instance has been found.
+ */
+static CRemoteSyncManagement * Fac_SearchRsm(CFactory * self, uint16_t address)
+{
+ CRemoteSyncManagement * found_rsm = NULL;
+ uint8_t i;
+ uint16_t tmp_addr = address;
+
+ if ((tmp_addr != UCS_ADDR_LOCAL_DEV) && (Net_IsOwnAddress(self->net_ptr, tmp_addr) == NET_IS_OWN_ADDR_NODE))
+ {
+ tmp_addr = UCS_ADDR_LOCAL_DEV;
+ }
+
+ for (i = 0U; (i<FAC_NUM_DEVICES) && (!Fac_IsFbiUninitialized(self->rsm_list[i].inic_ptr)); i++)
+ {
+ if (tmp_addr == Inic_GetTargetAddress(self->rsm_list[i].inic_ptr))
+ {
+ found_rsm = &self->rsm_list[i];
+ break;
+ }
+ }
+
+ return found_rsm;
+}
+
+/*! \brief Search for the XRM instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address to be looked for
+ * \return a reference to the found XRM or \c NULL if no suitable instance has been found.
+ */
+static CExtendedResourceManager * Fac_SearchXrm(CFactory * self, uint16_t address)
+{
+ CExtendedResourceManager * found_xrm = NULL;
+ uint8_t i;
+ uint16_t tmp_addr = address;
+
+ if ((tmp_addr != UCS_ADDR_LOCAL_DEV) && (Net_IsOwnAddress(self->net_ptr, tmp_addr) == NET_IS_OWN_ADDR_NODE))
+ {
+ tmp_addr = UCS_ADDR_LOCAL_DEV;
+ }
+
+ for (i = 0U; (i<FAC_NUM_DEVICES) && (!Fac_IsXrmUninitialized(&self->xrm_list[i])); i++)
+ {
+ if (tmp_addr == Inic_GetTargetAddress(self->xrm_list[i].rsm_ptr->inic_ptr))
+ {
+ found_xrm = &self->xrm_list[i];
+ break;
+ }
+ }
+
+ return found_xrm;
+}
+
+/*! \brief Search for the Gpio instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address to be looked for
+ * \return a reference to the found GPIO or \c NULL if no suitable instance has been found.
+ */
+static CGpio * Fac_SearchGpio(CFactory * self, uint16_t address)
+{
+ CGpio * found_gpio = NULL;
+ uint8_t i;
+ uint16_t tmp_addr = address;
+
+ if ((tmp_addr != UCS_ADDR_LOCAL_DEV) && (Net_IsOwnAddress(self->net_ptr, tmp_addr) == NET_IS_OWN_ADDR_NODE))
+ {
+ tmp_addr = UCS_ADDR_LOCAL_DEV;
+ }
+
+ for (i = 0U; (i<FAC_NUM_DEVICES) && (!Fac_IsGpioUninitialized(&self->gpio_list[i])); i++)
+ {
+ if (tmp_addr == Inic_GetTargetAddress(self->gpio_list[i].nsm_ptr->rsm_ptr->inic_ptr))
+ {
+ found_gpio = &self->gpio_list[i];
+ break;
+ }
+ }
+
+ return found_gpio;
+}
+
+/*! \brief Search for the I2c instance associated with the given address.
+ * \param self Instance pointer
+ * \param address Address to be looked for
+ * \return a reference to the found GPIO or \c NULL if no suitable instance has been found.
+ */
+static CI2c * Fac_SearchI2c(CFactory * self, uint16_t address)
+{
+ CI2c * found_i2c = NULL;
+ uint8_t i;
+ uint16_t tmp_addr = address;
+
+ if ((tmp_addr != UCS_ADDR_LOCAL_DEV) && (Net_IsOwnAddress(self->net_ptr, tmp_addr) == NET_IS_OWN_ADDR_NODE))
+ {
+ tmp_addr = UCS_ADDR_LOCAL_DEV;
+ }
+
+ for (i = 0U; (i<FAC_NUM_DEVICES) && (!Fac_IsI2cUninitialized(&self->i2c_list[i])); i++)
+ {
+ if (tmp_addr == Inic_GetTargetAddress(self->i2c_list[i].nsm_ptr->rsm_ptr->inic_ptr))
+ {
+ found_i2c = &self->i2c_list[i];
+ break;
+ }
+ }
+
+ return found_i2c;
+}
+
+/*! \brief Returns the next free uninitialized XRM instance
+ * \param self Instance pointer
+ * \return a reference to the next free uninitialized XRM instance if found, otherwise \c NULL.
+ */
+static CExtendedResourceManager * Fac_GetUninitializedXrm (CFactory * self)
+{
+ CExtendedResourceManager * tmp_xrm = NULL;
+ uint8_t i;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (self->xrm_list[i].rsm_ptr == NULL)
+ {
+ tmp_xrm = &self->xrm_list[i];
+ break;
+ }
+ }
+
+ return tmp_xrm;
+}
+
+/*! \brief Returns the next free uninitialized FBI instance
+ * \param self Instance pointer
+ * \return a reference to the next free uninitialized FBI instance if found, otherwise \c NULL.
+ */
+static CInic * Fac_GetUninitializedFbi (CFactory * self)
+{
+ CInic * tmp_inic = NULL;
+ uint8_t i;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (self->fbi_list[i].base_ptr == NULL)
+ {
+ tmp_inic = &self->fbi_list[i];
+ break;
+ }
+ }
+
+ return tmp_inic;
+}
+
+/*! \brief Returns the next free uninitialized NSM instance
+ * \param self Instance pointer
+ * \return a reference to the next free uninitialized NSM instance if found, otherwise \c NULL.
+ */
+static CNodeScriptManagement * Fac_GetUninitializedNsm (CFactory * self)
+{
+ CNodeScriptManagement * tmp_nsm = NULL;
+ uint8_t i;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (self->nsm_list[i].base_ptr == NULL)
+ {
+ tmp_nsm = &self->nsm_list[i];
+ break;
+ }
+ }
+
+ return tmp_nsm;
+}
+
+/*! \brief Returns the next free uninitialized RSM instance
+ * \param self Instance pointer
+ * \return a reference to the next free uninitialized RSM instance if found, otherwise \c NULL.
+ */
+static CRemoteSyncManagement * Fac_GetUninitializedRsm (CFactory * self)
+{
+ CRemoteSyncManagement * tmp_rsm = NULL;
+ uint8_t i;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (Inic_GetTargetAddress(self->rsm_list[i].inic_ptr) == 0x0U)
+ {
+ tmp_rsm = &self->rsm_list[i];
+ break;
+ }
+ }
+
+ return tmp_rsm;
+}
+
+/*! \brief Returns the next free uninitialized GPIO instance
+ * \param self Instance pointer
+ * \return a reference to the next free uninitialized GPIO instance if found, otherwise \c NULL.
+ */
+static CGpio * Fac_GetUninitializedGpio (CFactory * self)
+{
+ CGpio * tmp_gpio = NULL;
+ uint8_t i;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (NULL == self->gpio_list[i].nsm_ptr)
+ {
+ tmp_gpio = &self->gpio_list[i];
+ break;
+ }
+ }
+
+ return tmp_gpio;
+}
+
+/*! \brief Returns the next free uninitialized I2C instance
+ * \param self Instance pointer
+ * \return a reference to the next free uninitialized I2C instance if found, otherwise \c NULL.
+ */
+static CI2c * Fac_GetUninitializedI2c (CFactory * self)
+{
+ CI2c * tmp_i2c = NULL;
+ uint8_t i;
+
+ for (i = 0U; i<FAC_NUM_DEVICES; i++)
+ {
+ if (NULL == self->i2c_list[i].nsm_ptr)
+ {
+ tmp_i2c = &self->i2c_list[i];
+ break;
+ }
+ }
+
+ return tmp_i2c;
+}
+
+/*! \brief Constructs the given FBI instance
+ * \param self the MNS factory Instance pointer
+ * \param fbi the INIC Instance pointer
+ * \param address the device address of this FBlock INIC
+ */
+static void Fac_ConstructFbi (CFactory * self, CInic * fbi, uint16_t address)
+{
+ Inic_InitData_t inic_init_data;
+
+ if (address == UCS_ADDR_LOCAL_DEV)
+ {
+ inic_init_data.xcvr_ptr = self->icm_transceiver;
+ }
+ else
+ {
+ inic_init_data.xcvr_ptr = self->rcm_transceiver;
+ }
+
+ inic_init_data.base_ptr = self->base_ptr;
+ inic_init_data.tgt_addr = address;
+
+ Inic_Ctor(fbi, &inic_init_data);
+}
+
+/*! \brief Constructs the given NSM instance
+ * \param self the MNS factory Instance pointer
+ * \param nsm the NSM Instance pointer
+ * \param address the device address
+ */
+static void Fac_ConstructNsm (CFactory * self, CNodeScriptManagement * nsm, uint16_t address)
+{
+ Nsm_InitData_t nsm_init_data;
+
+ nsm_init_data.base_ptr = self->base_ptr;
+ nsm_init_data.rcm_ptr = self->rcm_transceiver;
+ nsm_init_data.rsm_ptr = Fac_GetRsm(self, address);
+
+ Nsm_Ctor(nsm, &nsm_init_data);
+}
+
+/*! \brief Checks whether the given FBlock INIC instance is uninitialized
+ * \param fbi the INIC Instance pointer
+ * \return \c true if the given Fbi instance is not initialized, otherwise \c False.
+ */
+static bool Fac_IsFbiUninitialized(CInic * fbi)
+{
+ return (fbi->base_ptr == NULL) ;
+}
+
+/*! \brief Checks whether the given NSM instance is uninitialized
+ * \param nsm the NSM Instance pointer
+ * \return \c true if the given NSM instance is not initialized, otherwise \c False.
+ */
+static bool Fac_IsNsmUninitialized(CNodeScriptManagement * nsm)
+{
+ return (nsm->base_ptr == NULL) ;
+}
+
+/*! \brief Checks whether the given RSM instance is uninitialized
+ * \param rsm Reference to the RSM instance pointer
+ * \return \c true if the given Fbi instance is not initialized, otherwise \c False.
+ */
+static bool Fac_IsRsmUninitialized(CRemoteSyncManagement * rsm)
+{
+ return Fac_IsFbiUninitialized(rsm->inic_ptr);
+}
+
+/*! \brief Checks whether the given XRM instance is uninitialized
+ * \param xrm the XRM Instance pointer
+ * \return \c true if the given XRM instance is not initialized, otherwise \c False.
+ */
+static bool Fac_IsXrmUninitialized(CExtendedResourceManager * xrm)
+{
+ return (xrm->rsm_ptr == NULL) ;
+}
+
+/*! \brief Checks whether the given GPIO instance is uninitialized
+ * \param gpio the GPIO Instance pointer
+ * \return \c true if the given GPIO instance is not initialized, otherwise \c False.
+ */
+static bool Fac_IsGpioUninitialized(CGpio * gpio)
+{
+ return (NULL == gpio->nsm_ptr);
+}
+
+/*! \brief Checks whether the given I2C instance is uninitialized
+ * \param i2c the I2C Instance pointer
+ * \return \c true if the given I2C instance is not initialized, otherwise \c False.
+ */
+static bool Fac_IsI2cUninitialized(CI2c * i2c)
+{
+ return (NULL == i2c->nsm_ptr);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_fsm.c b/ucs2-lib/src/ucs_fsm.c
new file mode 100644
index 0000000..c180b0a
--- /dev/null
+++ b/ucs2-lib/src/ucs_fsm.c
@@ -0,0 +1,172 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Finite State Machine.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_FSM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_fsm.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal definitions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief NIL-event, indicates that no event is pending at the moment */
+#define FSM_E_NILEVENT 0
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static Fsm_Act_t Fsm_StateEval(CFsm *self);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Finite State Machine class.
+ * \param self Instance pointer
+ * \param inst_ptr Instance pointer used for state machine actions
+ * \param trans_table_ptr Pointer to transition table
+ * \param num_events Maximum number of events
+ * \param init_state Initialization state to start with
+ */
+void Fsm_Ctor(CFsm *self, void *inst_ptr, const Fsm_StateElem_t *trans_table_ptr,
+ uint8_t num_events, int8_t init_state)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->inst_ptr = inst_ptr;
+ self->event_occured = FSM_E_NILEVENT; /* Clear event variable */
+ self->current_state = init_state; /* Set initialization state */
+ self->transition_table_ptr = trans_table_ptr; /* Set pointer to given transition table */
+ self->num_events = num_events; /* Store maximum number of events */
+ self->internal_state = FSM_STATE_IDLE; /* Clear internal state */
+}
+
+/*! \brief Determine required action
+ * \details This function determines the required action in dependency of the current state
+ * and the triggered event. The current state will be transitioned to the next state.
+ * The internal event variable will be cleared and the determined action will be
+ * returned.
+ * \param self Instance pointer
+ * \return Determined required action
+ * \return \c NULL if no action is required
+ */
+static Fsm_Act_t Fsm_StateEval(CFsm *self)
+{
+ Fsm_Act_t retval = NULL; /* Set default return value */
+
+ if(self->event_occured != FSM_E_NILEVENT) /* Event occurred ? */
+ {
+ if((uint8_t)self->event_occured <= self->num_events) /* Check if event is valid */
+ {
+ /* Get state-matrix-element in dependency of current state and triggered event */
+ uint8_t i = ((uint8_t)self->current_state * self->num_events) + (uint8_t)self->event_occured;
+ Fsm_StateElem_t stateEvaluation = self->transition_table_ptr[i];
+ self->current_state = stateEvaluation.next_state; /* Set new state */
+ self->internal_state = FSM_STATE_IDLE; /* Set internal state to \c IDLE */
+ retval = stateEvaluation.action_fptr; /* Return required action */
+ }
+ else
+ {
+ self->internal_state = FSM_STATE_ERROR; /* Error occurred: Unknown event */
+ }
+
+ self->event_occured = FSM_E_NILEVENT; /* Clear event variable */
+ }
+
+ return retval;
+}
+
+/*! \brief Service function for Finite State Machines
+ * \details The state machine will be serviced until it will be stopped by the user or no
+ * further event is triggered. If a state transition occurred the associated action
+ * will be executed.
+ * \param self Instance pointer
+ * \return Internal state of the state machine (see \ref Fsm_State_t).
+ */
+Fsm_State_t Fsm_Service(CFsm *self)
+{
+ /* Internal state is set to \c FSM_STATE_SERVICE and any event is triggered? */
+ while((self->internal_state == FSM_STATE_SERVICE) && (self->event_occured != FSM_E_NILEVENT))
+ {
+ Fsm_Act_t action_fptr = Fsm_StateEval(self); /* Execute state transition */
+ if(action_fptr != NULL) /* Action required ? */
+ {
+ (*action_fptr)(self->inst_ptr); /* Execute action */
+ }
+ }
+
+ return self->internal_state; /* Return internal state machine state */
+}
+
+/*! \brief Set an event
+ * \details This function sets the given event and triggers the service for the given
+ * state machine.
+ * \param self Instance pointer
+ * \param e New event
+ */
+void Fsm_SetEvent(CFsm *self, int8_t e)
+{
+ if(self->internal_state != FSM_STATE_END)
+ {
+ self->event_occured = e; /* Set new event */
+ self->internal_state = FSM_STATE_SERVICE; /* Set internal state to \c FSM_STATE_SERVICE */
+ }
+}
+
+/*! \brief Sets the wait state
+ * \details This function sets the given state state machine into the wait state. The state
+ * machine stops and must be re-triggered.
+ * \param self Instance pointer
+ */
+void Fsm_Wait(CFsm *self)
+{
+ if(self->internal_state != FSM_STATE_END)
+ {
+ self->internal_state = FSM_STATE_WAIT; /* Set internal state to \c WAIT */
+ }
+}
+
+/*! \brief End processing of the state machine
+ * \details If this function is called the given state machine will be stopped immediately.
+ * \param self Instance pointer
+ */
+void Fsm_End(CFsm *self)
+{
+ self->internal_state = FSM_STATE_END; /* Set internal state to \c END */
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_gpio.c b/ucs2-lib/src/ucs_gpio.c
new file mode 100644
index 0000000..645978f
--- /dev/null
+++ b/ucs2-lib/src/ucs_gpio.c
@@ -0,0 +1,713 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the GPIO module.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_GPIO
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_gpio.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Gpio_PortCreateResCb(void *self, void *result_ptr);
+static void Gpio_PinModeConfigResCb(void *self, void *result_ptr);
+static void Gpio_PinStateConfigResCb(void *self, void *result_ptr);
+static void Gpio_TriggerEventStatusCb(void *self, void *result_ptr);
+static bool Gpio_RxFilter4NsmCb(Msg_MostTel_t *tel_ptr, void *self);
+static void Gpio_RxError(void *self, Msg_MostTel_t *msg_ptr, Gpio_ErrResultCb_t res_cb_fptr);
+static void Gpio_PortCreate_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void Gpio_PortPinMode_Status(void *self, Msg_MostTel_t *msg_ptr);
+static void Gpio_PortPinState_Status(void *self, Msg_MostTel_t *msg_ptr);
+static void Gpio_NsmResultCb(void * self, Nsm_Result_t result);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class Gpio */
+/*------------------------------------------------------------------------------------------------*/
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the GPIO class.
+ * \param self Reference to CGpio instance.
+ * \param init_ptr init data_ptr.
+ */
+void Gpio_Ctor(CGpio *self, Gpio_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CGpio));
+
+ /* Set class instances */
+ self->inic_ptr = init_ptr->inic_ptr;
+ self->nsm_ptr = init_ptr->nsm_ptr;
+
+ self->curr_user_data.trigger_event_status_fptr = init_ptr->trigger_event_status_fptr;
+
+ /* Init observers */
+ Obs_Ctor(&self->triggerevent_observer, self, &Gpio_TriggerEventStatusCb);
+
+ /* Subscribe Observers */
+ Inic_AddObsrvGpioTriggerEvent(self->inic_ptr, &self->triggerevent_observer);
+
+ /* Set device target address */
+ self->device_address = Inic_GetTargetAddress(self->inic_ptr);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Creates the GPIO port
+ * \param self Reference to CGpio instance.
+ * \param index The index of the GPIO Port instance.
+ * \param debounce_time The timeout for the GPIO debounce timer (in ms).
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Gpio_CreatePort(CGpio * self, uint8_t index, uint16_t debounce_time, Ucs_Gpio_CreatePortResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ Gpio_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = index;
+ tmp_script->cfg_data[1] = MISC_HB(debounce_time);
+ tmp_script->cfg_data[2] = MISC_LB(debounce_time);
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_GPIO_PORT_CREATE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT;
+ tmp_script->cfg_msg.DataLen = 3U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &Gpio_RxFilter4NsmCb, &Gpio_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.portcreate_res_cb = res_fptr;
+ self->curr_res_cb = &Gpio_PortCreateResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Sets the pin mode configuration of the given GPIO port
+ * \param self Reference to CGpio instance.
+ * \param gpio_port_handle The GPIO Port resource handle.
+ * \param pin The GPIO pin that is to be configured.
+ * \param mode The mode of the GPIO pin.
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Gpio_SetPinModeConfig(CGpio * self, uint16_t gpio_port_handle, uint8_t pin, Ucs_Gpio_PinMode_t mode, Ucs_Gpio_ConfigPinModeResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ Gpio_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = MISC_HB(gpio_port_handle);
+ tmp_script->cfg_data[1] = MISC_LB(gpio_port_handle);
+ tmp_script->cfg_data[2] = pin;
+ tmp_script->cfg_data[3] = (uint8_t)mode;
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_GPIO_PORT_PIN_MODE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_SETGET;
+ tmp_script->cfg_msg.DataLen = 4U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &Gpio_RxFilter4NsmCb, &Gpio_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.pinmode_res_cb = res_fptr;
+ self->curr_res_cb = &Gpio_PinModeConfigResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Gets the pin mode configuration of the given GPIO port
+ * \param self Reference to CGpio instance.
+ * \param gpio_port_handle The GPIO Port resource handle.
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Gpio_GetPinModeConfig(CGpio * self, uint16_t gpio_port_handle, Ucs_Gpio_ConfigPinModeResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ Gpio_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = MISC_HB(gpio_port_handle);
+ tmp_script->cfg_data[1] = MISC_LB(gpio_port_handle);
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_GPIO_PORT_PIN_MODE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_GET;
+ tmp_script->cfg_msg.DataLen = 2U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &Gpio_RxFilter4NsmCb, &Gpio_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.pinmode_res_cb = res_fptr;
+ self->curr_res_cb = &Gpio_PinModeConfigResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Sets the pin state configuration of the given GPIO port
+ * \param self Reference to CGpio instance.
+ * \param gpio_port_handle The GPIO Port resource handle.
+ * \param mask The GPIO pin to be written.
+ * \param data The state of the GPIO pin to be written.
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Gpio_SetPinStateConfig(CGpio * self, uint16_t gpio_port_handle, uint16_t mask, uint16_t data, Ucs_Gpio_PinStateResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ Gpio_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = MISC_HB(gpio_port_handle);
+ tmp_script->cfg_data[1] = MISC_LB(gpio_port_handle);
+ tmp_script->cfg_data[2] = MISC_HB(mask);
+ tmp_script->cfg_data[3] = MISC_LB(mask);
+ tmp_script->cfg_data[4] = MISC_HB(data);
+ tmp_script->cfg_data[5] = MISC_LB(data);
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_GPIO_PORT_PIN_STATE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_SETGET;
+ tmp_script->cfg_msg.DataLen = 6U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &Gpio_RxFilter4NsmCb, &Gpio_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.pinstate_res_cb = res_fptr;
+ self->curr_res_cb = &Gpio_PinStateConfigResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Retrieves the pin state configuration of the given GPIO port
+ * \param self Reference to CGpio instance.
+ * \param gpio_port_handle The GPIO Port resource handle.
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Gpio_GetPinStateConfig(CGpio * self, uint16_t gpio_port_handle, Ucs_Gpio_PinStateResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ Gpio_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = MISC_HB(gpio_port_handle);
+ tmp_script->cfg_data[1] = MISC_LB(gpio_port_handle);
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_GPIO_PORT_PIN_STATE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_GET;
+ tmp_script->cfg_msg.DataLen = 2U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &Gpio_RxFilter4NsmCb, &Gpio_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.pinstate_res_cb = res_fptr;
+ self->curr_res_cb = &Gpio_PinStateConfigResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Handles the result of the GPIOPortCreate.StartResultAck
+ * \param self Reference to CGpio instance
+ * \param result_ptr result pointer
+ */
+static void Gpio_PortCreateResCb(void *self, void *result_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ uint16_t gpio_port_handle;
+ Ucs_Gpio_Result_t res;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ /* Init result */
+ MISC_MEM_SET(&res, 0, sizeof(Ucs_Gpio_Result_t));
+
+ if (NULL != result_ptr_)
+ {
+ gpio_port_handle = 0U;
+ res.code = UCS_GPIO_RES_ERR_CMD;
+ res.details.result_type = UCS_GPIO_RESULT_TYPE_TGT;
+ res.details.inic_result = result_ptr_->result;
+ if (result_ptr_->data_info != NULL)
+ {
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ res.code = UCS_GPIO_RES_SUCCESS;
+ gpio_port_handle = *(uint16_t *)result_ptr_->data_info;
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ res.details.result_type = UCS_GPIO_RESULT_TYPE_TX;
+ res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION)
+ {
+ res.code = UCS_GPIO_RES_ERR_SYNC;
+ }
+ }
+
+ if (NULL != self_->curr_user_data.portcreate_res_cb)
+ {
+ self_->curr_user_data.portcreate_res_cb(self_->device_address, gpio_port_handle, res, self_->inic_ptr->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Handles the result of the GPIOPortPinMode.Status
+ * \param self Reference to CGpio instance
+ * \param result_ptr result pointer
+ */
+static void Gpio_PinModeConfigResCb(void *self, void *result_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ Inic_GpioPortPinModeStatus_t status;
+ Ucs_Gpio_Result_t res;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ /* Init result */
+ MISC_MEM_SET(&res, 0, sizeof(Ucs_Gpio_Result_t));
+
+ if (NULL != result_ptr_)
+ {
+ status.gpio_handle = 0U;
+ status.cfg_list = NULL;
+ status.len = 0U;
+ res.code = UCS_GPIO_RES_ERR_CMD;
+ res.details.result_type = UCS_GPIO_RESULT_TYPE_TGT;
+ res.details.inic_result = result_ptr_->result;
+ if (result_ptr_->data_info != NULL)
+ {
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ res.code = UCS_GPIO_RES_SUCCESS;
+ status = *(Inic_GpioPortPinModeStatus_t *)result_ptr_->data_info;
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ res.details.result_type = UCS_GPIO_RESULT_TYPE_TX;
+ res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION)
+ {
+ res.code = UCS_GPIO_RES_ERR_SYNC;
+ }
+ }
+
+ if (NULL != self_->curr_user_data.pinmode_res_cb)
+ {
+ self_->curr_user_data.pinmode_res_cb(self_->device_address, status.gpio_handle, status.cfg_list, status.len, res, self_->inic_ptr->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Handles the result of the GPIOPortPinSate.Status
+ * \param self Reference to CGpio instance
+ * \param result_ptr result pointer
+ */
+static void Gpio_PinStateConfigResCb(void *self, void *result_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ Inic_GpioPortPinStateStatus_t status;
+ Ucs_Gpio_Result_t res;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ /* Init result */
+ MISC_MEM_SET(&res, 0, sizeof(Ucs_Gpio_Result_t));
+
+ if (NULL != result_ptr_)
+ {
+ status.gpio_handle = 0U;
+ status.current_state = 0U;
+ status.sticky_state = 0U;
+ res.code = UCS_GPIO_RES_ERR_CMD;
+ res.details.result_type = UCS_GPIO_RESULT_TYPE_TGT;
+ res.details.inic_result = result_ptr_->result;
+ if (result_ptr_->data_info != NULL)
+ {
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ res.code = UCS_GPIO_RES_SUCCESS;
+ status = *(Inic_GpioPortPinStateStatus_t *)result_ptr_->data_info;
+ }
+ else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION)
+ {
+ res.code = UCS_GPIO_RES_ERR_SYNC;
+ }
+ else
+ {
+ res.details.result_type = UCS_GPIO_RESULT_TYPE_TX;
+ res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ }
+
+ if (NULL != self_->curr_user_data.pinstate_res_cb)
+ {
+ self_->curr_user_data.pinstate_res_cb(self_->device_address, status.gpio_handle, status.current_state, status.sticky_state, res, self_->inic_ptr->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Handles the result of the GPIOPortTriggerEvent.Status
+ * \param self Reference to CGpio instance
+ * \param result_ptr result pointer
+ */
+static void Gpio_TriggerEventStatusCb(void *self, void *result_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ Inic_GpioTriggerEventStatus_t status;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (NULL != result_ptr_)
+ {
+ status = *(Inic_GpioTriggerEventStatus_t *)result_ptr_->data_info;
+
+ if (NULL != self_->curr_user_data.trigger_event_status_fptr)
+ {
+ self_->curr_user_data.trigger_event_status_fptr(self_->device_address, status.gpio_handle, status.rising_edges, status.falling_edges, status.levels, self_->inic_ptr->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+
+/*! \brief Checks whether the incoming is our message and handles It if it's.
+ * \param tel_ptr Reference to the message object.
+ * \param self Reference to the user argument.
+ * \return Returns \c true to discard the message and free it to the pool if it's our message. Otherwise, returns
+ * \c false.
+ */
+static bool Gpio_RxFilter4NsmCb(Msg_MostTel_t *tel_ptr, void *self)
+{
+ CGpio *self_ = (CGpio *)self;
+ bool ret_val = true;
+
+ if ((tel_ptr != NULL) && (tel_ptr->id.function_id == self_->curr_script.script.send_cmd->FunktId))
+ {
+ if (tel_ptr->id.op_type == UCS_OP_RESULT)
+ {
+ switch(tel_ptr->id.function_id)
+ {
+ case INIC_FID_GPIO_PORT_CREATE:
+ Gpio_PortCreate_Result(self_, tel_ptr);
+ break;
+ case INIC_FID_GPIO_PORT_PIN_MODE:
+ Gpio_PortPinMode_Status(self_, tel_ptr);
+ break;
+ case INIC_FID_GPIO_PORT_PIN_STATE:
+ Gpio_PortPinState_Status(self_, tel_ptr);
+ break;
+ default:
+ ret_val = false;
+ break;
+ }
+ }
+ else if (tel_ptr->id.op_type == UCS_OP_ERROR)
+ {
+ Gpio_ErrResultCb_t res_cb_fptr = self_->curr_res_cb;
+ Gpio_RxError(self_, tel_ptr, res_cb_fptr);
+ }
+ }
+ else
+ {
+ ret_val = false;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Result callback function for NSM result. Whenever this function is called the NodeScripting has finished the
+ * script's execution. This function handles transmission and sync error. Only these two kind of errors can occur.
+ * \param self Reference to the called user instance.
+ * \param result Result of the scripting operation.
+ */
+static void Gpio_NsmResultCb(void * self, Nsm_Result_t result)
+{
+ CGpio *self_ = (CGpio *)self;
+
+ if (self_ != NULL)
+ {
+ Inic_StdResult_t res_data;
+ bool allow_report = false;
+
+ if ((result.code == UCS_NS_RES_ERROR) && (result.details.result_type == NS_RESULT_TYPE_TX))
+ {
+ res_data.data_info = &result.details.tx_result;
+ res_data.result.code = UCS_RES_ERR_TRANSMISSION;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+ allow_report = true;
+ }
+ else if ((result.code == UCS_NS_RES_ERROR) && (result.details.result_type == NS_RESULT_TYPE_TGT_SYNC))
+ {
+ res_data.data_info = &result.details.inic_result;
+ res_data.result.code = result.details.inic_result.code;
+ res_data.result.info_ptr = result.details.inic_result.info_ptr;
+ res_data.result.info_size = result.details.inic_result.info_size;
+ allow_report = true;
+ }
+ else if ((result.code == UCS_NS_RES_ERROR) && ((result.details.tx_result == UCS_MSG_STAT_OK) ||
+ (result.details.inic_result.code == UCS_RES_SUCCESS)))
+ {
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_ERR_TIMEOUT;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+
+ TR_ERROR((self_->nsm_ptr->base_ptr->ucs_user_ptr, "[GPIO]", "TIMEOUT ERROR occurred for currently GPIO command. No response received from target device with address 0x%X.", 1U, self_->device_address));
+ }
+
+ if ((self_->curr_res_cb != NULL) && (allow_report))
+ {
+ self_->curr_res_cb(self_, &res_data);
+ }
+ }
+}
+
+/*---------------------------------- GW Functions ----------------------------------*/
+
+/*! \brief Error Handler function for all GPIO methods
+ * \param self Reference to CGpio instance
+ * \param msg_ptr Pointer to received message
+ * \param res_cb_fptr Pointer to a specified error handler function
+ */
+static void Gpio_RxError(void *self, Msg_MostTel_t *msg_ptr, Gpio_ErrResultCb_t res_cb_fptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_->inic_ptr,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ if (res_cb_fptr != NULL)
+ {
+ res_cb_fptr(self_, &res_data);
+ }
+}
+
+/*! \brief Handler function for GPIOPortCreate.ResultAck
+ * \details Element res_data.data_info points to the variable gpio_port_handle which holds the
+ * GPIO Port resource handle.
+ * \param self Reference to CGpio instance
+ * \param msg_ptr Pointer to received message
+ */
+static void Gpio_PortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ uint16_t gpio_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&gpio_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &gpio_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Gpio_PortCreateResCb(self_, &res_data);
+}
+
+/*! \brief Handler function for GPIOPortPinMode.Status
+ * \param self Reference to CGpio instance
+ * \param msg_ptr Pointer to received message
+ */
+static void Gpio_PortPinMode_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ Inic_GpioPortPinModeStatus_t res;
+ Inic_StdResult_t res_data;
+ uint8_t i = 2U, j = 0U;
+ Ucs_Gpio_PinConfiguration_t pin_ls[16U];
+
+ res.cfg_list = &pin_ls[0];
+ res.len = (msg_ptr->tel.tel_len - 2U) >> 1U;
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&res.gpio_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ for (; (i < msg_ptr->tel.tel_len) && (j < 16U); i=i+2U)
+ {
+ pin_ls[j].pin = msg_ptr->tel.tel_data_ptr[i];
+ pin_ls[j].mode = (Ucs_Gpio_PinMode_t)msg_ptr->tel.tel_data_ptr[i+1U];
+ j++;
+ }
+
+ Gpio_PinModeConfigResCb(self_, &res_data);
+}
+
+/*! \brief Handler function for GPIOPortPinState.Status
+ * \param self Reference to CGpio instance
+ * \param msg_ptr Pointer to received message
+ */
+static void Gpio_PortPinState_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CGpio *self_ = (CGpio *)self;
+ Inic_GpioPortPinStateStatus_t res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&res.gpio_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ MISC_DECODE_WORD(&res.current_state, &(msg_ptr->tel.tel_data_ptr[2]));
+ MISC_DECODE_WORD(&res.sticky_state, &(msg_ptr->tel.tel_data_ptr[4]));
+
+ Gpio_PinStateConfigResCb(self_, &res_data);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_i2c.c b/ucs2-lib/src/ucs_i2c.c
new file mode 100644
index 0000000..d2523b0
--- /dev/null
+++ b/ucs2-lib/src/ucs_i2c.c
@@ -0,0 +1,646 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the I2C Module.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_I2C
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_i2c.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void I2c_PortCreateResCb(void *self, void *result_ptr);
+static void I2c_PortWriteResCb(void *self, void *result_ptr);
+static void I2c_PortReadResCb(void *self, void *result_ptr);
+static void I2c_TriggerEventStatusCb(void *self, void *result_ptr);
+static bool I2c_RxFilter4NsmCb(Msg_MostTel_t *tel_ptr, void *self);
+static void I2c_PortCreate_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void I2c_PortRead_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void I2c_PortWrite_Result(void *self, Msg_MostTel_t *msg_ptr);
+static void I2c_RxError(void *self, Msg_MostTel_t *msg_ptr, I2c_ErrResultCb_t res_cb_fptr);
+static void I2c_NsmResultCb(void * self, Nsm_Result_t result);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class I2C */
+/*------------------------------------------------------------------------------------------------*/
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the I2C class.
+ * \param self Instance pointer
+ * \param init_ptr init data_ptr
+ */
+void I2c_Ctor(CI2c * self, I2c_InitData_t * init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CI2c));
+
+ /* Set class instances */
+ self->inic_ptr = init_ptr->inic_ptr;
+ self->base_ptr = self->inic_ptr->base_ptr;
+ self->nsm_ptr = init_ptr->nsm_ptr;
+
+ self->curr_user_data.i2c_interrupt_report_fptr = init_ptr->i2c_interrupt_report_fptr;
+
+ /* Init GPIOTriggerEvent observer */
+ Obs_Ctor(&self->triggerevent_observer, self, &I2c_TriggerEventStatusCb);
+
+ /* Subscribe Observers */
+ Inic_AddObsrvGpioTriggerEvent(self->inic_ptr, &self->triggerevent_observer);
+
+ /* Set device id */
+ self->device_address = Inic_GetTargetAddress(self->inic_ptr);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Creates an I2C Port with its associated parameter.
+ * \param self Reference to CI2c instance
+ * \param index I2C Port instance
+ * \param speed The speed grade of the I2C Port
+ * \param i2c_int_mask The I2C interrupt pin mask on the GPIO Port.
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t I2c_CreatePort(CI2c * self, uint8_t index, Ucs_I2c_Speed_t speed, uint8_t i2c_int_mask, Ucs_I2c_CreatePortResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+ uint8_t address = 0x00U; /* Address will be ignored */
+ uint8_t mode = 0x01U; /* Master Mode */
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ I2c_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = index;
+ tmp_script->cfg_data[1] = address;
+ tmp_script->cfg_data[2] = mode;
+ tmp_script->cfg_data[3] = (uint8_t)speed;
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_I2C_PORT_CREATE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT;
+ tmp_script->cfg_msg.DataLen = 4U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &I2c_RxFilter4NsmCb, &I2c_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.int_pin_mask = i2c_int_mask;
+ self->curr_user_data.portcreate_res_cb = res_fptr;
+ self->curr_res_cb = &I2c_PortCreateResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Writes a block of bytes to an I2C device at a specified I2C address.
+ * \param self Reference to CI2c instance
+ * \param port_handle Port resource handle
+ * \param mode The write transfer mode
+ * \param block_count The number of blocks to be written to the I2C address.
+ * \param slave_address The 7-bit I2C slave address of the peripheral to be read
+ * \param timeout The timeout for the I2C Port write
+ * \param data_len Number of bytes to be written to the addressed I2C peripheral
+ * \param data_ptr Reference to the data to be written
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t I2c_WritePort(CI2c * self, uint16_t port_handle, Ucs_I2c_TrMode_t mode, uint8_t block_count, uint8_t slave_address, uint16_t timeout, uint8_t data_len, uint8_t data_ptr[], Ucs_I2c_WritePortResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ if ((0U < data_len) && (NULL != data_ptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ bool is_ok = true;
+
+ result = UCS_RET_ERR_PARAM;
+ if ((UCS_I2C_BURST_MODE == mode) && (0U == block_count))
+ {
+ is_ok = false;
+ }
+
+ if (is_ok)
+ {
+ uint8_t i;
+ I2c_Script_t * tmp_script = &self->curr_script;
+
+ for (i = 0U; i < data_len; i++)
+ {
+ tmp_script->cfg_data[8U + i] = data_ptr[i];
+ }
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = MISC_HB(port_handle);
+ tmp_script->cfg_data[1] = MISC_LB(port_handle);
+ tmp_script->cfg_data[2] = (uint8_t)mode;
+ tmp_script->cfg_data[3] = block_count;
+ tmp_script->cfg_data[4] = slave_address;
+ tmp_script->cfg_data[5] = (mode == UCS_I2C_BURST_MODE) ? (data_len/block_count):data_len;
+ tmp_script->cfg_data[6] = MISC_HB(timeout);
+ tmp_script->cfg_data[7] = MISC_LB(timeout);
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_I2C_PORT_WRITE;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT;
+ tmp_script->cfg_msg.DataLen = data_len + 8U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &I2c_RxFilter4NsmCb, &I2c_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.portwrite_res_cb = res_fptr;
+ self->curr_res_cb = &I2c_PortWriteResCb;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Reads a block of bytes from an I2C device at a specified I2C address.
+ * \param self Reference to CI2c instance
+ * \param port_handle Port resource handle
+ * \param slave_address The 7-bit I2C slave address of the peripheral to be read
+ * \param data_len Number of bytes to be read from the address
+ * \param timeout The timeout for the I2C Port read
+ * \param res_fptr Required result callback function pointer.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t I2c_ReadPort(CI2c * self, uint16_t port_handle, uint8_t slave_address, uint8_t data_len, uint16_t timeout, Ucs_I2c_ReadPortResCb_t res_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((NULL != self) && (NULL != res_fptr))
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ if (!Nsm_IsLocked(self->nsm_ptr))
+ {
+ I2c_Script_t * tmp_script = &self->curr_script;
+
+ /* Set Data */
+ tmp_script->cfg_data[0] = MISC_HB(port_handle);
+ tmp_script->cfg_data[1] = MISC_LB(port_handle);
+ tmp_script->cfg_data[2] = slave_address;
+ tmp_script->cfg_data[3] = data_len;
+ tmp_script->cfg_data[4] = MISC_HB(timeout);
+ tmp_script->cfg_data[5] = MISC_LB(timeout);
+
+ /* Set message id */
+ tmp_script->cfg_msg.FBlockId = FB_INIC;
+ tmp_script->cfg_msg.InstId = 0U;
+ tmp_script->cfg_msg.FunktId = INIC_FID_I2C_PORT_READ;
+ tmp_script->cfg_msg.OpCode = (uint8_t)UCS_OP_STARTRESULT;
+ tmp_script->cfg_msg.DataLen = 6U;
+ tmp_script->cfg_msg.DataPtr = &tmp_script->cfg_data[0];
+
+ /* Set script */
+ tmp_script->script.send_cmd = &tmp_script->cfg_msg;
+ tmp_script->script.pause = 0U;
+
+ /* Transmit script */
+ result = Nsm_Run_Pv(self->nsm_ptr, &tmp_script->script, 1U, self, &I2c_RxFilter4NsmCb, &I2c_NsmResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->curr_user_data.portread_res_cb = res_fptr;
+ self->curr_res_cb = &I2c_PortReadResCb;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Handles the result of the I2CPortCreate.StartResultAck
+ * \param self Instance pointer
+ * \param result_ptr result pointer
+ */
+static void I2c_PortCreateResCb(void *self, void *result_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ uint16_t i2c_port_handle;
+ Ucs_I2c_Result_t res;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ /* Init result */
+ MISC_MEM_SET(&res, 0, sizeof(Ucs_I2c_Result_t));
+
+ if (NULL != result_ptr_)
+ {
+ i2c_port_handle = 0U;
+ res.code = UCS_I2C_RES_ERR_CMD;
+ res.details.result_type = UCS_I2C_RESULT_TYPE_TGT;
+ res.details.inic_result = result_ptr_->result;
+ if (result_ptr_->data_info != NULL)
+ {
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ res.code = UCS_I2C_RES_SUCCESS;
+ i2c_port_handle = *(uint16_t *)result_ptr_->data_info;
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ res.details.result_type = UCS_I2C_RESULT_TYPE_TX;
+ res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION)
+ {
+ res.code = UCS_I2C_RES_ERR_SYNC;
+ }
+ }
+
+ if (NULL != self_->curr_user_data.portcreate_res_cb)
+ {
+ self_->curr_user_data.portcreate_res_cb(self_->device_address, i2c_port_handle, res, self_->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Handles the result of the I2CPortWrite.StartResultAck
+ * \param self Instance pointer
+ * \param result_ptr result pointer
+ */
+static void I2c_PortWriteResCb(void *self, void *result_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ Inic_I2cWriteResStatus_t wr_res;
+ Ucs_I2c_Result_t res;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ /* Init result */
+ MISC_MEM_SET(&res, 0, sizeof(Ucs_I2c_Result_t));
+
+ if (NULL != result_ptr_)
+ {
+ wr_res.data_len = 0U;
+ wr_res.port_handle = 0U;
+ wr_res.slave_address = 0U;
+ res.code = UCS_I2C_RES_ERR_CMD;
+ res.details.result_type = UCS_I2C_RESULT_TYPE_TGT;
+ res.details.inic_result = result_ptr_->result;
+ if (result_ptr_->data_info != NULL)
+ {
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ res.code = UCS_I2C_RES_SUCCESS;
+ wr_res = *(Inic_I2cWriteResStatus_t *)result_ptr_->data_info;
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ res.details.result_type = UCS_I2C_RESULT_TYPE_TX;
+ res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION)
+ {
+ res.code = UCS_I2C_RES_ERR_SYNC;
+ }
+ }
+
+ if (NULL != self_->curr_user_data.portwrite_res_cb)
+ {
+ self_->curr_user_data.portwrite_res_cb(self_->device_address, wr_res.port_handle, wr_res.slave_address, wr_res.data_len, res, self_->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Handles the result of the I2CPortRead.StartResultAck
+ * \param self Instance pointer
+ * \param result_ptr result pointer
+ */
+static void I2c_PortReadResCb(void *self, void *result_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ Inic_I2cReadResStatus_t read_res;
+ Ucs_I2c_Result_t res;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ /* Init result */
+ MISC_MEM_SET(&res, 0, sizeof(Ucs_I2c_Result_t));
+
+ if (NULL != result_ptr_)
+ {
+ read_res.data_len = 0U;
+ read_res.data_ptr = NULL;
+ read_res.port_handle = 0U;
+ read_res.slave_address = 0U;
+ res.code = UCS_I2C_RES_ERR_CMD;
+ res.details.result_type = UCS_I2C_RESULT_TYPE_TGT;
+ res.details.inic_result = result_ptr_->result;
+ if (result_ptr_->data_info != NULL)
+ {
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ res.code = UCS_I2C_RES_SUCCESS;
+ read_res = *(Inic_I2cReadResStatus_t *)result_ptr_->data_info;
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ res.details.result_type = UCS_I2C_RESULT_TYPE_TX;
+ res.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ else if (result_ptr_->result.code == UCS_RES_ERR_CONFIGURATION)
+ {
+ res.code = UCS_I2C_RES_ERR_SYNC;
+ }
+ }
+
+ if (NULL != self_->curr_user_data.portread_res_cb)
+ {
+ self_->curr_user_data.portread_res_cb(self_->device_address, read_res.port_handle, read_res.slave_address, read_res.data_len, read_res.data_ptr, res, self_->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Handles the result of the GPIOPortTriggerEvent.Status
+ * \param self Instance pointer
+ * \param result_ptr result pointer
+ */
+static void I2c_TriggerEventStatusCb(void *self, void *result_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if ((NULL != result_ptr_) &&
+ (NULL != self_->curr_user_data.i2c_interrupt_report_fptr))
+ {
+ Inic_GpioTriggerEventStatus_t status;
+ uint16_t int_mask = self_->curr_user_data.int_pin_mask;
+ status = *(Inic_GpioTriggerEventStatus_t *)result_ptr_->data_info;
+
+ if ((!status.is_first_report) &&
+ ((int_mask == (status.rising_edges & int_mask)) ||
+ (int_mask == (status.levels & int_mask)) ||
+ (int_mask == (status.falling_edges & int_mask))))
+ {
+ self_->curr_user_data.i2c_interrupt_report_fptr(self_->device_address, self_->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Checks whether the incoming is our message and handles It if it's.
+ * \param tel_ptr Reference to the message object.
+ * \param self Reference to the user argument.
+ * \return Returns \c true to discard the message and free it to the pool if it's our message. Otherwise, returns
+ * \c false.
+ */
+static bool I2c_RxFilter4NsmCb(Msg_MostTel_t *tel_ptr, void *self)
+{
+ CI2c *self_ = (CI2c *)self;
+ bool ret_val = true;
+
+ if ((tel_ptr != NULL) && (tel_ptr->id.function_id == self_->curr_script.script.send_cmd->FunktId))
+ {
+ if (tel_ptr->id.op_type == UCS_OP_RESULT)
+ {
+ switch(tel_ptr->id.function_id)
+ {
+ case INIC_FID_I2C_PORT_CREATE:
+ I2c_PortCreate_Result(self_, tel_ptr);
+ break;
+ case INIC_FID_I2C_PORT_READ:
+ I2c_PortRead_Result(self_, tel_ptr);
+ break;
+ case INIC_FID_I2C_PORT_WRITE:
+ I2c_PortWrite_Result(self_, tel_ptr);
+ break;
+ default:
+ ret_val = false;
+ break;
+ }
+ }
+ else if (tel_ptr->id.op_type == UCS_OP_ERROR)
+ {
+ I2c_ErrResultCb_t res_cb_fptr = self_->curr_res_cb;
+ I2c_RxError(self_, tel_ptr, res_cb_fptr);
+ }
+ }
+ else
+ {
+ ret_val = false;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Result callback function for NSM result. Whenever this function is called the NodeScripting has finished the
+ * script's execution. This function handles transmission and sync error. Only these two kind of errors can occur.
+ * \param self Reference to the called user instance.
+ * \param result Result of the scripting operation.
+ */
+static void I2c_NsmResultCb(void * self, Nsm_Result_t result)
+{
+ CI2c *self_ = (CI2c *)self;
+
+ if (self_ != NULL)
+ {
+ Inic_StdResult_t res_data;
+ bool allow_report = false;
+
+ if ((result.code == UCS_NS_RES_ERROR) && (result.details.result_type == NS_RESULT_TYPE_TX))
+ {
+ res_data.data_info = &result.details.tx_result;
+ res_data.result.code = UCS_RES_ERR_TRANSMISSION;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+ allow_report = true;
+ }
+ else if ((result.code == UCS_NS_RES_ERROR) && (result.details.result_type == NS_RESULT_TYPE_TGT_SYNC))
+ {
+ res_data.data_info = &result.details.inic_result;
+ res_data.result.code = result.details.inic_result.code;
+ res_data.result.info_ptr = result.details.inic_result.info_ptr;
+ res_data.result.info_size = result.details.inic_result.info_size;
+ allow_report = true;
+ }
+ else if ((result.code == UCS_NS_RES_ERROR) && ((result.details.tx_result == UCS_MSG_STAT_OK) ||
+ (result.details.inic_result.code == UCS_RES_SUCCESS)))
+ {
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_ERR_TIMEOUT;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[I2C]", "TIMEOUT ERROR occurred for currently I2C command. No response received from target device with address 0x%X.", 1U, self_->device_address));
+ }
+
+ if ((self_->curr_res_cb != NULL) && (allow_report))
+ {
+ self_->curr_res_cb(self_, &res_data);
+ }
+ }
+}
+
+/*---------------------------------- GW Functions ----------------------------------*/
+
+/*! \brief Error Handler function for all I2C methods
+ * \param self Reference to CI2c instance
+ * \param msg_ptr Pointer to received message
+ * \param res_cb_fptr Pointer to a specified error handler function
+ */
+static void I2c_RxError(void *self, Msg_MostTel_t *msg_ptr, I2c_ErrResultCb_t res_cb_fptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_->inic_ptr,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ if (res_cb_fptr != NULL)
+ {
+ res_cb_fptr(self_, &res_data);
+ }
+}
+
+/*! \brief Handler function for I2CPortCreate.ResultAck
+ * \details Element res_data.data_info points to the variable i2c_port_handle which holds the
+ * I2C Port resource handle.
+ * \param self Reference to CI2c instance
+ * \param msg_ptr Pointer to received message
+ */
+static void I2c_PortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ uint16_t i2c_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&i2c_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &i2c_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ I2c_PortCreateResCb(self_, &res_data);
+}
+
+/*! \brief Handler function for I2CPortRead.ResultAck
+ * \details Element res_data.data_info points to a variable of type Inic_I2cReadResStatus_t which holds the
+ * the results of the I2CPortRead.StartResultAck command.
+ * \param self Reference to CI2c instance
+ * \param msg_ptr Pointer to received message
+ */
+static void I2c_PortRead_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ Inic_I2cReadResStatus_t i2c_read_res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &i2c_read_res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&i2c_read_res.port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ i2c_read_res.slave_address = msg_ptr->tel.tel_data_ptr[2];
+ i2c_read_res.data_len = msg_ptr->tel.tel_data_ptr[3];
+ i2c_read_res.data_ptr = &msg_ptr->tel.tel_data_ptr[4];
+
+ I2c_PortReadResCb(self_, &res_data);
+}
+
+/*! \brief Handler function for I2CPortWrite.ResultAck
+ * \details Element res_data.data_info points to a variable of type Inic_I2cWriteResStatus_t which holds the
+ * the results of the I2CPortWrite.StartResultAck command.
+ * \param self Reference to CI2c instance
+ * \param msg_ptr Pointer to received message
+ */
+static void I2c_PortWrite_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CI2c *self_ = (CI2c *)self;
+ Inic_I2cWriteResStatus_t i2c_write_res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &i2c_write_res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&i2c_write_res.port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ i2c_write_res.slave_address = msg_ptr->tel.tel_data_ptr[2];
+ i2c_write_res.data_len = msg_ptr->tel.tel_data_ptr[3];
+
+ I2c_PortWriteResCb(self_, &res_data);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_inic.c b/ucs2-lib/src/ucs_inic.c
new file mode 100644
index 0000000..5d96a49
--- /dev/null
+++ b/ucs2-lib/src/ucs_inic.c
@@ -0,0 +1,1817 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of FBlock INIC
+ * \details Contains the general, device an network management parts of INIC management
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_INIC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_misc.h"
+#include "ucs_ret_pb.h"
+#include "ucs_inic.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief List of all INIC messages */
+static const Dec_FktOpIcm_t inic_handler[] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+ { DEC_FKTOP(INIC_FID_NOTIFICATION, UCS_OP_STATUS), Inic_Notification_Status },
+ { DEC_FKTOP(INIC_FID_NOTIFICATION, UCS_OP_ERROR), Inic_Notification_Error },
+ { DEC_FKTOP(INIC_FID_DEVICE_STATUS, UCS_OP_STATUS), Inic_DeviceStatus_Status },
+ { DEC_FKTOP(INIC_FID_DEVICE_STATUS, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_DEVICE_VERSION, UCS_OP_STATUS), Inic_DeviceVersion_Status },
+ { DEC_FKTOP(INIC_FID_DEVICE_VERSION, UCS_OP_ERROR), Inic_DeviceVersion_Error },
+ { DEC_FKTOP(INIC_FID_DEVICE_POWER_OFF, UCS_OP_STATUS), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_DEVICE_POWER_OFF, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_DEVICE_ATTACH, UCS_OP_RESULT), Inic_DeviceAttach_Result },
+ { DEC_FKTOP(INIC_FID_DEVICE_ATTACH, UCS_OP_ERROR), Inic_DeviceAttach_Error },
+ { DEC_FKTOP(INIC_FID_DEVICE_SYNC, UCS_OP_RESULT), Inic_DeviceSync_Result },
+ { DEC_FKTOP(INIC_FID_DEVICE_SYNC, UCS_OP_ERROR), Inic_DeviceSync_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_STATUS, UCS_OP_STATUS), Inic_NwStatus_Status },
+ { DEC_FKTOP(INIC_FID_MOST_NW_STATUS, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_MOST_NW_CFG, UCS_OP_STATUS), Inic_NwConfig_Status },
+ { DEC_FKTOP(INIC_FID_MOST_NW_CFG, UCS_OP_ERROR), Inic_NwConfig_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_FRAME_COUNTER, UCS_OP_STATUS), Inic_NwFrameCounter_Status },
+ { DEC_FKTOP(INIC_FID_MOST_NW_FRAME_COUNTER, UCS_OP_ERROR), Inic_NwFrameCounter_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_STARTUP, UCS_OP_RESULT), Inic_NwStartup_Result },
+ { DEC_FKTOP(INIC_FID_MOST_NW_STARTUP, UCS_OP_ERROR), Inic_NwStartup_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_SHUTDOWN, UCS_OP_RESULT), Inic_NwShutdown_Result },
+ { DEC_FKTOP(INIC_FID_MOST_NW_SHUTDOWN, UCS_OP_ERROR), Inic_NwShutdown_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_TRIGGER_RBD, UCS_OP_RESULT), Inic_NwTriggerRbd_Result },
+ { DEC_FKTOP(INIC_FID_MOST_NW_TRIGGER_RBD, UCS_OP_ERROR), Inic_NwTriggerRbd_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_RBD_RESULT, UCS_OP_STATUS), Inic_NwRbdResult_Status },
+ { DEC_FKTOP(INIC_FID_MOST_NW_RBD_RESULT, UCS_OP_ERROR), Inic_NwRbdResult_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_ATTACH, UCS_OP_RESULT), Inic_NwAttach_Result },
+ { DEC_FKTOP(INIC_FID_MOST_NW_ATTACH, UCS_OP_ERROR), Inic_NwAttach_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_FORCE_NO_AVAIL, UCS_OP_STATUS), Inic_NwForceNotAvailable_Status },
+ { DEC_FKTOP(INIC_FID_MOST_NW_FORCE_NO_AVAIL, UCS_OP_ERROR), Inic_NwForceNotAvailable_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_SYS_DIAGNOSIS, UCS_OP_RESULT), Inic_NwSysDiagnosis_Result },
+ { DEC_FKTOP(INIC_FID_MOST_NW_SYS_DIAGNOSIS, UCS_OP_ERROR), Inic_NwSysDiagnosis_Error },
+ { DEC_FKTOP(INIC_FID_MOST_NW_SYS_DIAG_END, UCS_OP_RESULT), Inic_NwSysDiagEnd_Result },
+ { DEC_FKTOP(INIC_FID_MOST_NW_SYS_DIAG_END, UCS_OP_ERROR), Inic_NwSysDiagEnd_Error },
+ { DEC_FKTOP(INIC_FID_BACK_CHANNEL_DIAGNOSIS, UCS_OP_RESULT), Inic_BCDiagnosis_Result },
+ { DEC_FKTOP(INIC_FID_BACK_CHANNEL_DIAGNOSIS, UCS_OP_ERROR), Inic_BCDiagnosis_Error },
+ { DEC_FKTOP(INIC_FID_BACK_CHANNEL_DIAG_END, UCS_OP_RESULT), Inic_BCDiagEnd_Result },
+ { DEC_FKTOP(INIC_FID_BACK_CHANNEL_DIAG_END, UCS_OP_ERROR), Inic_BCDiagEnd_Error },
+ { DEC_FKTOP(INIC_FID_MOST_PORT_STATUS, UCS_OP_STATUS), Inic_MostPortStatus_Status },
+ { DEC_FKTOP(INIC_FID_MOST_PORT_STATUS, UCS_OP_ERROR), Inic_MostPortStatus_Error },
+ { DEC_FKTOP(INIC_FID_MOST_SOCKET_CREATE, UCS_OP_RESULT), Inic_MostSocketCreate_Result },
+ { DEC_FKTOP(INIC_FID_MOST_SOCKET_CREATE, UCS_OP_ERROR), Inic_MostSocketCreate_Error },
+ { DEC_FKTOP(INIC_FID_MOST_SOCKET_STATUS, UCS_OP_STATUS), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_MOST_SOCKET_STATUS, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_MLB_PORT_CREATE, UCS_OP_RESULT), Inic_MlbPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_MLB_PORT_CREATE, UCS_OP_ERROR), Inic_MlbPortCreate_Error },
+/* { DEC_FKTOP(INIC_FID_MLB_PORT_ALLOCATE_ONLY, UCS_OP_RESULT), Inic_DummyHandler }, */
+/* { DEC_FKTOP(INIC_FID_MLB_PORT_ALLOCATE_ONLY, UCS_OP_ERROR), Inic_DummyHandler }, */
+/* { DEC_FKTOP(INIC_FID_MLB_PORT_DEALLOC_ONLY, UCS_OP_RESULT), Inic_DummyHandler }, */
+/* { DEC_FKTOP(INIC_FID_MLB_PORT_DEALLOC_ONLY, UCS_OP_ERROR), Inic_DummyHandler }, */
+ { DEC_FKTOP(INIC_FID_MLB_SOCKET_CREATE, UCS_OP_RESULT), Inic_MlbSocketCreate_Result },
+ { DEC_FKTOP(INIC_FID_MLB_SOCKET_CREATE, UCS_OP_ERROR), Inic_MlbSocketCreate_Error },
+ { DEC_FKTOP(INIC_FID_SPI_PORT_CREATE, UCS_OP_RESULT), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_SPI_PORT_CREATE, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_SPI_SOCKET_CREATE, UCS_OP_RESULT), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_SPI_SOCKET_CREATE, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_USB_PORT_CREATE, UCS_OP_RESULT), Inic_UsbPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_USB_PORT_CREATE, UCS_OP_ERROR), Inic_UsbPortCreate_Error },
+ { DEC_FKTOP(INIC_FID_USB_SOCKET_CREATE, UCS_OP_RESULT), Inic_UsbSocketCreate_Result },
+ { DEC_FKTOP(INIC_FID_USB_SOCKET_CREATE, UCS_OP_ERROR), Inic_UsbSocketCreate_Error },
+ { DEC_FKTOP(INIC_FID_STREAM_PORT_CONFIG, UCS_OP_STATUS), Inic_StreamPortConfig_Status },
+ { DEC_FKTOP(INIC_FID_STREAM_PORT_CONFIG, UCS_OP_ERROR), Inic_StreamPortConfig_Error },
+ { DEC_FKTOP(INIC_FID_STREAM_PORT_CREATE, UCS_OP_RESULT), Inic_StreamPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_STREAM_PORT_CREATE, UCS_OP_ERROR), Inic_StreamPortCreate_Error },
+ { DEC_FKTOP(INIC_FID_STREAM_PORT_LOOPBACK, UCS_OP_STATUS), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_STREAM_PORT_LOOPBACK, UCS_OP_ERROR), Inic_DummyHandler },
+ { DEC_FKTOP(INIC_FID_STREAM_SOCKET_CREATE, UCS_OP_RESULT), Inic_StreamSocketCreate_Result },
+ { DEC_FKTOP(INIC_FID_STREAM_SOCKET_CREATE, UCS_OP_ERROR), Inic_StreamSocketCreate_Error },
+ { DEC_FKTOP(INIC_FID_RMCK_PORT_CREATE, UCS_OP_RESULT), Inic_RmckPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_RMCK_PORT_CREATE, UCS_OP_ERROR), Inic_RmckPortCreate_Error },
+ { DEC_FKTOP(INIC_FID_I2C_PORT_CREATE, UCS_OP_RESULT), Inic_I2cPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_I2C_PORT_CREATE, UCS_OP_ERROR), Inic_I2cPortCreate_Error },
+ { DEC_FKTOP(INIC_FID_I2C_PORT_READ, UCS_OP_RESULT), Inic_I2cPortRead_Result },
+ { DEC_FKTOP(INIC_FID_I2C_PORT_READ, UCS_OP_ERROR), Inic_I2cPortRead_Error },
+ { DEC_FKTOP(INIC_FID_I2C_PORT_WRITE, UCS_OP_RESULT), Inic_I2cPortWrite_Result },
+ { DEC_FKTOP(INIC_FID_I2C_PORT_WRITE, UCS_OP_ERROR), Inic_I2cPortWrite_Error },
+ { DEC_FKTOP(INIC_FID_PCI_PORT_CREATE, UCS_OP_RESULT), Inic_PciPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_PCI_PORT_CREATE, UCS_OP_ERROR), Inic_PciPortCreate_Error },
+ { DEC_FKTOP(INIC_FID_PCI_SOCKET_CREATE, UCS_OP_RESULT), Inic_PciSocketCreate_Result },
+ { DEC_FKTOP(INIC_FID_PCI_SOCKET_CREATE, UCS_OP_ERROR), Inic_PciSocketCreate_Error },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_CREATE, UCS_OP_RESULT), Inic_GpioPortCreate_Result },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_CREATE, UCS_OP_ERROR), Inic_GpioPortCreate_Error },
+ { DEC_FKTOP(INIC_FID_MOST_PORT_ENABLE, UCS_OP_RESULT), Inic_MostPortEnable_Result },
+ { DEC_FKTOP(INIC_FID_MOST_PORT_ENABLE, UCS_OP_ERROR), Inic_MostPortEnable_Error },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_PIN_MODE, UCS_OP_STATUS), Inic_GpioPortPinMode_Status },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_PIN_MODE, UCS_OP_ERROR), Inic_GpioPortPinMode_Error },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_PIN_STATE, UCS_OP_STATUS), Inic_GpioPortPinState_Status },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_PIN_STATE, UCS_OP_ERROR), Inic_GpioPortPinState_Error },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_TRIGGER_EVENT, UCS_OP_STATUS), Inic_GpioPortTrigger_Status },
+ { DEC_FKTOP(INIC_FID_GPIO_PORT_TRIGGER_EVENT, UCS_OP_ERROR), Inic_GpioPortTrigger_Error },
+ { DEC_FKTOP(INIC_FID_RESOURCE_DESTROY, UCS_OP_RESULT), Inic_ResourceDestroy_Result },
+ { DEC_FKTOP(INIC_FID_RESOURCE_DESTROY, UCS_OP_ERROR), Inic_ResourceDestroy_Error },
+ { DEC_FKTOP(INIC_FID_RESOURCE_INVALID_LIST, UCS_OP_STATUS), Inic_ResourceInvalidList_Status },
+ { DEC_FKTOP(INIC_FID_RESOURCE_INVALID_LIST, UCS_OP_ERROR), Inic_ResourceInvalidList_Error },
+ { DEC_FKTOP(INIC_FID_RESOURCE_MONITOR, UCS_OP_STATUS), Inic_ResourceMonitor_Status },
+ { DEC_FKTOP(INIC_FID_RESOURCE_MONITOR, UCS_OP_ERROR), Inic_ResourceMonitor_Error },
+/* { DEC_FKTOP(INIC_FID_PACKET_ATTACH_SOCKETS, UCS_OP_RESULT), Inic_DummyHandler }, */
+/* { DEC_FKTOP(INIC_FID_PACKET_ATTACH_SOCKETS, UCS_OP_ERROR), Inic_DummyHandler }, */
+/* { DEC_FKTOP(INIC_FID_PACKET_DETACH_SOCKETS, UCS_OP_RESULT), Inic_DummyHandler }, */
+/* { DEC_FKTOP(INIC_FID_PACKET_DETACH_SOCKETS, UCS_OP_ERROR), Inic_DummyHandler }, */
+ { DEC_FKTOP(INIC_FID_QOS_CREATE, UCS_OP_RESULT), Inic_QoSCreate_Result },
+ { DEC_FKTOP(INIC_FID_QOS_CREATE, UCS_OP_ERROR), Inic_QoSCreate_Error },
+ { DEC_FKTOP(INIC_FID_AVP_CREATE, UCS_OP_RESULT), Inic_AvpCreate_Result },
+ { DEC_FKTOP(INIC_FID_AVP_CREATE, UCS_OP_ERROR), Inic_AvpCreate_Error },
+ { DEC_FKTOP(INIC_FID_SYNC_CREATE, UCS_OP_RESULT), Inic_SyncCreate_Result },
+ { DEC_FKTOP(INIC_FID_SYNC_CREATE, UCS_OP_ERROR), Inic_SyncCreate_Error },
+ { DEC_FKTOP(INIC_FID_SYNC_MUTE, UCS_OP_RESULT), Inic_SyncMute_Result },
+ { DEC_FKTOP(INIC_FID_SYNC_MUTE, UCS_OP_ERROR), Inic_SyncMute_Error },
+ { DEC_FKTOP(INIC_FID_SYNC_DEMUTE, UCS_OP_RESULT), Inic_SyncDemute_Result },
+ { DEC_FKTOP(INIC_FID_SYNC_DEMUTE, UCS_OP_ERROR), Inic_SyncDemute_Error },
+ { DEC_FKTOP(INIC_FID_DFIPHASE_CREATE, UCS_OP_RESULT), Inic_DfiPhaseCreate_Result },
+ { DEC_FKTOP(INIC_FID_DFIPHASE_CREATE, UCS_OP_ERROR), Inic_DfiPhaseCreate_Error },
+ { DEC_FKTOP(INIC_FID_IPC_CREATE, UCS_OP_RESULT), Inic_IpcCreate_Result },
+ { DEC_FKTOP(INIC_FID_IPC_CREATE, UCS_OP_ERROR), Inic_IpcCreate_Error },
+ { DEC_FKTOP(INIC_FID_COMBINER_CREATE, UCS_OP_RESULT), Inic_CombinerCreate_Result },
+ { DEC_FKTOP(INIC_FID_COMBINER_CREATE, UCS_OP_ERROR), Inic_CombinerCreate_Error },
+ { DEC_FKTOP(INIC_FID_SPLITTER_CREATE, UCS_OP_RESULT), Inic_SplitterCreate_Result },
+ { DEC_FKTOP(INIC_FID_SPLITTER_CREATE, UCS_OP_ERROR), Inic_SplitterCreate_Error },
+ { DEC_FKTOP_TERMINATION, NULL }
+};
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal definitions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Bitmask for API method Inic_NwForceNotAvailable() used by API locking manager */
+#define INIC_API_NW_FORCE_NA 0x01U
+/*! \brief Bitmask for API method Inic_NwShutdown() used by API locking manager */
+#define INIC_API_NW_SHUTDOWN 0x02U
+/*! \brief Bitmask for API method Inic_NwFrameCounter_Get() used by API locking manager */
+#define INIC_API_NW_FRAME_COUNTER 0x04U
+/*! \brief Bitmask for API method Inic_NwTriggerRbd() used by API locking manager */
+#define INIC_API_NW_TRIGGER_RBD 0x08U
+/*! \brief Bitmask for API method Inic_NwRbdResult_Get() used by API locking manager */
+#define INIC_API_NW_RBD_RESULT 0x10U
+/*! \brief Bitmask for API method Inic_DeviceVersion_Get() used by API locking manager */
+#define INIC_API_DEVICE_VERSION_GET 0x20U
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Inic_HandleInternalErrors(void *self, void *error_code_ptr);
+static void Inic_HandleApiTimeout(void *self, void *method_mask_ptr);
+static void Inic_DecodeIcm(CInic *self, Msg_MostTel_t *msg_ptr);
+static void Inic_MsgTxStatusCb(void *self, Msg_MostTel_t *tel_ptr, Ucs_MsgTxStatus_t status);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of class CInic.
+ * \param self Reference to CInic instance
+ * \param init_ptr Reference to initialization data
+ */
+void Inic_Ctor(CInic *self, Inic_InitData_t *init_ptr)
+{
+ uint8_t i;
+ MISC_MEM_SET((void *)self, 0, sizeof(*self));
+
+ self->base_ptr = init_ptr->base_ptr;
+ self->xcvr_ptr = init_ptr->xcvr_ptr;
+ self->fkt_op_list_ptr = &inic_handler[0];
+ self->target_address = init_ptr->tgt_addr;
+
+ /* create instances of single-observers */
+ for(i=0U; i<INIC_NUM_SSUB; i++)
+ {
+ Ssub_Ctor(&self->ssubs[i], self->base_ptr->ucs_user_ptr);
+ }
+
+ /* create instances of "normal" observers */
+ for(i=0U; i<INIC_NUM_SUB; i++)
+ {
+ Sub_Ctor(&self->subs[i], self->base_ptr->ucs_user_ptr);
+ }
+
+ /* Observe internal errors and events */
+ Mobs_Ctor(&self->internal_error_obs, self, EH_M_TERMINATION_EVENTS, &Inic_HandleInternalErrors);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->internal_error_obs);
+
+ /* Initialize API locking mechanism */
+ Sobs_Ctor(&self->lock.observer, self, &Inic_HandleApiTimeout);
+ Al_Ctor(&self->lock.api, &self->lock.observer, self->base_ptr->ucs_user_ptr);
+ Alm_RegisterApi(&self->base_ptr->alm, &self->lock.api);
+
+ /* Initialize Resource Management part */
+ Inic_InitResourceManagement(self);
+}
+
+/*! \brief Handles internal errors and events
+ * \param self Instance pointer
+ * \param error_code_ptr Reference to reported error code
+ */
+static void Inic_HandleInternalErrors(void *self, void *error_code_ptr)
+{
+ uint8_t i;
+ Inic_StdResult_t res_data;
+ CInic *self_ = (CInic *)self;
+ MISC_UNUSED(error_code_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_ERR_SYSTEM;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+
+ /* Internal error has been occurred => Cancel running jobs */
+ for(i=0U; i<INIC_NUM_SSUB; i++)
+ {
+ Ssub_Notify(&self_->ssubs[i], &res_data, true);
+ }
+}
+
+/*! \brief Handles an API timeout
+ * \param self Instance pointer
+ * \param method_mask_ptr Bitmask to signal which API method has caused the timeout
+ */
+static void Inic_HandleApiTimeout(void *self, void *method_mask_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Alm_ModuleMask_t method_mask = *((Alm_ModuleMask_t *)method_mask_ptr);
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_ERR_TIMEOUT;
+ res_data.result.info_ptr = NULL;
+
+ switch(method_mask)
+ {
+ case INIC_API_NW_SHUTDOWN:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SHUTDOWN], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC]", "API locking timeout occurred for method Inic_NwShutdown().", 0U));
+ break;
+ case INIC_API_NW_FRAME_COUNTER:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_FRAME_COUNTER], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC]", "API locking timeout occurred for method Inic_NwFrameCounter_Get().", 0U));
+ break;
+ case INIC_API_NW_TRIGGER_RBD:
+ self_->lock.rbd_trigger_timeout_counter++;
+ if(self_->lock.rbd_trigger_timeout_counter < 5U)
+ {
+ (void)Al_Lock(&self_->lock.api, INIC_API_NW_TRIGGER_RBD);
+ }
+ else
+ {
+ Inic_StdResult_t rbd_result_data;
+ Ucs_StdResult_t result = {UCS_RES_ERR_TIMEOUT, NULL, 0U};
+ rbd_result_data.data_info = NULL;
+ rbd_result_data.result = result;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_TRIGGER_RBD], &rbd_result_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC]", "API locking timeout occurred for method Inic_NwTriggerRbd().", 0U));
+ }
+ break;
+ case INIC_API_NW_RBD_RESULT:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_RBD_RESULT], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC]", "API locking timeout occurred for method Inic_NwRbdResult_Get().", 0U));
+ break;
+ default:
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC]", "Unknown API locking bitmask detected. Mask: 0x%02X", 1U, method_mask));
+ break;
+ }
+}
+
+/*! \brief Decode an ICM message
+ * \param self Instance pointer to FBlock INIC
+ * \param msg_ptr pointer to the ICM message to decode
+ */
+static void Inic_DecodeIcm(CInic *self, Msg_MostTel_t *msg_ptr)
+{
+ Dec_Return_t result;
+ uint16_t index;
+
+ result = Dec_SearchFktOpIcm(self->fkt_op_list_ptr, &index, msg_ptr->id.function_id, msg_ptr->id.op_type);
+
+ if (result == DEC_RET_SUCCESS)
+ {
+ self->fkt_op_list_ptr[index].handler_function_ptr(self, msg_ptr);
+ }
+ else
+ {
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[INIC]", "Unknown ICM received. FBlockId: 0x%02X, InstId: 0x%02X, FktId: 0x%04X, OPType: 0x%02X", 4U, msg_ptr->id.fblock_id, msg_ptr->id.instance_id, msg_ptr->id.function_id, msg_ptr->id.op_type));
+ }
+}
+
+/*! \brief Receives ICMs
+ * \param self reference to INIC object
+ * \param tel_ptr received message
+ */
+void Inic_OnIcmRx(void *self, Msg_MostTel_t *tel_ptr)
+{
+ CInic *self_ = (CInic *)self;
+
+ if ((tel_ptr->source_addr == MSG_ADDR_INIC) && (tel_ptr->destination_addr == MSG_ADDR_EHC_CFG))
+ {
+ Inic_DecodeIcm(self_, tel_ptr);
+ }
+
+ Trcv_RxReleaseMsg(self_->xcvr_ptr, tel_ptr); /* free Rx telegram */
+}
+
+/*! \brief Filters RCM Rx messages
+ * \details The filter function shall not release the message object
+ * \param self Reference to INIC object
+ * \param tel_ptr Reference to the RCM Rx message object
+ */
+void Inic_OnRcmRxFilter(void *self, Msg_MostTel_t *tel_ptr)
+{
+ uint16_t index;
+ CInic *self_ = (CInic *)self;
+
+ if (Dec_SearchFktOpIcm(self_->fkt_op_list_ptr, &index, tel_ptr->id.function_id, tel_ptr->id.op_type) == DEC_RET_SUCCESS)
+ {
+ self_->fkt_op_list_ptr[index].handler_function_ptr(self, tel_ptr);
+ }
+}
+
+/*! \brief Handle message Tx status and free message objects
+ * \param self The instance
+ * \param tel_ptr Reference to transmitted message
+ * \param status Status of the transmitted message
+ */
+static void Inic_MsgTxStatusCb(void *self, Msg_MostTel_t *tel_ptr, Ucs_MsgTxStatus_t status)
+{
+ CInic *self_ = (CInic *)self;
+
+ if ((status != UCS_MSG_STAT_OK) && (tel_ptr->info_ptr != NULL))
+ {
+ Inic_StdResult_t res_data;
+ CSingleSubject *ssub_ptr = (CSingleSubject *)tel_ptr->info_ptr;
+
+ res_data.data_info = &status;
+ res_data.result.code = UCS_RES_ERR_TRANSMISSION;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+ Ssub_Notify(ssub_ptr, &res_data, true);
+ }
+ Trcv_TxReleaseMsg(tel_ptr);
+
+ /* ICM messages pending? */
+ if (Sub_GetNumObservers(&self_->subs[INIC_SUB_TX_MSG_OBJ_AVAIL]) > 0U)
+ {
+ Sub_Notify(&self_->subs[INIC_SUB_TX_MSG_OBJ_AVAIL], NULL);
+ }
+}
+
+/*! \brief Add an observer to be notified when a tx message object is available
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be informed
+ */
+void Inic_AddObsrvOnTxMsgObjAvail(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_AddObserver(&self->subs[INIC_SUB_TX_MSG_OBJ_AVAIL], obs_ptr);
+}
+
+/*! \brief Delete an observer set by Inic_AddObsrvOnTxMsgObjAvail()
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be removed
+ */
+void Inic_DelObsrvOnTxMsgObjAvail(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_TX_MSG_OBJ_AVAIL], obs_ptr);
+}
+
+/*! \brief Add an observer to the NetworkStatus subject
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be informed
+ */
+void Inic_AddObsrvNwStatus(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_AddObserver(&self->subs[INIC_SUB_NW_STATUS], obs_ptr);
+}
+
+/*! \brief Delete an observer to the NetworkStatus subject
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be removed
+ */
+void Inic_DelObsrvNwStatus(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_NW_STATUS], obs_ptr);
+}
+
+/*! \brief Add an observer to the NetworkConfiguration subject
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be informed
+ */
+void Inic_AddObsvrNwConfig(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_AddObserver(&self->subs[INIC_SUB_NW_CONFIG], obs_ptr);
+}
+
+/*! \brief Delete an observer to the NetworkConfiguration subject
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be removed
+ */
+void Inic_DelObsvrNwConfig(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_NW_CONFIG], obs_ptr);
+}
+
+/*! \brief Add an observer to the DeviceStatus subject
+ * \details The provided data points to a \ref Inic_DeviceStatus_t structure
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be informed
+ */
+void Inic_AddObsvrDeviceStatus(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_AddObserver(&self->subs[INIC_SUB_DEVICE_STATUS], obs_ptr);
+}
+
+/*! \brief Delete an observer to the DeviceStatus subject
+ * \param self instance of CInic
+ * \param obs_ptr pointer to observer to be removed
+ */
+void Inic_DelObsvrDeviceStatus(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_DEVICE_STATUS], obs_ptr);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal API */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief This method requests the INIC version info
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_DeviceVersion_Get(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, INIC_API_DEVICE_VERSION_GET) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_DEVICE_VERSION;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_DEVICE_VERSION];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_DEVICE_VERSION], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, INIC_API_DEVICE_VERSION_GET);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Attach EHC to the INIC
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_DeviceAttach(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_DEVICE_ATTACH;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_DEVICE_ATTACH];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_DEVICE_ATTACH], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Attaches the given PMS channel to the network
+ * \param self Reference to CInic instance
+ * \param pmp_channel_handle Port message channel resource handle
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_NwAttach(CInic *self,
+ uint16_t pmp_channel_handle,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_ATTACH;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(pmp_channel_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(pmp_channel_handle);
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_ATTACH];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_ATTACH], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief Starts the System diagnosis
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_NwSysDiagnosis(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_SYS_DIAGNOSIS;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_SYS_DIAGNOSIS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_SYS_DIAGNOSIS], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Stops the System diagnosis
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_NwSysDiagEnd(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_SYS_DIAG_END;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_SYS_DIAGEND];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_SYS_DIAGEND], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Starts the Backchannel Diagnosis Mode
+ *
+ * \param *self Reference to CInic instance
+ * \param *obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_BCDiagnosis(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = MSG_ADDR_INIC;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_BACK_CHANNEL_DIAGNOSIS;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_BC_DIAGNOSIS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_BC_DIAGNOSIS], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Stops the Backchannel Diagnosis Mode
+ *
+ * \param *self Reference to CInic instance
+ * \param *obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_BCDiagEnd(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = MSG_ADDR_INIC;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_BACK_CHANNEL_DIAG_END;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_BC_DIAG_END];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_BC_DIAG_END], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+
+/*! \brief Requests the INIC.MOSTNetworRBDResult.Status message
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_NwRbdResult_Get(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, INIC_API_NW_RBD_RESULT) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_RBD_RESULT;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_RBD_RESULT];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_RBD_RESULT], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, INIC_API_NW_RBD_RESULT);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+/*! \brief This functions starts up the MOST network.
+ * \param self Reference to CInic instance
+ * \param auto_forced_na The delay time to shutdown the network after INIC has entered the
+ * protected mode.
+ * \param packet_bandwidth The desired packed bandwidth
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_NwStartup(CInic *self, uint16_t auto_forced_na,
+ uint16_t packet_bandwidth, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if (self->startup_locked == false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ self->startup_locked = true;
+
+ msg_ptr->destination_addr = self->target_address;
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_STARTUP;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(auto_forced_na);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(auto_forced_na);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(packet_bandwidth);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(packet_bandwidth);
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_STARTUP];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_STARTUP], obs_ptr);
+ }
+ else
+ {
+ self->startup_locked = false;
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function shuts down the entire MOST network.
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_NwShutdown(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, INIC_API_NW_SHUTDOWN) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_SHUTDOWN;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_SHUTDOWN];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_SHUTDOWN], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, INIC_API_NW_SHUTDOWN);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function triggers the Ring Break Diagnosis.
+ * \param self Reference to CInic instance
+ * \param type Specifies if the INIC starts the RBD as a TimingMaster or TimingSlave.
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_NwTriggerRbd(CInic *self, Ucs_Diag_RbdType_t type, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, INIC_API_NW_TRIGGER_RBD) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ self->lock.rbd_trigger_timeout_counter = 0U;
+
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_TRIGGER_RBD;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = (uint8_t)type;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_TRIGGER_RBD];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_TRIGGER_RBD], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, INIC_API_NW_TRIGGER_RBD);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function triggers the INIC to force the NotAvailable state
+ * \param self Reference to CInic instance
+ * \param force Is \c true if the INIC shall force the network in NotAvailable state.
+ * If \c false the INIC shall no no longer force the network to NotAvailable state.
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_NwForceNotAvailable(CInic *self, bool force, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if (Al_Lock(&self->lock.api, INIC_API_NW_FORCE_NA) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_FORCE_NO_AVAIL;
+ msg_ptr->id.op_type = UCS_OP_SETGET;
+
+ if (force == false)
+ {
+ msg_ptr->tel.tel_data_ptr[0] = 0U;
+ }
+ else
+ {
+ msg_ptr->tel.tel_data_ptr[0] = 1U;
+ }
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_FORCE_NA];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_FORCE_NA], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, INIC_API_NW_FORCE_NA);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function modifies the INIC network configuration.
+ * \param self Reference to CInic instance
+ * \param mask Allows to change a single, multiple, or all parameters
+ * \param config Holds the parameter values
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_NwConfig_SetGet(CInic *self, uint16_t mask, Inic_NetworkConfig_t config,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 24U);
+
+ if (msg_ptr != NULL)
+ {
+ mask = mask & 7U; /* allow only bit 0..2 */
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_CFG;
+ msg_ptr->id.op_type = UCS_OP_SETGET;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(mask);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(mask);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(config.node_address);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(config.node_address);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(config.group_address);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(config.group_address);
+ msg_ptr->tel.tel_data_ptr[6] = config.llrbc;
+ MISC_MEM_SET(&msg_ptr->tel.tel_data_ptr[7], 0, 17U);
+
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_CONFIG], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Requests the INIC.NetworkConfiguration.Status message
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_NwConfig_Get(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_CFG;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ Trcv_TxSendMsg(self->xcvr_ptr, msg_ptr);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_CONFIG], obs_ptr);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Requests the INIC.MOSTNetworkFrameCounter.Status message
+ * \param self Reference to CInic instance
+ * \param reference Reference counter value
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_NwFrameCounter_Get(CInic *self, uint32_t reference, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.api, INIC_API_NW_FRAME_COUNTER) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_NW_FRAME_COUNTER;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->tel.tel_data_ptr[0] = (uint8_t)(reference >> 24);
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)(reference >> 16);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)(reference >> 8);
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)reference;
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NW_FRAME_COUNTER];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_MsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NW_FRAME_COUNTER], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.api, INIC_API_NW_FRAME_COUNTER);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Handler functions */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Dummy handler function for unused INIC functions
+ *
+ * \param self instance of CInic
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_DummyHandler(void *self, Msg_MostTel_t *msg_ptr)
+{
+ MISC_UNUSED(self);
+ MISC_UNUSED(msg_ptr);
+}
+
+/*! \brief Handler function for INIC.DeviceStatus.Status
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_DeviceStatus_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ TR_ASSERT(self_->base_ptr->ucs_user_ptr, "[INIC]", (msg_ptr->tel.tel_len == 5U));
+ self_->device_status.config_iface_state= (Inic_AttachState_t)msg_ptr->tel.tel_data_ptr[0];
+ self_->device_status.app_iface_state = (Inic_AttachState_t)msg_ptr->tel.tel_data_ptr[1];
+ self_->device_status.power_state = (Ucs_Inic_PowerState_t)msg_ptr->tel.tel_data_ptr[2];
+ self_->device_status.bist = (Inic_Bist_t)msg_ptr->tel.tel_data_ptr[3];
+ self_->device_status.last_reset_reason = (Ucs_Inic_LastResetReason_t)msg_ptr->tel.tel_data_ptr[4];
+
+ /* INIC BIST error detected */
+ if (self_->device_status.bist == INIC_BIST_ERROR)
+ {
+ Eh_ReportEvent(&self_->base_ptr->eh, EH_E_BIST_FAILED);
+ }
+
+ Sub_Notify(&self_->subs[INIC_SUB_DEVICE_STATUS], &self_->device_status);
+ }
+}
+
+/*! \brief Handler function for INIC.DeviceVersion.Status
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_DeviceVersion_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ MISC_DECODE_DWORD(&(self_->device_version.product_identifier), &(msg_ptr->tel.tel_data_ptr[0]));
+ self_->device_version.major_version = msg_ptr->tel.tel_data_ptr[4];
+ self_->device_version.minor_version = msg_ptr->tel.tel_data_ptr[5];
+ self_->device_version.release_version = msg_ptr->tel.tel_data_ptr[6];
+ MISC_DECODE_DWORD(&(self_->device_version.build_version), &(msg_ptr->tel.tel_data_ptr[7]));
+ self_->device_version.hw_revision = msg_ptr->tel.tel_data_ptr[11];
+ MISC_DECODE_WORD(&(self_->device_version.diagnosis_id), &(msg_ptr->tel.tel_data_ptr[12]));
+
+ TR_ASSERT(self_->base_ptr->ucs_user_ptr, "[INIC]", (msg_ptr->tel.tel_data_ptr[14] == 0x01U)); /* ExtIdentifier == CFGS ? */
+
+ self_->device_version.cs_major_version = msg_ptr->tel.tel_data_ptr[15];
+ self_->device_version.cs_minor_version = msg_ptr->tel.tel_data_ptr[16];
+ self_->device_version.cs_release_version = msg_ptr->tel.tel_data_ptr[17];
+
+
+ res_data.data_info = &self_->device_version;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_VERSION], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_DEVICE_VERSION_GET);
+}
+
+/*! \brief Handler function for INIC.DeviceVersion.Error
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_DeviceVersion_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_VERSION], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_DEVICE_VERSION_GET);
+}
+
+/*! \brief Handler function for INIC.NetworkStatus.Status
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwStatus_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = &self_->network_status;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ TR_ASSERT(self_->base_ptr->ucs_user_ptr, "[INIC]", (msg_ptr->tel.tel_len == 11U));
+ MISC_DECODE_WORD(&(self_->network_status.events), &(msg_ptr->tel.tel_data_ptr[0]));
+ self_->network_status.availability = (Ucs_Network_Availability_t)msg_ptr->tel.tel_data_ptr[2];
+ self_->network_status.avail_info = (Ucs_Network_AvailInfo_t)msg_ptr->tel.tel_data_ptr[3];
+ self_->network_status.avail_trans_cause = (Ucs_Network_AvailTransCause_t)msg_ptr->tel.tel_data_ptr[4];
+ MISC_DECODE_WORD(&(self_->network_status.node_address), &(msg_ptr->tel.tel_data_ptr[5]));
+ self_->network_status.node_position = msg_ptr->tel.tel_data_ptr[7];
+ self_->network_status.max_position = msg_ptr->tel.tel_data_ptr[8];
+ MISC_DECODE_WORD(&(self_->network_status.packet_bw), &(msg_ptr->tel.tel_data_ptr[9]));
+
+ Sub_Notify(&self_->subs[INIC_SUB_NW_STATUS], &res_data);
+ }
+}
+
+/*! \brief Handler function for INIC.NetworkConfiguration.Status
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwConfig_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 4U)
+ {
+ res_data.data_info = &self_->network_config;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&(self_->network_config.node_address), &(msg_ptr->tel.tel_data_ptr[0]));
+ MISC_DECODE_WORD(&(self_->network_config.group_address), &(msg_ptr->tel.tel_data_ptr[2]));
+ self_->network_config.llrbc = msg_ptr->tel.tel_data_ptr[4];
+
+ Sub_Notify(&self_->subs[INIC_SUB_NW_CONFIG], &res_data);
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_CONFIG], &res_data, true);
+ }
+}
+
+/*! \brief Handler function for INIC.NetworkConfiguration.Error
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwConfig_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_CONFIG], &res_data, true);
+ }
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkFrameCounter.Status
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwFrameCounter_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ Inic_FrameCounterStatus_t frame_counter_status;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ MISC_DECODE_DWORD(&frame_counter_status.reference, &(msg_ptr->tel.tel_data_ptr[0]));
+ MISC_DECODE_DWORD(&frame_counter_status.frame_counter, &(msg_ptr->tel.tel_data_ptr[4]));
+ frame_counter_status.lock = msg_ptr->tel.tel_data_ptr[8];
+ res_data.data_info = &frame_counter_status;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_FRAME_COUNTER], &res_data, true); /* provides pointer to Inic_StdResult_t structure */
+ }
+ Al_Release(&self_->lock.api, INIC_SSUB_NW_FRAME_COUNTER);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkFrameCounter.Error
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwFrameCounter_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_FRAME_COUNTER], &res_data, true); /* provides pointer to Inic_StdResult_t structure */
+ }
+ Al_Release(&self_->lock.api, INIC_SSUB_NW_FRAME_COUNTER);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkStartup.ErrorAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwStartup_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ self_->startup_locked = false;
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_STARTUP], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkStartup.ResultAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwStartup_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ self_->startup_locked = false;
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_STARTUP], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkShutdown.ErrorAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwShutdown_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SHUTDOWN], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_NW_SHUTDOWN);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkShutdown.ResultAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwShutdown_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SHUTDOWN], &res_data, true);
+ Al_Release(&self_->lock.api, INIC_API_NW_SHUTDOWN);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkTriggerRBD.ErrorAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwTriggerRbd_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_TRIGGER_RBD], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_NW_TRIGGER_RBD);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkTriggerRBD.ResultAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwTriggerRbd_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_TRIGGER_RBD], &res_data, true);
+ Al_Release(&self_->lock.api, INIC_API_NW_TRIGGER_RBD);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkForceNotAvailable.ErrorAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwForceNotAvailable_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_FORCE_NA], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_NW_FORCE_NA);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkForceNotAvailable.ResultAck
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_NwForceNotAvailable_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_FORCE_NA], &res_data, true);
+ Al_Release(&self_->lock.api, INIC_API_NW_FORCE_NA);
+}
+
+/*! \brief Handler function for INIC.DeviceAttach.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_DeviceAttach_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_ATTACH], &res_data, true);
+ }
+}
+
+/*! \brief Handler function for INIC.DeviceAttach.Result
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_DeviceAttach_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_ATTACH], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.NetworkAttach.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwAttach_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_ATTACH], &res_data, true);
+ }
+}
+
+/*! \brief Handler function for INIC.NetworkAttach.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwAttach_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_ATTACH], &res_data, true);
+}
+
+
+
+
+/*! \brief Handler function for INIC.MOSTNetworkSystemDiagnosis.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwSysDiagnosis_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SYS_DIAGNOSIS], &res_data, true);
+ }
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkSystemDiagnosis.Result
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwSysDiagnosis_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SYS_DIAGNOSIS], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkSystemDiagnosisEnd.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwSysDiagEnd_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SYS_DIAGEND], &res_data, true);
+ }
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkSystemDiagnosisEnd.Result
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwSysDiagEnd_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_SYS_DIAGEND], &res_data, true);
+}
+
+
+
+/*! \brief Handler function for INIC.BCDiag.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_BCDiagnosis_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_BC_DIAGNOSIS], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.BCDiag.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_BCDiagnosis_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_BC_DIAGNOSIS], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.BCDiagEnd.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_BCDiagEnd_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_BC_DIAG_END], &res_data, true);
+}
+
+/*! \brief Handler function for INIC.BCDiagEnd.Result
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_BCDiagEnd_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_BC_DIAG_END], &res_data, true);
+}
+
+
+
+
+
+/*! \brief Handler function for INIC.MOSTNetworkRBDResult.Status
+ * \param self Reference to INIC object
+ * \param msg_ptr Received message
+ */
+void Inic_NwRbdResult_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_RbdResult_t rbd_result_data;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ rbd_result_data.result = (Ucs_Diag_RbdResult_t)msg_ptr->tel.tel_data_ptr[0];
+ rbd_result_data.position = msg_ptr->tel.tel_data_ptr[1];
+ rbd_result_data.status = msg_ptr->tel.tel_data_ptr[2];
+ MISC_DECODE_WORD(&(rbd_result_data.diag_id), &(msg_ptr->tel.tel_data_ptr[3]));
+ res_data.data_info = &rbd_result_data;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_RBD_RESULT], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_NW_RBD_RESULT);
+}
+
+/*! \brief Handler function for INIC.MOSTNetworkRBDResult.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_NwRbdResult_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ if (msg_ptr->tel.tel_len > 0U)
+ {
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NW_RBD_RESULT], &res_data, true);
+ }
+ Al_Release(&self_->lock.api, INIC_API_NW_RBD_RESULT);
+}
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Helper functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Translates INIC error codes into UNICENS error codes and wraps the raw INIC
+ * error data to a byte stream.
+ * \param self Instance of CInic
+ * \param error_data[] INIC error data
+ * \param error_size Size of INIC error data in bytes
+ * \return The formatted error
+ */
+Ucs_StdResult_t Inic_TranslateError(CInic *self, uint8_t error_data[], uint8_t error_size)
+{
+ Ucs_StdResult_t ret_val;
+ MISC_UNUSED(self);
+
+ if(error_data[0] != 0x20U)
+ {
+ ret_val.code = UCS_RES_ERR_MOST_STANDARD;
+ }
+ else
+ {
+ ret_val.code = (Ucs_Result_t)(error_data[1] + 1U);
+ }
+
+ ret_val.info_ptr = &error_data[0];
+ ret_val.info_size = error_size;
+
+ return ret_val;
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Synchronous Getters */
+/*------------------------------------------------------------------------------------------------*/
+uint16_t Inic_GetGroupAddress(CInic *self)
+{
+ return self->network_config.group_address;
+}
+
+uint16_t Inic_GetPacketDataBandwidth(CInic *self)
+{
+ return self->network_status.packet_bw;
+}
+
+uint16_t Inic_GetNodeAddress(CInic *self)
+{
+ return self->network_status.node_address;
+}
+
+uint8_t Inic_GetNodePosition(CInic *self)
+{
+ return self->network_status.node_position;
+}
+
+uint8_t Inic_GetNumberOfNodes(CInic *self)
+{
+ return self->network_status.max_position;
+}
+
+uint8_t Inic_GetInicLlrbc(CInic *self)
+{
+ return self->network_config.llrbc;
+}
+
+Ucs_Inic_Version_t Inic_GetDeviceVersion(CInic *self)
+{
+ return self->device_version;
+}
+
+Ucs_Inic_LastResetReason_t Inic_GetLastResetReason(CInic *self)
+{
+ return self->device_status.last_reset_reason;
+}
+
+Ucs_Inic_PowerState_t Inic_GetDevicePowerState(CInic *self)
+{
+ return self->device_status.power_state;
+}
+
+Ucs_Network_Availability_t Inic_GetAvailability(CInic *self)
+{
+ return self->network_status.availability;
+}
+
+uint16_t Inic_GetTargetAddress (CInic *self)
+{
+ return self->target_address;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_inic_res.c b/ucs2-lib/src/ucs_inic_res.c
new file mode 100644
index 0000000..b9ab104
--- /dev/null
+++ b/ucs2-lib/src/ucs_inic_res.c
@@ -0,0 +1,3735 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of FBlock INIC (resource management parts of INIC management)
+ * \details Contains the resource management parts of INIC management
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_INIC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_misc.h"
+#include "ucs_ret_pb.h"
+#include "ucs_inic.h"
+#include "ucs_base.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal macros */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief API locking Bitmask for all INIC create methods. */
+#define INIC_API_CREATE_CLASS 0x0001U
+/*! \brief API locking Bitmask of method Inic_ResourceDestroy(). */
+#define INIC_API_RESOURCE_DESTROY 0x0002U
+/*! \brief API locking Bitmask of method Inic_ResourceInvalidList_Get(). */
+#define INIC_API_RESOURCE_INVAL_LIST 0x0004U
+/*! \brief API locking Bitmask of method Inic_Notification_Set(). */
+#define INIC_API_NOTIFICATION 0x0008U
+/*! \brief API locking Bitmask of method Inic_StreamPortConfig_Get(). */
+#define INIC_API_STREAM_PORT_CONFIG 0x0010U
+/*! \brief API locking Bitmask of method Inic_SyncMute(). */
+#define INIC_API_SYNC_MUTE 0x0020U
+/*! \brief API locking Bitmask of method Inic_SyncDemute(). */
+#define INIC_API_SYNC_DEMUTE 0x0040U
+/*! \brief API locking Bitmask of method Inic_MostPortEnable(). */
+#define INIC_API_MOST_PORT_ENABLE 0x0080U
+/*! \brief API locking Bitmask of method Inic_MostPortEnFullStr(). */
+#define INIC_API_MOST_PORT_EN_FULL_STR 0x0100U
+/*! \brief API locking Bitmask of method Inic_GpioPortPinMode_SetGet(). */
+#define INIC_API_GPIO_PIN_MODE 0x0200U
+/*! \brief API locking Bitmask of method Inic_GpioPortPinState_SetGet(). */
+#define INIC_API_GPIO_PIN_STATE 0x0400U
+/*! \brief API locking Bitmask of methods Inic_I2cPortRead_StartResultAck() and Inic_I2cPortWrite_StartResultAck(). */
+#define INIC_API_I2C_PORT_WR 0x0800U
+/*! \brief Bitmask for API method Inic_DeviceSync() used by API locking manager */
+#define INIC_API_DEVICE_SYNC 0x1000U
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Inic_HandleResApiTimeout(void *self, void *method_mask_ptr);
+static void Inic_ResMsgTxStatusCb(void *self, Msg_MostTel_t *tel_ptr, Ucs_MsgTxStatus_t status);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Initialization function of the INIC Resource Management part. Called by Inic_Ctor().
+ * \param self Instance pointer
+ */
+void Inic_InitResourceManagement(CInic *self)
+{
+ Sobs_Ctor(&self->lock.res_observer, self, &Inic_HandleResApiTimeout);
+ Al_Ctor(&self->lock.res_api, &self->lock.res_observer, self->base_ptr->ucs_user_ptr);
+ Alm_RegisterApi(&self->base_ptr->alm, &self->lock.res_api);
+
+ /* initializes the gpio report time status */
+ self->gpio_rt_status.first_report = true;
+}
+
+/*! \brief Handles an API timeout
+ * \param self Instance pointer
+ * \param method_mask_ptr Bitmask to signal which API method has caused the timeout
+ */
+static void Inic_HandleResApiTimeout(void *self, void *method_mask_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Alm_ModuleMask_t method_mask = *((Alm_ModuleMask_t *)method_mask_ptr);
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_ERR_TIMEOUT;
+ res_data.result.info_ptr = NULL;
+
+ switch(method_mask)
+ {
+ case INIC_API_CREATE_CLASS:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for INIC create method.", 0U));
+ break;
+ case INIC_API_RESOURCE_DESTROY:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_RESOURCE_DESTROY], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_ResourceDestroy().", 0U));
+ break;
+ case INIC_API_RESOURCE_INVAL_LIST:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_RESOURCE_INVAL_LIST], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_ResourceInvalidList_Get().", 0U));
+ break;
+ case INIC_API_NOTIFICATION:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NOTIFICATION], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_Notification_Get().", 0U));
+ break;
+ case INIC_API_STREAM_PORT_CONFIG:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_STREAM_PORT_CONFIG], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_StreamPortConfig_Get().", 0U));
+ break;
+ case INIC_API_SYNC_MUTE:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_SYNC_MUTE], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_SyncMute().", 0U));
+ break;
+ case INIC_API_SYNC_DEMUTE:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_SYNC_DEMUTE], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_SyncDemute().", 0U));
+ break;
+ case INIC_API_MOST_PORT_ENABLE:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_MOST_PORT_ENABLE], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_MostPortEnable().", 0U));
+ break;
+ case INIC_API_MOST_PORT_EN_FULL_STR:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_MOST_PORT_EN_FULL_STR], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_MostPortEnFullStr().", 0U));
+ break;
+ case INIC_API_GPIO_PIN_MODE:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_GPIO_PIN_MODE], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_GpioPortPinMode_SetGet().", 0U));
+ break;
+ case INIC_API_GPIO_PIN_STATE:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_GPIO_PIN_STATE], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_GpioPortPinState_SetGet().", 0U));
+ break;
+ case INIC_API_DEVICE_SYNC:
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_SYNC], &res_data, true);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "API locking timeout occurred for method Inic_DeviceSync_StartResult().", 0U));
+ break;
+ default:
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[INIC_RES]", "Unknown API locking bitmask detected. Mask: 0x%02X", 1U, method_mask));
+ break;
+ }
+}
+
+/*! \brief Add an observer to the ResourceMonitor subject
+ * \param self Instance of CInic
+ * \param obs_ptr Pointer to observer to be informed
+ */
+void Inic_AddObsrvResMonitor(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_AddObserver(&self->subs[INIC_SUB_RES_MONITOR], obs_ptr);
+}
+
+/*! \brief Delete an observer from the ResourceMonitor subject
+ * \param self Instance of CInic
+ * \param obs_ptr Pointer to observer to be informed
+ */
+void Inic_DelObsrvResMonitor(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_RES_MONITOR], obs_ptr);
+}
+
+/*! \brief Add an observer to the MOSTPortStatus subject
+ * \param self Instance of CInic
+ * \param obs_ptr Pointer to observer to be informed
+ */
+void Inic_AddObsrvMostPortStatus(CInic *self, CObserver *obs_ptr)
+{
+ if (Sub_AddObserver(&self->subs[INIC_SUB_MOST_PORT_STATUS], obs_ptr) != SUB_UNKNOWN_OBSERVER)
+ {
+ Sub_Notify(&self->subs[INIC_SUB_MOST_PORT_STATUS], &self->most_port_status);
+ }
+}
+
+/*! \brief Delete an observer from the MOSTPortStatus subject
+ * \param self Instance of CInic
+ * \param obs_ptr Pointer to observer to be informed
+ */
+void Inic_DelObsrvMostPortStatus(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_MOST_PORT_STATUS], obs_ptr);
+}
+
+/*! \brief Add an observer to the GpioTriggerEvent subject
+ * \param self Instance of CInic
+ * \param obs_ptr Pointer to observer to be informed
+ */
+void Inic_AddObsrvGpioTriggerEvent(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_AddObserver(&self->subs[INIC_SUB_GPIO_TRIGGER_EVENT], obs_ptr);
+}
+
+/*! \brief Removes an observer from the GpioTriggerEvent subject
+ * \param self Instance of CInic
+ * \param obs_ptr Pointer to observer to be informed
+ */
+void Inic_DelObsrvGpioTriggerEvent(CInic *self, CObserver *obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->subs[INIC_SUB_GPIO_TRIGGER_EVENT], obs_ptr);
+}
+
+/*! \brief Destroys the resources associated with the given resource handles
+ * \param self Reference to CInic instance
+ * \param res_handle_list resource handle list
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ * \return UCS_RET_ERR_PARAM Wrong length of resource handle list
+ */
+Ucs_Return_t Inic_ResourceDestroy(CInic *self,
+ Inic_ResHandleList_t res_handle_list,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ uint8_t len;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_RESOURCE_DESTROY) != false)
+ {
+ /* sender handle + number of resource handles */
+ len = 2U * res_handle_list.num_handles;
+
+ if ((len == 0U) || ((MAX_INVALID_HANDLES_LIST << 1) < len))
+ {
+ Al_Release(&self->lock.res_api, INIC_API_RESOURCE_DESTROY);
+ result = UCS_RET_ERR_PARAM;
+ }
+ else
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, len);
+
+ if (msg_ptr != NULL)
+ {
+ uint8_t i;
+
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_RESOURCE_DESTROY;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ for (i=0U; i < res_handle_list.num_handles; ++i)
+ {
+ msg_ptr->tel.tel_data_ptr[2U*i] = MISC_HB(res_handle_list.res_handles[i]);
+ msg_ptr->tel.tel_data_ptr[1U + (2U*i)] = MISC_LB(res_handle_list.res_handles[i]);
+ }
+
+ self->ssubs[INIC_SSUB_RESOURCE_DESTROY].user_mask = INIC_API_RESOURCE_DESTROY;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_RESOURCE_DESTROY];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_RESOURCE_DESTROY], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_RESOURCE_DESTROY);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Retrieves the list of invalid resources
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer. The result must be casted into type
+ * Inic_StdResult_t.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_ResourceInvalidList_Get(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_RESOURCE_INVAL_LIST) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 0U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_RESOURCE_INVALID_LIST;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ self->ssubs[INIC_SSUB_RESOURCE_INVAL_LIST].user_mask = INIC_API_RESOURCE_INVAL_LIST;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_RESOURCE_INVAL_LIST];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_RESOURCE_INVAL_LIST], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_RESOURCE_INVAL_LIST);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Resets the resource monitor back to its default state.
+ * \param self Reference to CInic instance
+ * \param control Used to reset the resource monitor
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_ResourceMonitor_Set(CInic *self, Ucs_Resource_MonitorCtrl_t control)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_RESOURCE_MONITOR;
+ msg_ptr->id.op_type = UCS_OP_SET;
+ msg_ptr->tel.tel_data_ptr[0] = (uint8_t)control;
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+
+ return result;
+}
+
+/*! \brief Triggers notification of the given function_id list.
+ * \param self Reference to CInic instance
+ * \param control control command used
+ * \param device_id Id of the sending device (local node address).
+ * \param fktid_list function ids list.
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_PARAM parameter exceeds its admissible range was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_Notification_Set(CInic *self, Ucs_Inic_NotificationCtrl_t control, uint16_t device_id, Inic_FktIdList_t fktid_list)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ /* control + device_id + size of the funcids list */
+ uint8_t len = 1U + 2U + (2U * fktid_list.num_fktids);
+
+ if (len > MSG_MAX_SIZE_PAYLOAD)
+ {
+ result = UCS_RET_ERR_PARAM;
+ }
+ else
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, len);
+
+ if (msg_ptr != NULL)
+ {
+ uint8_t i;
+
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_NOTIFICATION;
+ msg_ptr->id.op_type = UCS_OP_SET;
+
+ msg_ptr->tel.tel_data_ptr[0] = (uint8_t)control;
+ msg_ptr->tel.tel_data_ptr[1] = MISC_HB(device_id);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_LB(device_id);
+
+ if ((len > 3U) && (fktid_list.fktids_ptr != NULL) )
+ {
+ for (i=0U; i < fktid_list.num_fktids; ++i)
+ {
+ msg_ptr->tel.tel_data_ptr[3U+(2U*i)] = MISC_HB(fktid_list.fktids_ptr[i]);
+ msg_ptr->tel.tel_data_ptr[4U+(2U*i)] = MISC_LB(fktid_list.fktids_ptr[i]);
+ }
+ }
+
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NOTIFICATION];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Gets the device id that has notified the given function_id
+ * \param self Reference to CInic instance
+ * \param fktid The function id to be looked for
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_Notification_Get(CInic *self, uint16_t fktid, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_NOTIFICATION) != false)
+ {
+ Msg_MostTel_t * msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_NOTIFICATION;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(fktid);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(fktid);
+
+ self->ssubs[INIC_SSUB_NOTIFICATION].user_mask = INIC_API_NOTIFICATION;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_NOTIFICATION];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_NOTIFICATION], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_NOTIFICATION);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates a synchronous data connection. The connection can be directly associated with
+ * an input and output socket.
+ * \param self Reference to CInic instance
+ * \param resource_handle_in The ID number of the socket or splitter resource that is the
+ * starting point of the link.
+ * \param resource_handle_out The ID number of the socket or splitter resource that is the ending
+ * point of the link.
+ * \param default_mute specifies if the connection is muted by default
+ * \param mute_mode Configures how the resource monitor shall handle events that may
+ * the streamed data invalid.
+ * \param offset Denotes the offset from/to where data from/to a socket should be
+ * routed from/to a splitter/combiner.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_SyncCreate(CInic *self,
+ uint16_t resource_handle_in,
+ uint16_t resource_handle_out,
+ bool default_mute,
+ Ucs_Sync_MuteMode_t mute_mode,
+ uint16_t offset,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 8U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_SYNC_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(resource_handle_in);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(resource_handle_in);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(resource_handle_out);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(resource_handle_out);
+ msg_ptr->tel.tel_data_ptr[4] = (default_mute != false) ? 1U : 0U;
+ msg_ptr->tel.tel_data_ptr[5] = (uint8_t)mute_mode;
+ msg_ptr->tel.tel_data_ptr[6] = MISC_HB(offset);
+ msg_ptr->tel.tel_data_ptr[7] = MISC_LB(offset);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function manually mutes a synchronous data connection.
+ * \param self Reference to CInic instance
+ * \param sync_handle Resource handle of the synchronous connection
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_SyncMute(CInic *self,
+ uint16_t sync_handle,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_SYNC_MUTE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_SYNC_MUTE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(sync_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(sync_handle);
+
+ self->ssubs[INIC_SSUB_SYNC_MUTE].user_mask = INIC_API_SYNC_MUTE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_SYNC_MUTE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_SYNC_MUTE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_SYNC_MUTE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function manually de-mutes a synchronous data connection.
+ * \param self Reference to CInic instance
+ * \param sync_handle Resource handle of the synchronous connection
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_SyncDemute(CInic *self,
+ uint16_t sync_handle,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_SYNC_DEMUTE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_SYNC_DEMUTE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(sync_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(sync_handle);
+
+ self->ssubs[INIC_SSUB_SYNC_DEMUTE].user_mask = INIC_API_SYNC_DEMUTE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_SYNC_DEMUTE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_SYNC_DEMUTE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_SYNC_DEMUTE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates a DiscreteFrame Isochronous streaming phase connection. The connection can be
+ * directly associated with an input and output socket.
+ * \param self Reference to CInic instance
+ * \param resource_handle_in The ID number of the socket or splitter resource that is the
+ * starting point of the link.
+ * \param resource_handle_out The ID number of the socket or splitter resource that is the ending
+ * point of the link.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_DfiPhaseCreate(CInic *self,
+ uint16_t resource_handle_in,
+ uint16_t resource_handle_out,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_DFIPHASE_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(resource_handle_in);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(resource_handle_in);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(resource_handle_out);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(resource_handle_out);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates a combiner resource. A Combiner enables grouping of data from multiple network
+ * sockets into the same port socket.
+ * \param self Reference to CInic instance
+ * \param port_socket_handle Only supported sockets are Streaming Port, MLB, USB (OS81118) or PCI
+ * (OS81160) sockets of data type Synchronous. Direction must be OUT.
+ * \param most_port_handle When the splitter is created with a MOST socket, the socket must be
+ * created on the same port indicated by this handle.
+ * \param bytes_per_frame Specifies the total number of data bytes that are to be transferred
+ * each MOST frame.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_CombinerCreate(CInic *self,
+ uint16_t port_socket_handle,
+ uint16_t most_port_handle,
+ uint16_t bytes_per_frame,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 6U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_COMBINER_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(port_socket_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(port_socket_handle);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(bytes_per_frame);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(bytes_per_frame);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates a splitter resource. A Splitter enables splitting up the data from a single
+ * channel into multiple channels.
+ * \param self Reference to CInic instance
+ * \param socket_handle_in All sockets of data type Synchronous are supported, regardless of
+ * the port the socket is created on. The direction must be IN.
+ * \param most_port_handle When the splitter is created with a MOST socket, the socket must be
+ * created on the same port indicated by this handle.
+ * \param bytes_per_frame Specifies the total number of data bytes that are to be transferred
+ * each MOST frame.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_SplitterCreate(CInic *self,
+ uint16_t socket_handle_in,
+ uint16_t most_port_handle,
+ uint16_t bytes_per_frame,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 6U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_SPLITTER_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(socket_handle_in);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(socket_handle_in);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(bytes_per_frame);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(bytes_per_frame);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates an Quality of Service IP Streaming data connection.
+ * \param self Reference to CInic instance
+ * \param socket_in_handle The ID number of the created socket that is the starting point of
+ * the link. Must be a socket of type Input.
+ * \param socket_out_handle The ID number of the created socket that is the ending point of
+ * the link. Must be a socket of type Output.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_QoSCreate(CInic *self,
+ uint16_t socket_in_handle,
+ uint16_t socket_out_handle,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_QOS_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(socket_in_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(socket_in_handle);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(socket_out_handle);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(socket_out_handle);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates an IPC (Inter-Processor Communication) packet connection.
+ * \param self Reference to CInic instance
+ * \param socket_in_handle The ID number of the created socket that is the starting point of
+ * the link. Must be a socket of type Input.
+ * \param socket_out_handle The ID number of the created socket that is the ending point of
+ * the link. Must be a socket of type Output.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_IpcCreate(CInic *self,
+ uint16_t socket_in_handle,
+ uint16_t socket_out_handle,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_IPC_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(socket_in_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(socket_in_handle);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(socket_out_handle);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(socket_out_handle);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates an A/V Packetized Isochronous Streaming data connection.
+ * \param self Reference to CInic instance
+ * \param socket_in_handle The ID number of the created socket that is the starting point of
+ * the link. Must be a socket of type Input.
+ * \param socket_out_handle The ID number of the created socket that is the ending point of
+ * the link. Must be a socket of type Output.
+ * \param isoc_packet_size Specifies the size of data packets that are to be transported over
+ * the isochronous channel.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_AvpCreate(CInic *self,
+ uint16_t socket_in_handle,
+ uint16_t socket_out_handle,
+ Ucs_Avp_IsocPacketSize_t isoc_packet_size,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 6U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_AVP_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(socket_in_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(socket_in_handle);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(socket_out_handle);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(socket_out_handle);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB((uint16_t)isoc_packet_size);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB((uint16_t)isoc_packet_size);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates a MOST socket bound to the MOST Network Port.
+ * \param self Reference to CInic instance
+ * \param most_port_handle MOST Network Port resource handle
+ * \param direction indicates the direction of the data stream from the perspective of
+ * the INIC
+ * \param data_type Specifies the data type
+ * \param bandwidth Required socket bandwidth in bytes. Maximum value depends on current
+ * free network resources.
+ * \param connection_label MOST network connection label. When used as parameter with direction
+ * Input, the connection label is used to connect to the appropriate MOST
+ * frame bytes. When used as parameter with direction Output, the
+ * connection label is not used and must be set to 0xFFFF.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_MostSocketCreate(CInic *self,
+ uint16_t most_port_handle,
+ Ucs_SocketDirection_t direction,
+ Ucs_Most_SocketDataType_t data_type,
+ uint16_t bandwidth,
+ uint16_t connection_label,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 8U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_SOCKET_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)direction;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)data_type;
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(bandwidth);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(bandwidth);
+ msg_ptr->tel.tel_data_ptr[6] = MISC_HB(connection_label);
+ msg_ptr->tel.tel_data_ptr[7] = MISC_LB(connection_label);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates the MediaLB Port with its associated port instance identifier.
+ * \param self Reference to CInic instance
+ * \param index MediaLB Port instance
+ * \param clock_config Stores the clock speed configuration. The value is a multiple
+ * of the MOST network frame rate Fs; this means the MediaLB Port
+ * can only be frequency locked to the network's system clock.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_MlbPortCreate(CInic *self,
+ uint8_t index,
+ Ucs_Mlb_ClockConfig_t clock_config,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MLB_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = index;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)clock_config;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates a MediaLB socket bound to the MediaLB Port with the associated port instance
+ * identifier. If INIC enters Protected Mode, the MediaLB socket will be automatically
+ * destroyed.
+ * \param self Reference to CInic instance
+ * \param mlb_port_handle MediaLB Port resource handle
+ * \param direction Indicates the direction of the data stream from the perspective of
+ * the INIC
+ * \param data_type Specifies the data type
+ * \param bandwidth Required socket bandwidth in bytes
+ * \param channel_address Indicates the MediaLB ChannelAddress where the socket is mapped to.
+ * If the MediaLB Port is created by default, ChannelAddresses 0x0002
+ * and 0x0004 are reserved and cannot be used.
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_MlbSocketCreate(CInic *self,
+ uint16_t mlb_port_handle,
+ Ucs_SocketDirection_t direction,
+ Ucs_Mlb_SocketDataType_t data_type,
+ uint16_t bandwidth,
+ uint16_t channel_address,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 8U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MLB_SOCKET_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(mlb_port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(mlb_port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)direction;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)data_type;
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(bandwidth);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(bandwidth);
+ msg_ptr->tel.tel_data_ptr[6] = MISC_HB(channel_address);
+ msg_ptr->tel.tel_data_ptr[7] = MISC_LB(channel_address);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates the USB Port with its associated port instance identifier. The instance
+ * identifier of an USB Port is always directly bound to a specific hardware port.
+ * \param self Reference to CInic instance
+ * \param index USB Port instance
+ * \param physical_layer USB Port physical layer
+ * \param devices_interfaces USB Interfaces supported by the device
+ * \param streaming_if_ep_out_count number of USB OUT Endpoints being provided through the USB streaming interface
+ * \param streaming_if_ep_in_count number of USB IN Endpoints being provided through the USB Streaming interface
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_UsbPortCreate(CInic *self,
+ uint8_t index,
+ Ucs_Usb_PhysicalLayer_t physical_layer,
+ uint16_t devices_interfaces,
+ uint8_t streaming_if_ep_out_count,
+ uint8_t streaming_if_ep_in_count,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 6U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_USB_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = index;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)physical_layer;
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(devices_interfaces);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(devices_interfaces);
+ msg_ptr->tel.tel_data_ptr[4] = streaming_if_ep_out_count;
+ msg_ptr->tel.tel_data_ptr[5] = streaming_if_ep_in_count;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Creates a USB socket bound to the USB port with its associated port instance identifier.
+ * If INIC enters Protected Mode, the USB socket will be automatically destroyed.
+ * \param self Reference to CInic instance
+ * \param usb_port_handle USB Port resource handle
+ * \param direction Indicates the direction of the data stream from the perspective of
+ * the INIC
+ * \param data_type Specifies the data type
+ * \param end_point_addr Denotes the address of a USB endpoint as per its description in the
+ * USB 2.0 Specification
+ * \param frames_per_transfer Indicates the number of MOST frames per transfer per one USB
+ * transaction
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_UsbSocketCreate(CInic *self,
+ uint16_t usb_port_handle,
+ Ucs_SocketDirection_t direction,
+ Ucs_Usb_SocketDataType_t data_type,
+ uint8_t end_point_addr,
+ uint16_t frames_per_transfer,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 7U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_USB_SOCKET_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(usb_port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(usb_port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)direction;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)data_type;
+ msg_ptr->tel.tel_data_ptr[4] = end_point_addr;
+ msg_ptr->tel.tel_data_ptr[5] = MISC_HB(frames_per_transfer);
+ msg_ptr->tel.tel_data_ptr[6] = MISC_LB(frames_per_transfer);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function is used to configure the Streaming Ports.
+ * \param self Reference to CInic instance
+ * \param index Streaming Port instance
+ * \param op_mode Streaming Port Operation mode
+ * \param port_option Streaming Port Options
+ * \param clock_mode Stream Port Clock Mode
+ * \param clock_data_delay Stream Port Clock Data Delay
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_StreamPortConfig_SetGet(CInic *self,
+ uint8_t index,
+ Ucs_Stream_PortOpMode_t op_mode,
+ Ucs_Stream_PortOption_t port_option,
+ Ucs_Stream_PortClockMode_t clock_mode,
+ Ucs_Stream_PortClockDataDelay_t clock_data_delay,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_STREAM_PORT_CONFIG) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 5U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_STREAM_PORT_CONFIG;
+ msg_ptr->id.op_type = UCS_OP_SETGET;
+
+ msg_ptr->tel.tel_data_ptr[0] = (uint8_t)index;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)op_mode;
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)port_option;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)clock_mode;
+ msg_ptr->tel.tel_data_ptr[4] = (uint8_t)clock_data_delay;
+
+ self->ssubs[INIC_SSUB_STREAM_PORT_CONFIG].user_mask = INIC_API_STREAM_PORT_CONFIG;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_STREAM_PORT_CONFIG];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_STREAM_PORT_CONFIG], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_STREAM_PORT_CONFIG);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function is used to request the configurations of the Streaming Ports.
+ * \param self Reference to CInic instance
+ * \param index Streaming Port Instance ID
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_StreamPortConfig_Get(CInic *self,
+ uint8_t index,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_STREAM_PORT_CONFIG) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_STREAM_PORT_CONFIG;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->tel.tel_data_ptr[0] = (uint8_t)index;
+
+ self->ssubs[INIC_SSUB_STREAM_PORT_CONFIG].user_mask = INIC_API_STREAM_PORT_CONFIG;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_STREAM_PORT_CONFIG];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_STREAM_PORT_CONFIG], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_STREAM_PORT_CONFIG);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates the Streaming Port with its associated port instance identifier.
+ * \param self Reference to CInic instance
+ * \param index Streaming Port instance
+ * \param clock_config Clock speed configuration of the SCK signal
+ * \param data_alignment Defines the alignment of the data bytes within the streaming port frame
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_StreamPortCreate(CInic *self,
+ uint8_t index,
+ Ucs_Stream_PortClockConfig_t clock_config,
+ Ucs_Stream_PortDataAlign_t data_alignment,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 3U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_STREAM_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = index;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)clock_config;
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)data_alignment;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates a Synchronous or DiscreteFrame Isochronous Streaming data socket
+ * bound to the Streaming Port with the denoted port instance identifier.
+ * \param self Reference to CInic instance
+ * \param stream_port_handle Streaming Port resource handle
+ * \param direction Indicates the direction of the data stream, from the INIC's
+ * perspective
+ * \param data_type Specifies the data type
+ * \param bandwidth Required socket bandwidth in bytes
+ * \param stream_pin_id ID of the serial interface pin of the addressed Streaming Port
+ * instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_StreamSocketCreate(CInic *self,
+ uint16_t stream_port_handle,
+ Ucs_SocketDirection_t direction,
+ Ucs_Stream_SocketDataType_t data_type,
+ uint16_t bandwidth,
+ Ucs_Stream_PortPinId_t stream_pin_id,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 7U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_STREAM_SOCKET_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(stream_port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(stream_port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)direction;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)data_type;
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(bandwidth);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(bandwidth);
+ msg_ptr->tel.tel_data_ptr[6] = (uint8_t)stream_pin_id;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates an RMCK Port with its associated port instance identifier.
+ * \param self Reference to CInic instance
+ * \param index RMCK Port instance
+ * \param clock_source Indicates the source of the RMCK clock
+ * \param divisor Divisor of the clock source
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_RmckPortCreate(CInic *self,
+ uint8_t index,
+ Ucs_Rmck_PortClockSource_t clock_source,
+ uint16_t divisor,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_RMCK_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = index;
+ msg_ptr->tel.tel_data_ptr[1] = (uint8_t)clock_source;
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(divisor);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(divisor);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates an I2C Port with its associated port instance identifier.
+ * \param self Reference to CInic instance
+ * \param index I2C Port instance
+ * \param address The 7-bit I2C slave address of the peripheral to be read.
+ * \param mode The operation mode of the I2C Port
+ * \param speed The speed grade of the I2C Port
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_I2cPortCreate(CInic *self,
+ uint8_t index,
+ uint8_t address,
+ uint8_t mode,
+ Ucs_I2c_Speed_t speed,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_I2C_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = index;
+ msg_ptr->tel.tel_data_ptr[1] = address;
+ msg_ptr->tel.tel_data_ptr[2] = mode;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)speed;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function reads a block of bytes from an I2C device at a specified I2C address.
+ * \param self Reference to CInic instance
+ * \param port_handle Port resource handle
+ * \param slave_address The 7-bit I2C slave address of the peripheral to be read
+ * \param data_len Number of bytes to be read from the address
+ * \param timeout The timeout for the I2C Port read
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_I2cPortRead(CInic *self,
+ uint16_t port_handle,
+ uint8_t slave_address,
+ uint8_t data_len,
+ uint16_t timeout,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_I2C_PORT_WR) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 6U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_I2C_PORT_READ;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = slave_address;
+ msg_ptr->tel.tel_data_ptr[3] = data_len;
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(timeout);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(timeout);
+
+ self->ssubs[INIC_SSUB_I2C_PORT_WR].user_mask = INIC_API_I2C_PORT_WR;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_I2C_PORT_WR];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_I2C_PORT_WR], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_I2C_PORT_WR);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function writes a block of bytes to an I2C device at a specified I2C address.
+ * \param self Reference to CInic instance
+ * \param port_handle Port resource handle
+ * \param mode The write transfer mode
+ * \param block_count The number of blocks to be written to the I2C address.
+ * \param slave_address The 7-bit I2C slave address of the peripheral to be read
+ * \param timeout The timeout for the I2C Port write
+ * \param data_len Number of bytes to be written to the addressed I2C peripheral
+ * \param data_list Reference to the data list to be written
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_I2cPortWrite(CInic *self,
+ uint16_t port_handle,
+ Ucs_I2c_TrMode_t mode,
+ uint8_t block_count,
+ uint8_t slave_address,
+ uint16_t timeout,
+ uint8_t data_len,
+ uint8_t data_list[],
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_I2C_PORT_WR) != false)
+ {
+ uint8_t i;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, (8U + data_len));
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_I2C_PORT_WRITE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)mode;
+ msg_ptr->tel.tel_data_ptr[3] = block_count;
+ msg_ptr->tel.tel_data_ptr[4] = slave_address;
+ msg_ptr->tel.tel_data_ptr[5] = (mode == UCS_I2C_BURST_MODE) ? (data_len/block_count):data_len;
+ msg_ptr->tel.tel_data_ptr[6] = MISC_HB(timeout);
+ msg_ptr->tel.tel_data_ptr[7] = MISC_LB(timeout);
+
+ if (data_list != NULL)
+ {
+ for (i = 0U; i < data_len; i++)
+ {
+ msg_ptr->tel.tel_data_ptr[8U + i] = data_list[i];
+ }
+ }
+
+ self->ssubs[INIC_SSUB_I2C_PORT_WR].user_mask = INIC_API_I2C_PORT_WR;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_I2C_PORT_WR];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_I2C_PORT_WR], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_I2C_PORT_WR);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates an PCIe Port with its associated port instance identifier.
+ * \param self Reference to CInic instance
+ * \param index PCIe Port instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_PciPortCreate(CInic *self,
+ uint8_t index,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_PCI_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = index;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates a PCIe socket bound to the PCIe Port with the associated port
+ * instance identifier. If the EHC detaches, the PCIe socket will be automatically
+ * destroyed.
+ * \param self Reference to CInic instance
+ * \param pci_port_handle PCIe Port resource handle
+ * \param direction Indicates the direction of the data stream from the perspective of
+ * the INIC
+ * \param data_type Specifies the data type
+ * \param dma_channel Specifies the DMA channel
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_PciSocketCreate(CInic *self,
+ uint16_t pci_port_handle,
+ Ucs_SocketDirection_t direction,
+ Ucs_Pci_SocketDataType_t data_type,
+ uint8_t dma_channel,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 5U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_PCI_SOCKET_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(pci_port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(pci_port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (uint8_t)direction;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)data_type;
+ msg_ptr->tel.tel_data_ptr[4] = dma_channel;
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function creates a GPIO Port with its associated port instance identifier.
+ * \param self Reference to CInic instance
+ * \param gpio_port_index GPIO Port instance
+ * \param debounce_time Timeout for the GPIO debounce timer
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_GpioPortCreate(CInic *self,
+ uint8_t gpio_port_index,
+ uint16_t debounce_time,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_CREATE_CLASS) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 3U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_GPIO_PORT_CREATE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = gpio_port_index;
+ msg_ptr->tel.tel_data_ptr[1] = MISC_HB(debounce_time);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_LB(debounce_time);
+
+ self->ssubs[INIC_SSUB_CREATE_CLASS].user_mask = INIC_API_CREATE_CLASS;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_CREATE_CLASS];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_CREATE_CLASS], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_CREATE_CLASS);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function enables or disables a specific MOST Network Port.
+ * \param self Reference to CInic instance
+ * \param most_port_handle Port resource handle
+ * \param enabled Indicates whether a MOST Network Port should be enabled or disabled
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_MostPortEnable(CInic *self,
+ uint16_t most_port_handle,
+ bool enabled,
+ CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_MOST_PORT_ENABLE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 3U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_MOST_PORT_ENABLE;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(most_port_handle);
+ msg_ptr->tel.tel_data_ptr[2] = (enabled != false) ? 1U : 0U;
+
+ self->ssubs[INIC_SSUB_MOST_PORT_ENABLE].user_mask = INIC_API_MOST_PORT_ENABLE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_MOST_PORT_ENABLE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_MOST_PORT_ENABLE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_MOST_PORT_ENABLE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function retrieves the current pin mode of the given GPIO Port.
+ * \param self Reference to CInic instance
+ * \param gpio_handle GPIO Port instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_GpioPortPinMode_Get(CInic *self, uint16_t gpio_handle, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_GPIO_PIN_MODE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_GPIO_PORT_PIN_MODE;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(gpio_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(gpio_handle);
+
+ self->ssubs[INIC_SSUB_GPIO_PIN_MODE].user_mask = INIC_API_GPIO_PIN_MODE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_GPIO_PIN_MODE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_GPIO_PIN_MODE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_GPIO_PIN_MODE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function sets and retrieves the mode of a pin on the GPIO Port
+ * \param self Reference to CInic instance
+ * \param gpio_handle GPIO Port instance
+ * \param pin The GPIO pin that is to be configured
+ * \param mode The mode of the GPIO pin
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_GpioPortPinMode_SetGet(CInic *self, uint16_t gpio_handle, uint8_t pin, Ucs_Gpio_PinMode_t mode, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_GPIO_PIN_MODE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 4U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_GPIO_PORT_PIN_MODE;
+ msg_ptr->id.op_type = UCS_OP_SETGET;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(gpio_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(gpio_handle);
+ msg_ptr->tel.tel_data_ptr[2] = pin;
+ msg_ptr->tel.tel_data_ptr[3] = (uint8_t)mode;
+
+ self->ssubs[INIC_SSUB_GPIO_PIN_MODE].user_mask = INIC_API_GPIO_PIN_MODE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_GPIO_PIN_MODE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_GPIO_PIN_MODE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_GPIO_PIN_MODE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function retrieves the pin state of the given GPIO Port.
+ * \param self Reference to CInic instance
+ * \param gpio_handle GPIO Port instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_GpioPortPinState_Get(CInic *self, uint16_t gpio_handle, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_GPIO_PIN_STATE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 2U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_GPIO_PORT_PIN_STATE;
+ msg_ptr->id.op_type = UCS_OP_GET;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(gpio_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(gpio_handle);
+
+ self->ssubs[INIC_SSUB_GPIO_PIN_STATE].user_mask = INIC_API_GPIO_PIN_STATE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_GPIO_PIN_STATE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_GPIO_PIN_STATE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_GPIO_PIN_STATE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function sets and retrieves the state of a pin on the GPIO Port
+ * \param self Reference to CInic instance
+ * \param gpio_handle GPIO Port instance
+ * \param mask The GPIO pins to be written
+ * \param data The state of the GPIO pins to be written
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_GpioPortPinState_SetGet(CInic *self, uint16_t gpio_handle, uint16_t mask, uint16_t data, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_GPIO_PIN_STATE) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 6U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_GPIO_PORT_PIN_STATE;
+ msg_ptr->id.op_type = UCS_OP_SETGET;
+
+ msg_ptr->tel.tel_data_ptr[0] = MISC_HB(gpio_handle);
+ msg_ptr->tel.tel_data_ptr[1] = MISC_LB(gpio_handle);
+ msg_ptr->tel.tel_data_ptr[2] = MISC_HB(mask);
+ msg_ptr->tel.tel_data_ptr[3] = MISC_LB(mask);
+ msg_ptr->tel.tel_data_ptr[4] = MISC_HB(data);
+ msg_ptr->tel.tel_data_ptr[5] = MISC_LB(data);
+
+ self->ssubs[INIC_SSUB_GPIO_PIN_STATE].user_mask = INIC_API_GPIO_PIN_STATE;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_GPIO_PIN_STATE];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_GPIO_PIN_STATE], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_GPIO_PIN_STATE);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief This function enables full streaming for a specific MOST Network Port.
+ * \param self Reference to CInic instance
+ * \param most_port_handle Port resource handle
+ * \param enabled Indicates whether full streaming is enabled or disabled
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_API_LOCKED Resource API is already used by another command
+ */
+Ucs_Return_t Inic_MostPortEnFullStr(CInic *self,
+ uint16_t most_port_handle,
+ bool enabled,
+ CSingleObserver *obs_ptr)
+{
+ MISC_UNUSED(self);
+ MISC_UNUSED(most_port_handle);
+ MISC_UNUSED(enabled);
+ MISC_UNUSED(obs_ptr);
+
+ return UCS_RET_ERR_NOT_SUPPORTED;
+}
+
+/*! \brief Allows remote synchronization of the given device
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_DeviceSync(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_DEVICE_SYNC) != false)
+ {
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_DEVICE_SYNC;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = 0x01U;
+
+ self->ssubs[INIC_SSUB_DEVICE_SYNC].user_mask = INIC_API_DEVICE_SYNC;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_DEVICE_SYNC];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_DEVICE_SYNC], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_DEVICE_SYNC);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*! \brief Un-synchronizes to the given remote device
+ * \param self Reference to CInic instance
+ * \param obs_ptr Reference to an optional observer
+ * \return UCS_RET_SUCCESS message was created
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ */
+Ucs_Return_t Inic_DeviceUnsync(CInic *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->xcvr_ptr, 1U);
+
+ if(Al_Lock(&self->lock.res_api, INIC_API_DEVICE_SYNC) != false)
+ {
+ if (msg_ptr != NULL)
+ {
+ msg_ptr->destination_addr = self->target_address;
+
+ msg_ptr->id.fblock_id = FB_INIC;
+ msg_ptr->id.instance_id = 0U;
+ msg_ptr->id.function_id = INIC_FID_DEVICE_SYNC;
+ msg_ptr->id.op_type = UCS_OP_STARTRESULT;
+
+ msg_ptr->tel.tel_data_ptr[0] = 0U;
+
+ self->ssubs[INIC_SSUB_DEVICE_SYNC].user_mask = INIC_API_DEVICE_SYNC;
+ msg_ptr->info_ptr = &self->ssubs[INIC_SSUB_DEVICE_SYNC];
+ Trcv_TxSendMsgExt(self->xcvr_ptr, msg_ptr, &Inic_ResMsgTxStatusCb, self);
+
+ (void)Ssub_AddObserver(&self->ssubs[INIC_SSUB_DEVICE_SYNC], obs_ptr);
+ }
+ else
+ {
+ Al_Release(&self->lock.res_api, INIC_API_DEVICE_SYNC);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ result = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return result;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Handler functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Handle message Tx status, Unlock the API and free the message objects
+ * \param self The instance
+ * \param tel_ptr Reference to transmitted message
+ * \param status Status of the transmitted message
+ */
+static void Inic_ResMsgTxStatusCb(void *self, Msg_MostTel_t *tel_ptr, Ucs_MsgTxStatus_t status)
+{
+ CInic *self_ = (CInic *)self;
+ CSingleSubject *ssub_ptr = (CSingleSubject *)tel_ptr->info_ptr;
+
+ if ((status != UCS_MSG_STAT_OK) && (tel_ptr->info_ptr != NULL))
+ {
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &status;
+ res_data.result.code = UCS_RES_ERR_TRANSMISSION;
+ res_data.result.info_ptr = NULL;
+ res_data.result.info_size = 0U;
+ Ssub_Notify(ssub_ptr, &res_data, true);
+
+ if ((ssub_ptr != NULL) && (ssub_ptr->user_mask != 0U))
+ {
+ Al_Release(&self_->lock.res_api, (Alm_ModuleMask_t)ssub_ptr->user_mask);
+ }
+ }
+ Trcv_TxReleaseMsg(tel_ptr);
+ /* Reset user mask of the single subject if available */
+ if (ssub_ptr != NULL)
+ {
+ ssub_ptr->user_mask = 0U;
+ }
+
+ /* ICM messages pending? */
+ if (Sub_GetNumObservers(&self_->subs[INIC_SUB_TX_MSG_OBJ_AVAIL]) > 0U)
+ {
+ Sub_Notify(&self_->subs[INIC_SUB_TX_MSG_OBJ_AVAIL], NULL);
+ }
+}
+
+/*! \brief Handler function for INIC.ResourceDestroy.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_ResourceDestroy_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_RESOURCE_DESTROY], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_RESOURCE_DESTROY);
+}
+
+/*! \brief Handler function for INIC.ResourceDestroy.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_ResourceDestroy_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_RESOURCE_DESTROY], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_RESOURCE_DESTROY);
+}
+
+/*! \brief Handler function for INIC.ResourceInvalidList.Status
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_ResourceInvalidList_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ Inic_ResHandleList_t result;
+ uint8_t i;
+ uint16_t inv_res_handles[22]; /* Max. ICM message size is 45 -> max. 22 16-bit values */
+
+ res_data.data_info = &result;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ /* Length of message must be even, since 16-Bit values are transmitted via two 8-bit fields. */
+ TR_ASSERT(self_->base_ptr->ucs_user_ptr, "[INIC_RES]", ((msg_ptr->tel.tel_len % 2U) == 0U));
+
+ for(i=0U; (i < (uint8_t)(msg_ptr->tel.tel_len >> 1)); i++)
+ {
+ MISC_DECODE_WORD(&inv_res_handles[i],
+ &(msg_ptr->tel.tel_data_ptr[(uint8_t)((uint8_t)i << 1)]));
+ }
+ result.res_handles = &inv_res_handles[0];
+ result.num_handles = (uint8_t)((uint8_t)msg_ptr->tel.tel_len >> 1);
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_RESOURCE_INVAL_LIST], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_RESOURCE_INVAL_LIST);
+}
+
+/*! \brief Handler function for INIC.ResourceInvalidList.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_ResourceInvalidList_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_RESOURCE_INVAL_LIST], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_RESOURCE_INVAL_LIST);
+}
+
+/*! \brief Handler function for INIC.ResourceMonitor.Status
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_ResourceMonitor_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ Ucs_Resource_MonitorState_t state;
+
+ res_data.data_info = &state;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ state = (Ucs_Resource_MonitorState_t)msg_ptr->tel.tel_data_ptr[0];
+ Sub_Notify(&self_->subs[INIC_SUB_RES_MONITOR], &res_data);
+}
+
+/*! \brief Handler function for INIC.ResourceMonitor.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_ResourceMonitor_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+ Sub_Notify(&self_->subs[INIC_SUB_RES_MONITOR], &res_data);
+}
+
+/*! \brief Handler function for INIC.SyncCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SyncCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.SyncCreate.ResultAck. res_data.data_info points to the
+ * resource handle of the synchronous connection which was created.
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SyncCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+
+/*! \brief Handler function for INIC.SyncMute.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SyncMute_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_SYNC_MUTE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_SYNC_MUTE);
+}
+
+/*! \brief Handler function for INIC.SyncMute.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SyncMute_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_SYNC_MUTE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_SYNC_MUTE);
+}
+
+/*! \brief Handler function for INIC.SyncDemute.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SyncDemute_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_SYNC_DEMUTE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_SYNC_DEMUTE);
+}
+
+/*! \brief Handler function for INIC.SyncDemute.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SyncDemute_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_SYNC_DEMUTE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_SYNC_DEMUTE);
+}
+
+/*! \brief Handler function for INIC.DFIPhaseCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_DfiPhaseCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.DFIPhaseCreate.ResultAck. res_data.data_info points to the
+ * resource handle of the DFIPhase streaming phase connection which was created.
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_DfiPhaseCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.CombinerCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_CombinerCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.CombinerCreate.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_CombinerCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.SplitterCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SplitterCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.SplitterCreate.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_SplitterCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.QoSCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_QoSCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.QoSCreate.ResultAck.res_data.data_info points to the Resource
+ * handle of the Quality of Service IP Streaming data connection which was created.
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_QoSCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.IPCPacketCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_IpcCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.IPCPacketCreate.ResultAck.res_data.data_info points to the resource
+ * handle of the Inter-Processor Communication packet connection which was created.
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_IpcCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.AVPCreate.ErrorAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_AvpCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.AVPCreate.ResultAck. res_data.data_info points to the Resource
+ * handle of the A/V Packetized Isochronous Streaming connection which was created.
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_AvpCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.MOSTPortStatus.Status
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostPortStatus_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_MostPortStatus_t result;
+
+ MISC_DECODE_WORD(&(result.most_port_handle), &(msg_ptr->tel.tel_data_ptr[0]));
+ result.availability = (Ucs_Most_PortAvail_t)msg_ptr->tel.tel_data_ptr[2];
+ result.avail_info = (Ucs_Most_PortAvailInfo_t)msg_ptr->tel.tel_data_ptr[3];
+ result.fullstreaming_enabled = (msg_ptr->tel.tel_data_ptr[4] != 0U) ? true : false;
+ MISC_DECODE_WORD(&(result.freestreaming_bw), &(msg_ptr->tel.tel_data_ptr[5]));
+
+ self_->most_port_status = result;
+
+ Sub_Notify(&self_->subs[INIC_SUB_MOST_PORT_STATUS], &result);
+}
+
+/*! \brief Handler function for INIC.MOSTPortStatus.Error
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostPortStatus_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+
+ Sub_Notify(&self_->subs[INIC_SUB_MOST_PORT_STATUS], &res_data);
+}
+
+/*! \brief Handler function for INIC.MOSTSocketCreate.ErrorAck. Result is delivered via the
+ * SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostSocketCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.MOSTSocketCreate.ResultAck
+ * \details Result is delivered via the SingleObserver element ssubs[INIC_SSUB_CREATE_CLASS]. Element
+ * res_data.data_info points to a variable of type Inic_MostSocketCreate_Result_t
+ * which holds the results of the MOSTSocketCreate command.
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostSocketCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ Inic_MostSocketCreate_Result_t res;
+
+ MISC_DECODE_WORD(&(res.most_socket_handle), &(msg_ptr->tel.tel_data_ptr[0]));
+ MISC_DECODE_WORD(&(res.conn_label), &(msg_ptr->tel.tel_data_ptr[2]));
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+
+/*! \brief Handler function for INIC.MLBPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MlbPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.MLBPortCreate.ResultAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * Element res_data.data_info points to the variable mlb_port_handle which holds the
+ * MediaLB Port resource handle.
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MlbPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t mlb_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&mlb_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &mlb_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.MLBSocketCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MlbSocketCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.MLBSocketCreate.ResultAck
+ * \details Element res_data.data_info points to the variable mlb_socket_handle which holds the
+ * MediaLB Socket resource handle of the created socket. Result is delivered via the
+ * SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MlbSocketCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t mlb_socket_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&mlb_socket_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &mlb_socket_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.USBPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_UsbPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.USBPortCreate.ResultAck
+ * \details Element res_data.data_info points to the variable usb_port_handle which holds the USB
+ * Port resource handle.
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_UsbPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t usb_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&usb_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &usb_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.USBSocketCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_UsbSocketCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.USBSocketCreate.ResultAck
+ * \details Element res_data.data_info points to the variable usb_socket_handle which holds the
+ * Socket resource handle of the created socket. Result is delivered via the
+ * SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_UsbSocketCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t usb_socket_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&usb_socket_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &usb_socket_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.StreamPortConfiguration.Status
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_STREAM_PORT_CONFIG].
+ * Element res_data.data_info points to a variable of type Inic_StreamPortConfigStatus_t
+ * which holds the results of the INIC.StreamPortConfiguration.Get command.
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_StreamPortConfig_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StreamPortConfigStatus_t res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ res.index = msg_ptr->tel.tel_data_ptr[0];
+ res.op_mode = (Ucs_Stream_PortOpMode_t)msg_ptr->tel.tel_data_ptr[1];
+ res.port_option = (Ucs_Stream_PortOption_t)msg_ptr->tel.tel_data_ptr[2];
+ res.clock_mode = (Ucs_Stream_PortClockMode_t)msg_ptr->tel.tel_data_ptr[3];
+ res.clock_data_delay = (Ucs_Stream_PortClockDataDelay_t)msg_ptr->tel.tel_data_ptr[4];
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_STREAM_PORT_CONFIG], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_STREAM_PORT_CONFIG);
+}
+
+/*! \brief Handler function for INIC.StreamPortConfiguration.Error
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_STREAM_PORT_CONFIG].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_StreamPortConfig_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_STREAM_PORT_CONFIG], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_STREAM_PORT_CONFIG);
+}
+
+/*! \brief Handler function for INIC.StreamPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_StreamPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.StreamPortCreate.ResultAck
+ * \details Element res_data.data_info points to the variable stream_port_handle which holds the
+ * Streaming Port resource handle. Result is delivered via the SingleObserver object
+ * ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_StreamPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t stream_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&stream_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &stream_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.StreamSocketCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_StreamSocketCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.StreamSocketCreate.ResultAck
+ * \details Element res_data.data_info points to the variable stream_socket_handle which holds
+ * the Socket resource handle of the created socket. Result is delivered via the
+ * SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_StreamSocketCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t stream_socket_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&stream_socket_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &stream_socket_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.RMCKOutPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_RmckPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.RMCKOutPortCreate.ResultAck
+ * \details Element res_data.data_info points to the variable rmck_port_handle which holds the
+ * RMCK Port resource handle. Result is delivered via the SingleObserver object
+ * ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_RmckPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t rmck_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&rmck_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &rmck_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.I2CPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_I2cPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.I2CPortCreate.ResultAck
+ * \details Element res_data.data_info points to the variable i2c_port_handle which holds the
+ * I2C Port resource handle. Result is delivered via the SingleObserver object
+ * ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_I2cPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t i2c_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&i2c_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &i2c_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.I2CPortRead.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_I2C_PORT_WR].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_I2cPortRead_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_I2C_PORT_WR], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_I2C_PORT_WR);
+}
+
+/*! \brief Handler function for INIC.I2CPortRead.ResultAck
+ * \details Element res_data.data_info points to a variable of type Inic_I2cReadResStatus_t which holds the
+ * the results of the INIC.I2CPortRead.StartResultAck command.
+ * Result is delivered via the SingleObserver object ssubs[INIC_SSUB_I2C_PORT_WR].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_I2cPortRead_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_I2cReadResStatus_t i2c_read_res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &i2c_read_res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&i2c_read_res.port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ i2c_read_res.slave_address = msg_ptr->tel.tel_data_ptr[2];
+ i2c_read_res.data_len = msg_ptr->tel.tel_data_ptr[3];
+ i2c_read_res.data_ptr = &msg_ptr->tel.tel_data_ptr[4];
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_I2C_PORT_WR], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_I2C_PORT_WR);
+}
+
+/*! \brief Handler function for INIC.I2CPortWrite.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_I2C_PORT_WR].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_I2cPortWrite_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_I2C_PORT_WR], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_I2C_PORT_WR);
+}
+
+/*! \brief Handler function for INIC.I2CPortWrite.ResultAck
+ * \details Element res_data.data_info points to a variable of type Inic_I2cWriteResStatus_t which holds the
+ * the results of the INIC.I2CPortWrite.StartResultAck command.
+ * Result is delivered via the SingleObserver object ssubs[INIC_SSUB_I2C_PORT_WR].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_I2cPortWrite_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_I2cWriteResStatus_t i2c_write_res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &i2c_write_res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&i2c_write_res.port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ i2c_write_res.slave_address = msg_ptr->tel.tel_data_ptr[2];
+ i2c_write_res.data_len = msg_ptr->tel.tel_data_ptr[3];
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_I2C_PORT_WR], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_I2C_PORT_WR);
+}
+
+/*! \brief Handler function for INIC.PCIPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_PciPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.PCIPortCreate.ResultAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_PciPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t pci_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&pci_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &pci_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.PCISocketCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_PciSocketCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.PCISocketCreate.ResultAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_PciSocketCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t pci_socket_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&pci_socket_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &pci_socket_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.GPIOPortCreate.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortCreate_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.GPIOPortCreate.ResultAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_CREATE_CLASS].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortCreate_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ uint16_t gpio_port_handle;
+ Inic_StdResult_t res_data;
+
+ MISC_DECODE_WORD(&gpio_port_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ res_data.data_info = &gpio_port_handle;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_CREATE_CLASS], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_CREATE_CLASS);
+}
+
+/*! \brief Handler function for INIC.MOSTPortEnable.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_MOST_PORT_ENABLE].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostPortEnable_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_MOST_PORT_ENABLE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_MOST_PORT_ENABLE);
+}
+
+/*! \brief Handler function for INIC.MOSTPortEnable.ResultAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_MOST_PORT_ENABLE].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostPortEnable_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_MOST_PORT_ENABLE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_MOST_PORT_ENABLE);
+}
+
+/*! \brief Handler function for INIC.GPIOPortPinMode.Status
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_GPIO_PIN_MODE].
+ * Element res_data.data_info points to a variable of type Inic_GpioPortPinModeStatus_t
+ * which holds the results of the INIC.GPIOPortPinMode.Get command.
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortPinMode_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_GpioPortPinModeStatus_t res;
+ Inic_StdResult_t res_data;
+ uint8_t i = 2U, j = 0U;
+ Ucs_Gpio_PinConfiguration_t pin_ls[16U];
+
+ res.cfg_list = &pin_ls[0];
+ res.len = (msg_ptr->tel.tel_len - 2U) >> 1U;
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&res.gpio_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ for (; (i < msg_ptr->tel.tel_len) && (j < 16U); i=i+2U)
+ {
+ pin_ls[j].pin = msg_ptr->tel.tel_data_ptr[i];
+ pin_ls[j].mode = (Ucs_Gpio_PinMode_t)msg_ptr->tel.tel_data_ptr[i+1U];
+ j++;
+ }
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_GPIO_PIN_MODE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_GPIO_PIN_MODE);
+}
+
+/*! \brief Handler function for INIC.GPIOPortPinMode.Error
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_GPIO_PIN_MODE].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortPinMode_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_GPIO_PIN_MODE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_GPIO_PIN_MODE);
+}
+
+/*! \brief Handler function for INIC.GPIOPortPinState.Status
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_GPIO_PIN_STATE].
+ * Element res_data.data_info points to a variable of type Inic_GpioPortPinStateStatus_t
+ * which holds the results of the INIC.GPIOPortPinState.Get command.
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortPinState_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_GpioPortPinStateStatus_t res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&res.gpio_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ MISC_DECODE_WORD(&res.current_state, &(msg_ptr->tel.tel_data_ptr[2]));
+ MISC_DECODE_WORD(&res.sticky_state, &(msg_ptr->tel.tel_data_ptr[4]));
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_GPIO_PIN_STATE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_GPIO_PIN_STATE);
+}
+
+/*! \brief Handler function for INIC.GPIOPortPinState.Error
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_GPIO_PIN_STATE].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortPinState_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_GPIO_PIN_STATE], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_GPIO_PIN_STATE);
+}
+
+/*! \brief Handler function for INIC.GPIOPortTriggerEvent.Status
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_GPIO_TRIGGER_EVENT].
+ * Element res_data.data_info points to a variable of type Inic_GpioTriggerEventStatus_t
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortTrigger_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_GpioTriggerEventStatus_t res;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = &res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&res.gpio_handle, &(msg_ptr->tel.tel_data_ptr[0]));
+ MISC_DECODE_WORD(&res.rising_edges, &(msg_ptr->tel.tel_data_ptr[2]));
+ MISC_DECODE_WORD(&res.falling_edges, &(msg_ptr->tel.tel_data_ptr[4]));
+ MISC_DECODE_WORD(&res.levels, &(msg_ptr->tel.tel_data_ptr[6]));
+ res.is_first_report = self_->gpio_rt_status.first_report;
+ if (self_->gpio_rt_status.first_report)
+ {
+ self_->gpio_rt_status.first_report = false;
+ }
+
+ Sub_Notify(&self_->subs[INIC_SUB_GPIO_TRIGGER_EVENT], &res_data);
+}
+
+/*! \brief Handler function for INIC.GPIOPortTriggerEvent.Error
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_GPIO_TRIGGER_EVENT].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_GpioPortTrigger_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ msg_ptr->tel.tel_len);
+ Sub_Notify(&self_->subs[INIC_SUB_GPIO_TRIGGER_EVENT], &res_data);
+}
+
+/*! \brief Handler function for INIC.MOSTPortEnableFullStreaming.ErrorAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_MOST_PORT_EN_FULL_STR].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostPortEnFullStr_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_MOST_PORT_EN_FULL_STR], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_MOST_PORT_EN_FULL_STR);
+}
+
+/*! \brief Handler function for INIC.MOSTPortEnableFullStreaming.ResultAck
+ * \details Result is delivered via the SingleObserver object ssubs[INIC_SSUB_MOST_PORT_EN_FULL_STR].
+ * \param self Reference to CInic instance
+ * \param msg_ptr Pointer to received message
+ */
+void Inic_MostPortEnFullStr_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_MOST_PORT_EN_FULL_STR], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_MOST_PORT_EN_FULL_STR);
+}
+
+/*! \brief Handler function for INIC.Notification.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_Notification_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NOTIFICATION], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_NOTIFICATION);
+}
+
+/*! \brief Handler function for INIC.Notification.ResultAck
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_Notification_Status(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+ Inic_NotificationResult_t notif_res;
+
+ res_data.data_info = &notif_res;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ MISC_DECODE_WORD(&notif_res.func_id, &(msg_ptr->tel.tel_data_ptr[0]));
+ if (msg_ptr->tel.tel_len == 4U)
+ {
+ MISC_DECODE_WORD(&notif_res.device_id, &(msg_ptr->tel.tel_data_ptr[2]));
+ }
+ else
+ {
+ notif_res.device_id = 0U;
+ }
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_NOTIFICATION], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_NOTIFICATION);
+}
+
+/*! \brief Handler function for INIC.DeviceSync.Error
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_DeviceSync_Error(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ res_data.data_info = NULL;
+ res_data.result = Inic_TranslateError(self_,
+ &msg_ptr->tel.tel_data_ptr[0],
+ (msg_ptr->tel.tel_len));
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_SYNC], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_DEVICE_SYNC);
+}
+
+/*! \brief Handler function for INIC.DeviceSync.Result
+ * \param self reference to INIC object
+ * \param msg_ptr received message
+ */
+void Inic_DeviceSync_Result(void *self, Msg_MostTel_t *msg_ptr)
+{
+ CInic *self_ = (CInic *)self;
+ Inic_StdResult_t res_data;
+
+ MISC_UNUSED(msg_ptr);
+
+ res_data.data_info = NULL;
+ res_data.result.code = UCS_RES_SUCCESS;
+ res_data.result.info_ptr = NULL;
+
+ Ssub_Notify(&self_->ssubs[INIC_SSUB_DEVICE_SYNC], &res_data, true);
+ Al_Release(&self_->lock.res_api, INIC_API_DEVICE_SYNC);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_jobs.c b/ucs2-lib/src/ucs_jobs.c
new file mode 100644
index 0000000..2ef6c8d
--- /dev/null
+++ b/ucs2-lib/src/ucs_jobs.c
@@ -0,0 +1,369 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Job classes
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_MGR
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_jobs.h"
+#include "ucs_misc.h"
+/*#include "ucs_scheduler.h"
+#include "ucs_trace.h"*/
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+static const uint8_t JBS_SRV_PRIO = 246U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Jbs_Service(void *self);
+static void Jbq_OnJobResult(void *self, void *data_ptr);
+static bool Jbs_ForEachJbq(void *d_ptr, void *ud_ptr);
+static bool Jbq_CheckState(CJobQ *self, CJob *job_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* CJobService Methods */
+/*------------------------------------------------------------------------------------------------*/
+void Jbs_Ctor(CJobService *self, CBase *base_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ self->base_ptr = base_ptr;
+ Dl_Ctor(&self->list, base_ptr->ucs_user_ptr);
+ Srv_Ctor(&self->service, JBS_SRV_PRIO, self, &Jbs_Service);
+ (void)Scd_AddService(&self->base_ptr->scd, &self->service);
+}
+
+void Jbs_RegisterJobQ(CJobService *self, CDlNode *job_q_node)
+{
+ Dl_InsertTail(&self->list, job_q_node);
+}
+
+void Jbs_TriggerEvent(CJobService *self, Srv_Event_t id)
+{
+ Srv_SetEvent(&self->service, id);
+}
+
+static bool Jbs_ForEachJbq(void *d_ptr, void *ud_ptr)
+{
+ Srv_Event_t *event_ptr = (Srv_Event_t*)ud_ptr;
+ CJobQ *jobQ_ptr = (CJobQ*)d_ptr;
+
+ if ((*event_ptr & Jbq_GetEventId(jobQ_ptr)) != 0U)
+ {
+ Jbq_Service(jobQ_ptr);
+ }
+
+ return false; /* continue loop for all jobQs */
+}
+
+static void Jbs_Service(void *self)
+{
+ CJobService *self_ = (CJobService *)self;
+ Srv_Event_t event_mask;
+
+ Srv_GetEvent(&self_->service, &event_mask); /* save and reset current events */
+ Srv_ClearEvent(&self_->service, event_mask);
+
+ Dl_Foreach(&self_->list, &Jbs_ForEachJbq, &event_mask); /* service jobQ with the corresponding event */
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* CJobQ Methods */
+/*------------------------------------------------------------------------------------------------*/
+void Jbq_Ctor(CJobQ *self, CJobService *job_service_ptr, Srv_Event_t event_id, CJob *job_list[])
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ self->job_service_ptr = job_service_ptr;
+ self->event_id = event_id;
+ self->job_list = job_list;
+
+ self->index = 0U;
+ self->state = JOB_S_STOPPED;
+ self->result = JOB_R_NA;
+
+ Dln_Ctor(&self->node, self);
+ Ssub_Ctor(&self->q_subject, 0U /*inst id*/);
+ Sobs_Ctor(&self->result_obs, self, &Jbq_OnJobResult);
+ Jbs_RegisterJobQ(self->job_service_ptr, &self->node);
+}
+
+Srv_Event_t Jbq_GetEventId(CJobQ *self)
+{
+ return self->event_id;
+}
+
+void Jbq_Start(CJobQ *self, CSingleObserver *result_obs_ptr)
+{
+ if (self->state != JOB_S_STARTED)
+ {
+ if (self->job_list[self->index] != NULL)
+ {
+ TR_INFO((0U, "[JBQ]", "Jbq_Start(): Starting job queue. Id: 0x%04X", 1U, self->event_id));
+ self->index = 0U;
+ self->state = JOB_S_STARTED;
+ self->result = JOB_R_NA;
+ (void)Ssub_AddObserver(&self->q_subject, result_obs_ptr); /* register observer for finished queue */
+ Job_Start(self->job_list[self->index], &self->result_obs); /* pass own observer for finished job */
+ }
+ else
+ {
+ TR_ERROR((0U, "[JBQ]", "Jbq_Start(): Invalid job list. Id: 0x%04X", 1U, self->event_id));
+ }
+ }
+ else
+ {
+ TR_ERROR((0U, "[JBQ]", "Jbq_Start(): JobQ already started. Id: 0x%04X", 1U, self->event_id));
+ }
+}
+
+void Jbq_Stop(CJobQ *self)
+{
+ if (self->state == JOB_S_STARTED)
+ {
+ if (self->job_list[self->index] != NULL)
+ {
+ self->index = 0U;
+ self->state = JOB_S_STOPPED;
+ self->result = JOB_R_NA;
+ (void)Ssub_RemoveObserver(&self->q_subject);
+ Job_Stop(self->job_list[self->index]);
+ }
+ }
+}
+
+
+static void Jbq_OnJobResult(void *self, void *data_ptr)
+{
+ CJobQ *self_ = (CJobQ *)self;
+ Job_Result_t *result_ptr = (Job_Result_t *)data_ptr;
+
+ if (self_->state == JOB_S_STARTED)
+ {
+ TR_INFO((0U, "[JBQ]", "Jbq_OnJobResult(): Receiving job result. event_id=0x%04X, result=0x%02X", 2U, self_->event_id, *result_ptr));
+ Jbs_TriggerEvent(self_->job_service_ptr, self_->event_id);
+ }
+ else
+ {
+ TR_INFO((0U, "[JBQ]", "Jbq_OnJobResult(): Receiving job result for stopped job. Id: 0x%04X", 1U, self_->event_id));
+ }
+
+ MISC_UNUSED(result_ptr);
+}
+
+
+static bool Jbq_CheckState(CJobQ *self, CJob *job_ptr)
+{
+ bool ret = false;
+
+ if (self->state == JOB_S_STARTED)
+ {
+ if (job_ptr != NULL)
+ {
+ if ((Job_GetState(job_ptr) == JOB_S_FINISHED) && (Job_GetResult(job_ptr) != JOB_R_NA))
+ {
+ ret = true; /* job attributes are correct -> process */
+ }
+ }
+ else
+ {
+ TR_ERROR((0U, "[JBQ]", "Jbq_Service(): Invalid job list. Id: 0x%04X", 1U, self->event_id));
+ }
+ }
+ else
+ {
+ TR_ERROR((0U, "[JBQ]", "Jbq_Service(): JobQ not started. Id: 0x%04X", 1U, self->event_id));
+ }
+
+ return ret;
+}
+
+void Jbq_Service(CJobQ *self)
+{
+ CJob *curr_job_ptr = self->job_list[self->index];
+ CJob *next_job_ptr = self->job_list[self->index + 1U];
+
+ if (Jbq_CheckState(self, curr_job_ptr))
+ {
+ if (curr_job_ptr != NULL)
+ {
+ Job_Result_t tmp_res = Job_GetResult(curr_job_ptr);
+
+ if ((next_job_ptr != NULL) && (tmp_res == JOB_R_SUCCESS)) /* job successfully and next job available */
+ {
+ self->index += 1U;
+ Job_Start(next_job_ptr, &self->result_obs);
+ }
+ else /* current job not successful or last job */
+ {
+ self->result = tmp_res; /* copy status from last job and finish */
+ self->state = JOB_S_FINISHED;
+ Ssub_Notify(&self->q_subject, &tmp_res, true/*auto-remove*/);
+ }
+ }
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* CJob Methods */
+/*------------------------------------------------------------------------------------------------*/
+
+void Job_Ctor(CJob *self, Job_StartCb_t start_fptr, void *inst_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ self->start_fptr = start_fptr;
+ self->inst_ptr = inst_ptr;
+
+ self->state = JOB_S_STOPPED;
+ self->result = JOB_R_NA;
+ Ssub_Ctor(&self->subject, 0U /*ucs instance*/);
+}
+
+void Job_Start(CJob *self, CSingleObserver *result_obs_ptr)
+{
+ if (self->state != JOB_S_STARTED)
+ {
+ TR_ASSERT(0U, "[JOB]", (self->start_fptr != NULL));
+ (void)Ssub_AddObserver(&self->subject, result_obs_ptr);
+ self->state = JOB_S_STARTED;
+ self->result = JOB_R_NA;
+
+ TR_INFO((0U, "[JOB]", "Job_Start(): starting job", 0U));
+ self->start_fptr(self->inst_ptr);
+ }
+ else
+ {
+ TR_INFO((0U, "[JOB]", "Job_Start(): ambiguous state during job start", 0U));
+ }
+}
+
+void Job_Stop(CJob *self)
+{
+ self->state = JOB_S_STOPPED;
+ self->result = JOB_R_NA;
+ Ssub_RemoveObserver(&self->subject);
+ TR_INFO((0U, "[JOB]", "Job_Stop()", 0U));
+}
+
+void Job_SetResult(CJob *self, Job_Result_t result)
+{
+ TR_INFO((0U, "[JOB]", "Job_SetResult(): result=%d", 1U, result));
+
+ if (self->state == JOB_S_STARTED)
+ {
+ self->state = JOB_S_FINISHED;
+ self->result = result;
+ Ssub_Notify(&self->subject, &result, true/*auto-remove*/);
+ MISC_UNUSED(self);
+ MISC_UNUSED(result);
+ }
+ else
+ {
+ TR_ERROR((0U, "[JOB]", "Job_SetResult(): called in ambiguous state=%d", 1U, self->state));
+ }
+}
+
+Job_State_t Job_GetState(CJob *self)
+{
+ return self->state;
+}
+
+Job_Result_t Job_GetResult(CJob *self)
+{
+ return self->result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of Manager class
+ * \param self The instance
+ * \param base_ptr Reference to base component
+ * \param inic_ptr Reference to INIC component
+ * \param net_ptr Reference to net component
+ * \param packet_bw Desired packet bandwidth
+ */
+void Mgr_Ctor(CManager *self, CBase *base_ptr, CInic *inic_ptr, CNetworkManagement *net_ptr, uint16_t packet_bw)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ self->base_ptr = base_ptr;
+ self->inic_ptr = inic_ptr;
+ self->net_ptr = net_ptr;
+ self->packet_bw = packet_bw;
+
+ Srv_Ctor(&self->service, MGR_SRV_PRIO, self, &Mgr_Service); /* register service */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->service);
+
+ Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Mgr_OnInitComplete);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
+
+ Sobs_Ctor(&self->startup_obs, self, &Mgr_OnNwStartupResult);
+ Sobs_Ctor(&self->shutdown_obs, self, &Mgr_OnNwShutdownResult);
+ Mobs_Ctor(&self->nwstatus_mobs, self, MGR_NWSTATUS_MASK, &Mgr_OnNwStatus);
+ Fsm_Ctor(&self->fsm, self, &(mgr_state_tbl[0][0]), (uint8_t)MGR_EV_MAX_NUM_EVENTS, MGR_EV_NIL/*init.event*/);
+}
+
+#endif
+
+
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_lldpool.c b/ucs2-lib/src/ucs_lldpool.c
new file mode 100644
index 0000000..b0b833d
--- /dev/null
+++ b/ucs2-lib/src/ucs_lldpool.c
@@ -0,0 +1,99 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of LLD Message Pool
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PMF
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_lldpool.h"
+#include "ucs_misc.h"
+#include "ucs_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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Lldp_Ctor(CLldPool *self, void *owner_ptr, void* ucs_user_ptr)
+{
+ uint8_t cnt;
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ Dl_Ctor(&self->list, ucs_user_ptr);
+
+ for (cnt = 0U; cnt < LLDP_NUM_HANDLES; cnt++) /* setup LLD Tx handles */
+ {
+ TR_ASSERT(ucs_user_ptr, "[FIFO]", (self->messages[cnt].msg_ptr == NULL) );
+ 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/ucs2-lib/src/ucs_message.c b/ucs2-lib/src/ucs_message.c
new file mode 100644
index 0000000..e39e165
--- /dev/null
+++ b/ucs2-lib/src/ucs_message.c
@@ -0,0 +1,353 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of class message
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_MESSAGE
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_message.h"
+#include "ucs_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)
+{
+/* UCS_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
+ */
+Ucs_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, Ucs_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;
+}
+
+/*! \brief Merges the alternate message id into a the most message id
+ * \param self The instance
+ * \return The alternate message id
+ */
+uint16_t Msg_GetAltMsgId(CMessage *self)
+{
+ uint16_t msg_id;
+ msg_id = (uint16_t)(self->pb_msg.id.function_id >> 4);
+ msg_id = (uint16_t)((uint16_t)self->pb_msg.id.instance_id << 8) | msg_id;
+ return msg_id;
+}
+
+/*! \brief Extracts the alternate message id from a the most message id
+ * \param self The instance
+ * \param alt_id The alternate message id
+ */
+void Msg_SetAltMsgId(CMessage *self, uint16_t alt_id)
+{
+ self->pb_msg.id.fblock_id = MSG_DEF_FBLOCK_ID;
+ self->pb_msg.id.instance_id = MISC_HB(alt_id);
+ self->pb_msg.id.function_id = (uint16_t)((((alt_id) & (uint16_t)0xFF)) << 4) | (uint16_t)MSG_DEF_FUNC_ID_LSN;
+ self->pb_msg.id.op_type = MSG_DEF_OP_TYPE;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_mgr.c b/ucs2-lib/src/ucs_mgr.c
new file mode 100644
index 0000000..23443b9
--- /dev/null
+++ b/ucs2-lib/src/ucs_mgr.c
@@ -0,0 +1,354 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of CManager class
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_MGR
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_mgr.h"
+#include "ucs_misc.h"
+#include "ucs_scheduler.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Priority of the Application Message Service */
+static const uint8_t MGR_SRV_PRIO = 245U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */
+/*! \brief Event which triggers the service */
+static const Srv_Event_t MGR_SRV_EV_SERVICE = 1U;
+
+/*! \brief Network status mask */
+static const uint32_t MGR_NWSTATUS_MASK = 0x0FU; /* parasoft-suppress MISRA2004-8_7 "configuration property" */
+/*! \brief The time in milliseconds the INIC will go to AutoForcedNA after sync lost. */
+static const uint16_t MGR_AUTOFORCED_NA_TIME = 5000U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Mgr_OnInitComplete(void *self, void *error_code_ptr);
+static void Mgr_OnNwStatus(void *self, void *data_ptr);
+static void Mgr_OnJobQResult(void *self, void *result_ptr);
+static void Mgr_Startup(void *self);
+static void Mgr_OnNwStartupResult(void *self, void *result_ptr);
+static void Mgr_LeaveForcedNA(void *self);
+static void Mgr_OnLeaveForcedNAResult(void *self, void *result_ptr);
+#if 0
+static void Mgr_Shutdown(void *self);
+static void Mgr_OnNwShutdownResult(void *self, void *result_ptr);
+#endif
+
+/*------------------------------------------------------------------------------------------------*/
+/* Class methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of Manager class
+ * \param self The instance
+ * \param base_ptr Reference to base component
+ * \param inic_ptr Reference to INIC component
+ * \param net_ptr Reference to net component
+ * \param nd_ptr Reference to NodeDiscovery component
+ * \param packet_bw Desired packet bandwidth
+ */
+void Mgr_Ctor(CManager *self, CBase *base_ptr, CInic *inic_ptr, CNetworkManagement *net_ptr, CNodeDiscovery *nd_ptr, uint16_t packet_bw)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ self->initial = true;
+ self->base_ptr = base_ptr;
+ self->inic_ptr = inic_ptr;
+ self->net_ptr = net_ptr;
+ self->nd_ptr = nd_ptr;
+ self->packet_bw = packet_bw;
+
+ Jbs_Ctor(&self->job_service, base_ptr);
+ Job_Ctor(&self->job_leave_forced_na, &Mgr_LeaveForcedNA, self);
+ Job_Ctor(&self->job_startup, &Mgr_Startup, self);
+#if 0
+ Job_Ctor(&self->job_shutdown, &Mgr_Shutdown, self);
+#endif
+
+ self->list_startup[0] = &self->job_startup;
+ self->list_startup[1] = NULL;
+ self->list_force_startup[0] = &self->job_leave_forced_na;
+ self->list_force_startup[1] = &self->job_startup;
+ self->list_force_startup[2] = NULL;
+#if 0
+ self->list_shutdown[0] = &self->job_shutdown;
+ self->list_shutdown[1] = NULL;
+#endif
+
+ Jbq_Ctor(&self->job_q_startup, &self->job_service, 1U, self->list_startup);
+ Jbq_Ctor(&self->job_q_force_startup, &self->job_service, 2U, self->list_force_startup);
+#if 0
+ Jbq_Ctor(&self->job_q_shutdown, &self->job_service, 4U, self->list_shutdown);
+#endif
+
+ Sobs_Ctor(&self->startup_obs, self, &Mgr_OnNwStartupResult);
+ Sobs_Ctor(&self->force_na_obs, self, &Mgr_OnLeaveForcedNAResult);
+#if 0
+ Sobs_Ctor(&self->shutdown_obs, self, &Mgr_OnNwShutdownResult);
+#endif
+ Sobs_Ctor(&self->job_q_obs, self, &Mgr_OnJobQResult);
+
+ Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Mgr_OnInitComplete);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
+ Mobs_Ctor(&self->nwstatus_mobs, self, MGR_NWSTATUS_MASK, &Mgr_OnNwStatus);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Callback Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Callback function which is invoked if the initialization is complete
+ * \param self The instance
+ * \param error_code_ptr Reference to the error code
+ */
+static void Mgr_OnInitComplete(void *self, void *error_code_ptr)
+{
+ CManager *self_ = (CManager*)self;
+ MISC_UNUSED(error_code_ptr);
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Received init complete event", 0U));
+ Net_AddObserverNetworkStatus(self_->net_ptr, &self_->nwstatus_mobs); /* register observer */
+ (void)Nd_Start(self_->nd_ptr);
+}
+
+/*! \brief NetworkStatus callback function
+ * \details The function is only active if \c listening flag is \c true.
+ * This avoids to re-register und un-register the observer for several times.
+ * \param self The instance
+ * \param data_ptr Reference to \ref Net_NetworkStatusParam_t
+ */
+static void Mgr_OnNwStatus(void *self, void *data_ptr)
+{
+ CManager *self_ = (CManager*)self;
+ Net_NetworkStatusParam_t *param_ptr = (Net_NetworkStatusParam_t *)data_ptr;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwStatus()", 0U));
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwStatus(): mask=0x%04X, events=0x%04X", 2U, param_ptr->change_mask ,param_ptr->events));
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwStatus(): avail=0x%X, avail_i=0x%X, bw=0x%X", 3U, param_ptr->availability, param_ptr->avail_info, param_ptr->packet_bw));
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwStatus(): addr=0x%03X, pos=0x%X, mpr=0x%X", 3U, param_ptr->node_address, param_ptr->node_position, param_ptr->max_position));
+
+ if ((param_ptr->change_mask & ((uint16_t)UCS_NW_M_AVAIL | (uint16_t)UCS_NW_M_PACKET_BW)) != 0U)
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwStatus(): trigger event", 0U));
+
+ if (self_->current_q_ptr != NULL)
+ {
+ Jbq_Stop(self_->current_q_ptr);
+ }
+
+ if (param_ptr->avail_info == UCS_NW_AVAIL_INFO_FORCED_NA)
+ {
+ self_->current_q_ptr = &self_->job_q_force_startup; /* stop forcing NA, then startup */
+ Jbq_Start(&self_->job_q_force_startup, &self_->job_q_obs);
+ }
+ else if (param_ptr->availability == UCS_NW_NOT_AVAILABLE)
+ {
+ self_->current_q_ptr = &self_->job_q_startup; /* just startup */
+ Jbq_Start(&self_->job_q_startup, &self_->job_q_obs);
+ }
+#if 0
+ else if ((param_ptr->node_position != 0U) || (param_ptr->packet_bw != self_->packet_bw))
+ {
+ self_->current_q_ptr = &self_->job_q_shutdown; /* just shutdown - startup is triggered automatically */
+ Jbq_Start(&self_->job_q_shutdown, &self_->job_q_obs);
+ }
+#endif
+ if (self_->initial != false)
+ {
+ self_->initial = false;
+ if (self_->current_q_ptr == NULL) /* trigger InitAll() if no job is required for the */
+ { /* initial network status notification */
+ Nd_InitAll(self_->nd_ptr);
+ }
+ }
+ }
+}
+
+/*! \brief Callback function that is triggered after finished a job.
+ * \details Failed jobs will be restarted here.
+ * \param self The instance
+ * \param result_ptr Reference to the job result \ref Job_Result_t.
+ */
+static void Mgr_OnJobQResult(void *self, void *result_ptr)
+{
+ CManager *self_ = (CManager*)self;
+ Job_Result_t *res = (Job_Result_t *)result_ptr;
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnJobQResult(): result=%d", 1U, *res));
+
+ if ((*res != JOB_R_SUCCESS) && (self_->current_q_ptr != NULL))
+ {
+ Jbq_Start(self_->current_q_ptr, &self_->job_q_obs);
+ }
+ else
+ {
+ self_->current_q_ptr = NULL;
+ }
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Job: LeaveForcedNA */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Action that sets the INIC from "Forced-NotAvailable" to "NotAvailable"
+ * \param self The instance
+ */
+static void Mgr_LeaveForcedNA(void *self)
+{
+ CManager *self_ = (CManager*)self;
+ Ucs_Return_t ret;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_LeaveForcedNA()", 0U));
+ ret = Inic_NwForceNotAvailable(self_->inic_ptr, false /*no longer force NA*/, &self_->force_na_obs);
+
+ if (ret != UCS_RET_SUCCESS)
+ {
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_LeaveForcedNA(), function returns 0x%02X", 1U, ret));
+ Job_SetResult(&self_->job_leave_forced_na, JOB_R_FAILED);
+ }
+}
+
+/*! \brief Callback function which announces the result of Inic_NwForceNotAvailable()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t.
+ */
+static void Mgr_OnLeaveForcedNAResult(void *self, void *result_ptr)
+{
+ CManager *self_ = (CManager*)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnLeaveForcedNAResult(): code=0x%02X", 1U, result_ptr_->result.code));
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Job_SetResult(&self_->job_leave_forced_na, JOB_R_SUCCESS);
+ }
+ else
+ {
+ Job_SetResult(&self_->job_leave_forced_na, JOB_R_FAILED);
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Job: Startup */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Action that starts the network with the given parameters
+ * \param self The instance
+ */
+static void Mgr_Startup(void *self)
+{
+ CManager *self_ = (CManager*)self;
+ Ucs_Return_t ret;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_Startup()", 0U));
+ ret = Inic_NwStartup(self_->inic_ptr, MGR_AUTOFORCED_NA_TIME, self_->packet_bw, &self_->startup_obs); /* Startup without ForcedNA */
+
+ if (ret != UCS_RET_SUCCESS)
+ {
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_Startup(), startup returns 0x%02X", 1U, ret));
+ Job_SetResult(&self_->job_startup, JOB_R_FAILED);
+ }
+}
+
+/*! \brief Callback function which announces the result of Net_NetworkStartup()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t.
+ */
+static void Mgr_OnNwStartupResult(void *self, void *result_ptr)
+{
+ CManager *self_ = (CManager*)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwStartupResult(): code=0x%02X", 1U, result_ptr_->result.code));
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Job_SetResult(&self_->job_startup, JOB_R_SUCCESS);
+ }
+ else
+ {
+ Job_SetResult(&self_->job_startup, JOB_R_FAILED);
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Job: Shutdown */
+/*------------------------------------------------------------------------------------------------*/
+#if 0
+/*! \brief Action that performs a network shutdown.
+ * \param self The instance
+ */
+static void Mgr_Shutdown(void *self)
+{
+ CManager *self_ = (CManager*)self;
+ Ucs_Return_t ret;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_Shutdown()", 0U));
+ ret = Inic_NwShutdown(self_->inic_ptr, &self_->shutdown_obs);
+
+ if (ret != UCS_RET_SUCCESS)
+ {
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_Shutdown(), shutdown returns 0x%02X", 1U, ret));
+ Job_SetResult(&self_->job_shutdown, JOB_R_FAILED);
+ }
+}
+
+/*! \brief Callback function which announces the result of Net_NetworkShutdown()
+ * \param self The instance
+ * \param result_ptr Reference to result. Must be casted into Inic_StdResult_t.
+ */
+static void Mgr_OnNwShutdownResult(void *self, void *result_ptr)
+{
+ CManager *self_ = (CManager*)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[MGR]", "Mgr_OnNwShutdownResult(): code=0x%02X", 1U, result_ptr_->result.code));
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Job_SetResult(&self_->job_shutdown, JOB_R_SUCCESS);
+ }
+ else
+ {
+ Job_SetResult(&self_->job_shutdown, JOB_R_FAILED);
+ }
+}
+#endif
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_misc.c b/ucs2-lib/src/ucs_misc.c
new file mode 100644
index 0000000..8c018a4
--- /dev/null
+++ b/ucs2-lib/src/ucs_misc.c
@@ -0,0 +1,80 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the library module which contains miscellaneous helper functions.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_MISC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief UNICENS 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 UNICENS 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/ucs2-lib/src/ucs_net.c b/ucs2-lib/src/ucs_net.c
new file mode 100644
index 0000000..4e4b274
--- /dev/null
+++ b/ucs2-lib/src/ucs_net.c
@@ -0,0 +1,310 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Network Management.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_NET
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_net.h"
+#include "ucs_misc.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the NET service used by scheduler */
+static const uint8_t NET_SRV_PRIO = 251U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Event to trigger notification of MOST Network Status */
+static const Srv_Event_t NET_EVENT_NOTIFY_NW_STATUS = 1U;
+/*! \brief Event to trigger notification of MOST Network Configuration */
+static const Srv_Event_t NET_EVENT_NOTIFY_NW_CONFIG = 2U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Default value used for INIC sender handles */
+static const uint16_t NET_DEFAULT_SENDER_HANDLE = 0x0001U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Initialization timeout in milliseconds (t = 7s) */
+static const uint16_t NET_PBW_TIMEOUT = 7000U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Net_Service(void *self);
+static void Net_UpdateNetworkStatus(void *self, void *data_ptr);
+static void Net_UpdateNetworkConfiguration(void *self, void *data_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CNetworkManagement */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Network Management class.
+ * \param self Instance pointer
+ * \param init_ptr Reference to the initialization data
+ */
+void Net_Ctor(CNetworkManagement *self, Net_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->base_ptr = init_ptr->base_ptr;
+ self->inic_ptr = init_ptr->inic_ptr;
+ Obs_Ctor(&self->network_status.observer, self, &Net_UpdateNetworkStatus);
+ Inic_AddObsrvNwStatus(self->inic_ptr, &self->network_status.observer);
+ self->network_status.param.change_mask = 0xFFFFU; /* Used for initial notification! */
+ Sub_Ctor(&self->network_status.pre_subject, self->base_ptr->ucs_user_ptr);
+ Sub_Ctor(&self->network_status.subject, self->base_ptr->ucs_user_ptr);
+
+ Obs_Ctor(&self->network_configuration.observer, self, &Net_UpdateNetworkConfiguration);
+ Inic_AddObsvrNwConfig(self->inic_ptr, &self->network_configuration.observer);
+ self->network_configuration.param.change_mask = 0xFFFFU; /* Used for initial notification! */
+ Sub_Ctor(&self->network_configuration.pre_subject, self->base_ptr->ucs_user_ptr);
+ Sub_Ctor(&self->network_configuration.subject, self->base_ptr->ucs_user_ptr);
+
+ Srv_Ctor(&self->net_srv, NET_SRV_PRIO, self, &Net_Service); /* Initialize Network Management service */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->net_srv); /* Add NET service to scheduler */
+}
+
+/*! \brief Service function of the network management.
+ * \param self Instance pointer
+ */
+static void Net_Service(void *self)
+{
+ CNetworkManagement *self_ = (CNetworkManagement *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->net_srv, &event_mask);
+ /* Notification of MOST Network Status triggered? */
+ if((event_mask & NET_EVENT_NOTIFY_NW_STATUS) == NET_EVENT_NOTIFY_NW_STATUS)
+ {
+ Srv_ClearEvent(&self_->net_srv, NET_EVENT_NOTIFY_NW_STATUS);
+ self_->network_status.param.change_mask = 0xFFFFU;
+ Sub_Notify(&self_->network_status.pre_subject, &self_->network_status.param);
+ self_->network_status.param.change_mask = 0U;
+ (void)Sub_SwitchObservers(&self_->network_status.subject,
+ &self_->network_status.pre_subject);
+ }
+ /* Notification of MOST Network Configuration triggered? */
+ if((event_mask & NET_EVENT_NOTIFY_NW_CONFIG) == NET_EVENT_NOTIFY_NW_CONFIG)
+ {
+ Srv_ClearEvent(&self_->net_srv, NET_EVENT_NOTIFY_NW_CONFIG);
+ self_->network_configuration.param.change_mask = 0xFFFFU;
+ Sub_Notify(&self_->network_configuration.pre_subject, &self_->network_configuration.param);
+ self_->network_configuration.param.change_mask = 0U;
+ (void)Sub_SwitchObservers(&self_->network_configuration.subject,
+ &self_->network_configuration.pre_subject);
+ }
+}
+
+/*! \brief Adds an observer which is called if the network status has been changed.
+ * \param self Instance pointer
+ * \param obs_ptr Reference to an observer
+ */
+void Net_AddObserverNetworkStatus(CNetworkManagement *self, CMaskedObserver *obs_ptr)
+{
+ (void)Msub_AddObserver(&self->network_status.pre_subject, obs_ptr);
+ Srv_SetEvent(&self->net_srv, NET_EVENT_NOTIFY_NW_STATUS);
+}
+
+/*! \brief Removes an observer registered by Net_AddObserverNetworkStatus().
+ * \param self Instance pointer
+ * \param obs_ptr Reference to observer to be removed
+
+ */
+void Net_DelObserverNetworkStatus(CNetworkManagement *self, CMaskedObserver *obs_ptr)
+{
+ (void)Msub_RemoveObserver(&self->network_status.pre_subject, obs_ptr);
+ (void)Msub_RemoveObserver(&self->network_status.subject, obs_ptr);
+}
+
+/*! \brief Adds an observer which is called if the network configuration has been changed.
+ * \param self Instance pointer
+ * \param obs_ptr Reference to an observer
+ */
+void Net_AddObserverNetworkConfig(CNetworkManagement *self, CMaskedObserver *obs_ptr)
+{
+ (void)Msub_AddObserver(&self->network_configuration.pre_subject, obs_ptr);
+ Srv_SetEvent(&self->net_srv, NET_EVENT_NOTIFY_NW_CONFIG);
+}
+
+/*! \brief Removes an observer registered by Net_AddObserverNetworkConfig().
+ * \param self Instance pointer
+ * \param obs_ptr Reference to observer to be removed
+ */
+void Net_DelObserverNetworkConfig(CNetworkManagement *self, CMaskedObserver *obs_ptr)
+{
+ (void)Msub_RemoveObserver(&self->network_configuration.pre_subject, obs_ptr);
+ (void)Msub_RemoveObserver(&self->network_configuration.subject, obs_ptr);
+}
+
+/*! \brief Observer callback used for the MOST Network Status
+ * \param self Instance pointer
+ * \param data_ptr Reference to the data structure
+ */
+static void Net_UpdateNetworkStatus(void *self, void *data_ptr)
+{
+ Inic_StdResult_t *data_ptr_ = (Inic_StdResult_t *)data_ptr;
+
+ if(data_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ CNetworkManagement *self_ = (CNetworkManagement *)self;
+ Inic_NetworkStatus_t result = *((Inic_NetworkStatus_t *)data_ptr_->data_info);
+
+ /* Check for changes */
+ if(result.events != 0U) /* Notify only if at least one event flag is set */
+ {
+ self_->network_status.param.change_mask |= 0x0001U;
+ }
+ if(self_->network_status.param.availability != result.availability)
+ {
+ self_->network_status.param.change_mask |= 0x0002U;
+ }
+ if(self_->network_status.param.avail_info != result.avail_info)
+ {
+ self_->network_status.param.change_mask |= 0x0004U;
+ }
+ if(self_->network_status.param.avail_trans_cause != result.avail_trans_cause)
+ {
+ self_->network_status.param.change_mask |= 0x0008U;
+ }
+ if(self_->network_status.param.node_address != result.node_address)
+ {
+ self_->network_status.param.change_mask |= 0x0010U;
+ }
+ if(self_->network_status.param.node_position != result.node_position)
+ {
+ self_->network_status.param.change_mask |= 0x0020U;
+ }
+ if(self_->network_status.param.max_position != result.max_position)
+ {
+ self_->network_status.param.change_mask |= 0x0040U;
+ }
+ if(self_->network_status.param.packet_bw != result.packet_bw)
+ {
+ self_->network_status.param.change_mask |= 0x0080U;
+ }
+
+ /* Update MOST Network Status parameters */
+ self_->network_status.param.events = result.events;
+ self_->network_status.param.availability = result.availability;
+ self_->network_status.param.avail_info = result.avail_info;
+ self_->network_status.param.avail_trans_cause = result.avail_trans_cause;
+ self_->network_status.param.node_address = result.node_address;
+ self_->network_status.param.node_position = result.node_position;
+ self_->network_status.param.max_position = result.max_position;
+ self_->network_status.param.packet_bw = result.packet_bw;
+
+ /* Notify observer? */
+ Msub_Notify(&self_->network_status.subject,
+ &self_->network_status.param,
+ (uint32_t)self_->network_status.param.change_mask);
+
+ /* Clear change-mask */
+ self_->network_status.param.change_mask = 0U;
+ }
+}
+
+/*! \brief Observer callback used for the MOST Network Configuration
+ * \param self Instance pointer
+ * \param data_ptr Reference to the data structure
+ */
+static void Net_UpdateNetworkConfiguration(void *self, void *data_ptr)
+{
+ Inic_StdResult_t *data_ptr_ = (Inic_StdResult_t *)data_ptr;
+
+ if(data_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ CNetworkManagement *self_ = (CNetworkManagement *)self;
+ Inic_NetworkConfig_t result = *((Inic_NetworkConfig_t *)data_ptr_->data_info);
+
+ /* Check for changes */
+ if(self_->network_configuration.param.node_address != result.node_address)
+ {
+ self_->network_configuration.param.change_mask |= 0x0001U;
+ }
+ if(self_->network_configuration.param.group_address != result.group_address)
+ {
+ self_->network_configuration.param.change_mask |= 0x0002U;
+ }
+ if(self_->network_configuration.param.llrbc != result.llrbc)
+ {
+ self_->network_configuration.param.change_mask |= 0x0004U;
+ }
+
+ /* Update MOST Network Configuration parameters */
+ self_->network_configuration.param.node_address = result.node_address;
+ self_->network_configuration.param.group_address = result.group_address;
+ self_->network_configuration.param.llrbc = result.llrbc;
+
+ /* Notify observer? */
+ Msub_Notify(&self_->network_configuration.subject,
+ &self_->network_configuration.param,
+ (uint32_t)self_->network_configuration.param.change_mask);
+
+ /* Clear change-mask */
+ self_->network_configuration.param.change_mask = 0U;
+ }
+}
+
+/*! \brief Checks if the given address matches with the own node address, node position or group address.
+ * \param self Instance pointer
+ * \param address Address to be checked
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------- | -------------------------------------------------------------
+ * NET_IS_OWN_ADDR_NODE | Is own node position address or own logical node address
+ * NET_IS_OWN_ADDR_GROUP | Is own group address
+ * NET_IS_OWN_ADDR_NONE | Is foreign address
+ */
+Net_IsOwnAddrResult_t Net_IsOwnAddress(CNetworkManagement *self, uint16_t address)
+{
+ Net_IsOwnAddrResult_t ret_val;
+
+ if((self->network_status.param.node_address == address) ||
+ (((uint16_t)self->network_status.param.node_position + (uint16_t)0x400) == address))
+ {
+ ret_val = NET_IS_OWN_ADDR_NODE;
+ }
+ else if(self->network_configuration.param.group_address == address)
+ {
+ ret_val = NET_IS_OWN_ADDR_GROUP;
+ }
+ else
+ {
+ ret_val = NET_IS_OWN_ADDR_NONE;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_nodedis.c b/ucs2-lib/src/ucs_nodedis.c
new file mode 100644
index 0000000..b9f2f5a
--- /dev/null
+++ b/ucs2-lib/src/ucs_nodedis.c
@@ -0,0 +1,1038 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Node Discovery.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_NODE_DIS
+ * @{
+
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_inic_pb.h"
+#include "ucs_nodedis.h"
+#include "ucs_misc.h"
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+#define ND_NUM_STATES 5U /*!< \brief Number of state machine states */
+#define ND_NUM_EVENTS 14U /*!< \brief Number of state machine events */
+
+#define ND_TIMEOUT_PERIODIC 5000U /*!< \brief 5s timeout */
+#define ND_TIMEOUT_COMMAND 300U /*!< \brief supervise EXC commands */
+
+#define ND_SIGNATURE_VERSION 1U /*!< \brief signature version used for Node Discovery */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the Node Discovery service used by scheduler */
+static const uint8_t ND_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! Main event for the Node Discovery service */
+static const Srv_Event_t ND_EVENT_SERVICE = 1U;
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal enumerators */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Possible events of the Node Discovery state machine */
+typedef enum Nd_Events_
+{
+ ND_E_NIL = 0U, /*!< \brief NIL Event */
+ ND_E_START = 1U, /*!< \brief API start command was called. */
+ ND_E_STOP = 2U, /*!< \brief Stop request occurred. */
+ ND_E_CHECK = 3U, /*!< \brief Check conditions in CHECK_HELLO state. */
+ ND_E_NET_OFF = 4U, /*!< \brief NetOff occurred. */
+ ND_E_HELLO_STATUS = 5U, /*!< \brief Hello.Status message available to be processed. */
+ ND_E_RES_NODE_OK = 6U, /*!< \brief Evaluation result of node: ok. */
+ ND_E_RES_UNKNOWN = 7U, /*!< \brief Evaluation result of node: unknown node. */
+ ND_E_RES_CHECK_UNIQUE = 8U, /*!< \brief Evaluation result of node: check if node is unique. */
+ ND_E_WELCOME_SUCCESS = 9U, /*!< \brief Welcome command was successful. */
+ ND_E_WELCOME_NOSUCCESS = 10U, /*!< \brief Welcome command was not successful. */
+ ND_E_SIGNATURE_SUCCESS = 11U, /*!< \brief Signature command was successful. */
+ ND_E_TIMEOUT = 12U, /*!< \brief Timeout occurred. */
+ ND_E_ERROR = 13U /*!< \brief An unexpected error occurred. */
+
+} Nd_Events_t;
+
+
+/*! \brief States of the Node Discovery state machine */
+typedef enum Nd_State_
+{
+ ND_S_IDLE = 0U, /*!< \brief Idle state */
+ ND_S_CHECK_HELLO = 1U, /*!< \brief Node Discovery started */
+ ND_S_WAIT_EVAL = 2U, /*!< \brief Evaluate next Hello.Status message */
+ ND_S_WAIT_WELCOME = 3U, /*!< \brief Wait for Welcome.Status */
+ ND_S_WAIT_PING = 4U /*!< \brief Wait for Signature.Status */
+} Nd_State_t;
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Nd_Service(void *self);
+
+static void Nd_HelloStatusCb(void *self, void *result_ptr);
+static void Nd_WelcomeResultCb(void *self, void *result_ptr);
+static void Nd_SignatureStatusCb(void *self, void *result_ptr);
+static void Nd_InitCb(void *self, void *result_ptr);
+static void Nd_TimerCb(void *self);
+static void Nd_OnTerminateEventCb(void *self, void *result_ptr);
+static void Nd_NetworkStatusCb(void *self, void *result_ptr);
+
+static void Nd_Reset_Lists(void *self);
+
+static void Nd_A_Start(void *self);
+static void Nd_A_Stop(void *self);
+static void Nd_A_CheckConditions(void *self);
+static void Nd_A_Eval_Hello(void *self);
+static void Nd_A_Welcome(void *self);
+static void Nd_A_Unknown(void *self);
+static void Nd_A_CheckUnique(void *self);
+static void Nd_A_WelcomeSuccess(void *self);
+static void Nd_A_WelcomeNoSuccess(void *self);
+static void Nd_A_WelcomeTimeout(void *self);
+static void Nd_A_Timeout_Hello(void *self);
+static void Nd_A_NetOff(void *self);
+static void Nd_A_Signature_Timeout(void *self);
+static void Nd_A_Signature_Success(void *self);
+static void Nd_A_Error(void *self);
+
+static void Nd_Send_Hello_Get(void *self);
+static void Nd_Start_Periodic_Timer(void *self);
+static void Nd_Send_Welcome_SR(void *self, Ucs_Signature_t *signature);
+static void Nd_Send_Signature_Get(void *self, uint16_t target_address);
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* State transition table (used by finite state machine) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief State transition table */
+static const Fsm_StateElem_t nd_trans_tab[ND_NUM_STATES][ND_NUM_EVENTS] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+ { /* State ND_S_IDLE */
+ /* ND_E_NIL */ {NULL, ND_S_IDLE },
+ /* ND_E_START */ {&Nd_A_Start, ND_S_CHECK_HELLO },
+ /* ND_E_STOP */ {NULL, ND_S_IDLE },
+ /* ND_E_CHECK */ {NULL, ND_S_IDLE },
+ /* ND_E_NET_OFF */ {NULL, ND_S_IDLE },
+ /* ND_E_HELLO_STATUS */ {NULL, ND_S_IDLE },
+ /* ND_E_RES_NODE_OK */ {NULL, ND_S_IDLE },
+ /* ND_E_RES_UNKNOWN */ {NULL, ND_S_IDLE },
+ /* ND_E_RES_CHECK_UNIQUE */ {NULL, ND_S_IDLE },
+ /* ND_E_WELCOME_SUCCESS */ {NULL, ND_S_IDLE },
+ /* ND_E_WELCOME_NOSUCCESS */ {NULL, ND_S_IDLE },
+ /* ND_E_SIGNATURE_SUCCESS */ {NULL, ND_S_IDLE },
+ /* ND_E_TIMEOUT */ {NULL, ND_S_IDLE },
+ /* ND_E_ERROR */ {NULL, ND_S_IDLE }
+
+ },
+ { /* State ND_S_CHECK_HELLO */
+ /* ND_E_NIL */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_START */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_STOP */ {&Nd_A_Stop, ND_S_IDLE },
+ /* ND_E_CHECK */ {&Nd_A_CheckConditions, ND_S_CHECK_HELLO },
+ /* ND_E_NET_OFF */ {&Nd_A_NetOff, ND_S_CHECK_HELLO },
+ /* ND_E_HELLO_STATUS */ {&Nd_A_Eval_Hello, ND_S_WAIT_EVAL },
+ /* ND_E_RES_NODE_OK */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_RES_UNKNOWN */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_RES_CHECK_UNIQUE */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_WELCOME_SUCCESS */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_WELCOME_NOSUCCESS */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_SIGNATURE_SUCCESS */ {NULL, ND_S_CHECK_HELLO },
+ /* ND_E_TIMEOUT */ {&Nd_A_Timeout_Hello, ND_S_CHECK_HELLO },
+ /* ND_E_ERROR */ {&Nd_A_Error, ND_S_IDLE }
+ },
+ { /* State ND_S_WAIT_EVAL */
+ /* ND_E_NIL */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_START */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_STOP */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_CHECK */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_NET_OFF */ {&Nd_A_NetOff, ND_S_CHECK_HELLO },
+ /* ND_E_HELLO_STATUS */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_RES_NODE_OK */ {&Nd_A_Welcome, ND_S_WAIT_WELCOME },
+ /* ND_E_RES_UNKNOWN */ {&Nd_A_Unknown, ND_S_CHECK_HELLO },
+ /* ND_E_RES_CHECK_UNIQUE */ {&Nd_A_CheckUnique, ND_S_WAIT_PING },
+ /* ND_E_WELCOME_SUCCESS */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_WELCOME_NOSUCCESS */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_SIGNATURE_SUCCESS */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_TIMEOUT */ {NULL, ND_S_WAIT_EVAL },
+ /* ND_E_ERROR */ {&Nd_A_Error, ND_S_IDLE }
+ },
+
+ {/* ND_S_WAIT_WELCOME */
+ /* ND_E_NIL */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_START */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_STOP */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_CHECK */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_NET_OFF */ {&Nd_A_NetOff, ND_S_CHECK_HELLO },
+ /* ND_E_HELLO_STATUS */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_RES_NODE_OK */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_RES_UNKNOWN */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_RES_CHECK_UNIQUE */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_WELCOME_SUCCESS */ {&Nd_A_WelcomeSuccess, ND_S_CHECK_HELLO },
+ /* ND_E_WELCOME_NOSUCCESS */ {&Nd_A_WelcomeNoSuccess, ND_S_CHECK_HELLO },
+ /* ND_E_SIGNATURE_SUCCESS */ {NULL, ND_S_WAIT_WELCOME },
+ /* ND_E_TIMEOUT */ {&Nd_A_WelcomeTimeout, ND_S_CHECK_HELLO },
+ /* ND_E_ERROR */ {&Nd_A_Error, ND_S_IDLE }
+ },
+ {/* ND_S_WAIT_PING */
+ /* ND_E_NIL */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_START */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_STOP */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_CHECK */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_NET_OFF */ {&Nd_A_NetOff, ND_S_CHECK_HELLO },
+ /* ND_E_HELLO_STATUS */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_RES_NODE_OK */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_RES_UNKNOWN */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_RES_CHECK_UNIQUE */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_WELCOME_SUCCESS */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_WELCOME_NOSUCCESS */ {NULL, ND_S_WAIT_PING },
+ /* ND_E_SIGNATURE_SUCCESS */ {&Nd_A_Signature_Success, ND_S_CHECK_HELLO },
+ /* ND_E_TIMEOUT */ {&Nd_A_Signature_Timeout, ND_S_WAIT_WELCOME },
+ /* ND_E_ERROR */ {&Nd_A_Error, ND_S_IDLE }
+ }
+};
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of class CNodeDiscovery.
+ * \param self Reference to CNodeDiscovery instance
+ * \param inic Reference to CInic instance
+ * \param base Reference to CBase instance
+ * \param exc Reference to CExc instance
+ * \param init_ptr Report callback function
+ */
+void Nd_Ctor(CNodeDiscovery *self, CInic *inic, CBase *base, CExc *exc, Nd_InitData_t *init_ptr)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self));
+
+ self->inic = inic;
+ self->exc = exc;
+ self->base = base;
+ self->cb_inst_ptr = init_ptr->inst_ptr;
+ self->report_fptr = init_ptr->report_fptr;
+ self->eval_fptr = init_ptr->eval_fptr;
+
+ Fsm_Ctor(&self->fsm, self, &(nd_trans_tab[0][0]), ND_NUM_EVENTS, ND_E_NIL);
+
+ Nd_Reset_Lists(self);
+
+ Sobs_Ctor(&self->nd_hello, self, &Nd_HelloStatusCb);
+ Sobs_Ctor(&self->nd_welcome, self, &Nd_WelcomeResultCb);
+ Sobs_Ctor(&self->nd_signature, self, &Nd_SignatureStatusCb);
+ Sobs_Ctor(&self->nd_init, self, &Nd_InitCb);
+
+ /* register termination events */
+ Mobs_Ctor(&self->nd_terminate, self, EH_M_TERMINATION_EVENTS, &Nd_OnTerminateEventCb);
+ Eh_AddObsrvInternalEvent(&self->base->eh, &self->nd_terminate);
+
+ /* Register NetOn and MPR events */
+ Obs_Ctor(&self->nd_nwstatus, self, &Nd_NetworkStatusCb);
+ Inic_AddObsrvNwStatus(self->inic, &self->nd_nwstatus);
+ self->neton = false;
+
+ /* Initialize Node Discovery service */
+ Srv_Ctor(&self->service, ND_SRV_PRIO, self, &Nd_Service);
+ /* Add Node Discovery service to scheduler */
+ (void)Scd_AddService(&self->base->scd, &self->service);
+
+}
+
+
+/*! \brief Service function of the Node Discovery service.
+ * \param self Reference to Node Discovery object
+ */
+static void Nd_Service(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->service, &event_mask);
+ if(ND_EVENT_SERVICE == (event_mask & ND_EVENT_SERVICE)) /* Is event pending? */
+ {
+ Fsm_State_t result;
+ Srv_ClearEvent(&self_->service, ND_EVENT_SERVICE);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "FSM __ %d %d", 2U, self_->fsm.current_state, self_->fsm.event_occured));
+ result = Fsm_Service(&self_->fsm);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[ND]", (result != FSM_STATE_ERROR));
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "FSM -> %d", 1U, self_->fsm.current_state));
+ MISC_UNUSED(result);
+ }
+}
+
+
+
+/**************************************************************************************************/
+/* API functions */
+/**************************************************************************************************/
+/*! \brief Start the Node Discovery
+ *
+ * \param *self Reference to Node Discovery object
+ * \return UCS_RET_SUCCESS Operation successful
+ * \return UCS_RET_ERR_API_LOCKED Node Discovery was already started
+ */
+Ucs_Return_t Nd_Start(CNodeDiscovery *self)
+{
+ Ucs_Return_t ret_val = UCS_RET_SUCCESS;
+
+
+ if (self->running == false)
+ {
+ Fsm_SetEvent(&self->fsm, ND_E_START);
+ Srv_SetEvent(&self->service, ND_EVENT_SERVICE);
+ self->running = true;
+ TR_INFO((self->base->ucs_user_ptr, "[ND]", "Nd_Start", 0U));
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_API_LOCKED;
+ TR_INFO((self->base->ucs_user_ptr, "[ND]", "Nd_Start failed", 0U));
+ }
+
+ return ret_val;
+
+
+
+
+}
+
+/*! \brief Stops the Node Discovery
+ *
+ * \param *self Reference to Node Discovery object
+ * \return UCS_RET_SUCCESS Operation successful
+ * \return UCS_RET_ERR_NOT_AVAILABLE Node Discovery not running
+ */
+Ucs_Return_t Nd_Stop(CNodeDiscovery *self)
+{
+ Ucs_Return_t ret_val = UCS_RET_SUCCESS;
+
+ if (self->running == true) /* check if Node Discovery was started */
+ {
+ self->stop_request = true;
+ Fsm_SetEvent(&self->fsm, ND_E_CHECK);
+ Srv_SetEvent(&self->service, ND_EVENT_SERVICE);
+
+ TR_INFO((self->base->ucs_user_ptr, "[ND]", "Nd_Stop", 0U));
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ TR_INFO((self->base->ucs_user_ptr, "[ND]", "Nd_Stop failed", 0U));
+ }
+
+ return ret_val;
+}
+
+
+/*! \brief Sends the Init command to all nodes
+ *
+ * \param *self Reference to Node Discovery object
+ */
+void Nd_InitAll(CNodeDiscovery *self)
+{
+ Ucs_Return_t result;
+
+ result = Exc_DeviceInit_Start(self->exc, UCS_ADDR_BROADCAST_BLOCKING, NULL);
+ if (result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base->ucs_user_ptr, "[ND]", "Nd_InitAll", 0U));
+ }
+ else
+ {
+ TR_INFO((self->base->ucs_user_ptr, "[ND]", "Nd_InitAll failed", 0U));
+ }
+
+}
+
+
+
+
+/**************************************************************************************************/
+/* FSM Actions */
+/**************************************************************************************************/
+/*! \brief Action on start event
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Start(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ /* empty new_list*/
+ Nd_Reset_Lists(self_);
+
+ Nd_Send_Hello_Get(self_);
+
+ Nd_Start_Periodic_Timer(self_);
+
+ self_->stop_request = false;
+ self_->hello_mpr_request = false;
+ self_->hello_neton_request = false;
+}
+
+/*! \brief Action on stop event
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Stop(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t *dummy = NULL;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_STOPPED, dummy);
+ }
+ self_->running = false;
+}
+
+/*! \brief Check conditions
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_CheckConditions(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ if (self_->stop_request == true)
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_STOP);
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+ }
+ else if (self_->hello_mpr_request == true)
+ {
+ Nd_Reset_Lists(self_);
+ Nd_Send_Hello_Get(self_);
+ Nd_Start_Periodic_Timer(self_);
+ self_->hello_mpr_request = false;
+ self_->hello_neton_request = false;
+ }
+ else if (self_->hello_neton_request == true)
+ {
+ Nd_Send_Hello_Get(self_);
+ Nd_Start_Periodic_Timer(self_);
+ self_->hello_neton_request = false;
+ }
+ else if (Dl_GetSize(&(self_->new_list)) > 0U)
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_HELLO_STATUS);
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+ }
+ else
+ {
+ Nd_Start_Periodic_Timer(self_);
+ }
+
+}
+
+
+/*! \brief Evaluate the signature of the next node
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Eval_Hello(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ CDlNode *node;
+ Ucs_Nd_CheckResult_t result;
+ bool service_flag = false;
+ Ucs_Signature_t temp_sig;
+
+ if (Dl_GetSize(&(self_->new_list)) > 0U)
+ {
+ node = Dl_PopHead(&(self_->new_list));
+ self_->current_sig = *((Ucs_Signature_t *)(node->data_ptr));
+
+ if (self_->eval_fptr != NULL)
+ {
+ temp_sig = self_->current_sig; /* provide only a copy to the application */
+ result = self_->eval_fptr(self_->cb_inst_ptr, &temp_sig);
+
+ switch (result)
+ {
+ case UCS_ND_CHK_UNKNOWN:
+ Fsm_SetEvent(&self_->fsm, ND_E_RES_UNKNOWN);
+ service_flag = true;
+ break;
+
+ case UCS_ND_CHK_WELCOME:
+ Fsm_SetEvent(&self_->fsm, ND_E_RES_NODE_OK);
+ service_flag = true;
+ break;
+
+ case UCS_ND_CHK_UNIQUE:
+ Fsm_SetEvent(&self_->fsm, ND_E_RES_CHECK_UNIQUE);
+ service_flag = true;
+ break;
+
+ default:
+ Fsm_SetEvent(&self_->fsm, ND_E_ERROR);
+ service_flag = true;
+ break;
+ }
+ }
+ }
+
+ if (service_flag == true)
+ {
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+ }
+}
+
+
+/*! \brief Sends a Welcome message to the current node
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Welcome(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ Nd_Send_Welcome_SR(self, &self_->current_sig);
+}
+
+
+/*! \brief Report the current node as unknown
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Unknown(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t temp_sig;
+
+ if (self_->report_fptr != NULL)
+ {
+ temp_sig = self_->current_sig; /* provide only a copy to the application */
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_UNKNOWN, &temp_sig);
+ }
+
+ Fsm_SetEvent(&self_->fsm, ND_E_CHECK);
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+/*! \brief Check if the current node has already got a Welcome message
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_CheckUnique(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ Nd_Send_Signature_Get(self, self_->current_sig.node_address);
+
+}
+
+
+/*! \brief Report a successful Welcome.Result
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_WelcomeSuccess(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t temp_sig;
+
+ if (self_->report_fptr != NULL)
+ {
+ temp_sig = self_->current_sig; /* provide only a copy to the application */
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_WELCOME_SUCCESS, &temp_sig);
+ }
+
+ /* initiate a Hello.Get if the current node is the local INIC */
+ if (self_->current_sig.node_pos_addr == 0x0400U)
+ {
+ Nd_Send_Hello_Get(self_);
+ Nd_Start_Periodic_Timer(self_);
+ }
+
+ Fsm_SetEvent(&self_->fsm, ND_E_CHECK);
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! \brief Report an unsuccessful Welcome.Result
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_WelcomeNoSuccess(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ /* same reaction as for MPR event */
+ self_->hello_mpr_request = true;
+
+ Fsm_SetEvent(&self_->fsm, ND_E_CHECK);
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! \brief Reaction on a timeout for the Welcome messsage
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_WelcomeTimeout(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ /* same reaction as for MPR event */
+ self_->hello_mpr_request = true;
+
+ Fsm_SetEvent(&self_->fsm, ND_E_CHECK);
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! \brief The periodic timer elapsed
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Timeout_Hello(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ Nd_Send_Hello_Get(self_);
+ Nd_Start_Periodic_Timer(self_);
+}
+
+
+/*! \brief Reaction on a NetOff event
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_NetOff(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t *dummy = NULL;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_NETOFF, dummy);
+ }
+
+ Nd_Reset_Lists(self_);
+
+}
+
+
+/*! \brief Reaction on a timeout of the Signature command
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Signature_Timeout(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ Nd_Send_Welcome_SR(self, &self_->current_sig);
+}
+
+
+/*! \brief Reaction on a successful Signature answer
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Signature_Success(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t temp_sig;
+
+ if (self_->report_fptr != NULL)
+ {
+ temp_sig = self_->current_sig; /* provide only a copy to the application */
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_MULTI, &temp_sig);
+ }
+}
+
+
+/*! \brief An unecpected error occurred
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_A_Error(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t *dummy = NULL;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_ERROR, dummy);
+ }
+ self_->running = false;
+}
+
+
+/**************************************************************************************************/
+/* Callback functions */
+/**************************************************************************************************/
+
+/*! Callback function for the Exc.Hello.Status message
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Result of the Exc_Hello_Get() command
+ */
+static void Nd_HelloStatusCb(void *self, void *result_ptr)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+ CDlNode *node;
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ /* read signature and store it in the new_list */
+ node = Dl_PopHead(&(self_->unused_list)); /* get an unused list element */
+ if (node != NULL)
+ {
+ ((Nd_Node *)(node->data_ptr))->signature = (*(Exc_HelloStatus_t *)(result_ptr_->data_info)).signature;
+ Dl_InsertTail(&(self_->new_list), node);
+
+ Fsm_SetEvent(&self_->fsm, ND_E_HELLO_STATUS);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_HelloStatusCb UCS_RES_SUCCESS", 0U));
+ }
+ else
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_HelloStatusCb No list entry av.", 0U));
+ }
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_HelloStatusCb ND_E_ERROR", 0U));
+ }
+
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! \brief Function is called on reception of the Welcome.Result messsage
+ * \param self Reference to Node Discovery object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Nd_WelcomeResultCb(void *self, void *result_ptr)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ /* read signature and store it */
+ self_->welcome_result = *(Exc_WelcomeResult_t *)(result_ptr_->data_info);
+ if (self_->welcome_result.res == EXC_WELCOME_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_WELCOME_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_WelcomeResultCb ND_E_WELCOME_SUCCESS", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_WELCOME_NOSUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_WelcomeResultCb ND_E_WELCOME_NOSUCCESS", 0U));
+ }
+ }
+ else
+ {
+ uint8_t i;
+
+ if ( (result_ptr_->result.info_size == 3U)
+ && (result_ptr_->result.info_ptr[0] == 0x20U)
+ && (result_ptr_->result.info_ptr[1] == 0x03U)
+ && (result_ptr_->result.info_ptr[2] == 0x31U))
+ { /* Device has not yet received an ExtendedNetworkControl.Hello.Get() message. */
+ Fsm_SetEvent(&self_->fsm, ND_E_WELCOME_NOSUCCESS);
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_WelcomeResultCb Error (code) 0x%x", 1U, result_ptr_->result.code));
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_WelcomeResultCb Error (info) 0x%x", 1U, result_ptr_->result.info_ptr[i]));
+ }
+ }
+ }
+
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! \brief Callback function for Signature status and error messages
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Pointer to the result of the Signature message
+ */
+static void Nd_SignatureStatusCb(void *self, void *result_ptr)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ self_->signature_status = *(Exc_SignatureStatus_t *)(result_ptr_->data_info);
+ Fsm_SetEvent(&self_->fsm, ND_E_SIGNATURE_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_SignatureStatusCb ND_E_SIGNATURE_SUCCESS", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, ND_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_SignatureStatusCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! \brief Callback function for Init error messages
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Pointer to the result of the Init message
+ */
+static void Nd_InitCb(void *self, void *result_ptr)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ MISC_UNUSED(self_);
+ MISC_UNUSED(result_ptr_);
+
+}
+
+
+/*! \brief Timer callback used for supervising INIC command timeouts.
+ * \param self Reference to Node Discovery object
+ */
+static void Nd_TimerCb(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ Fsm_SetEvent(&self_->fsm, ND_E_TIMEOUT);
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_TimerCb ND_E_TIMEOUT", 0U));
+
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+/*! Function is called on severe internal errors
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Reference to data
+ */
+static void Nd_OnTerminateEventCb(void *self, void *result_ptr)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Ucs_Signature_t *dummy = NULL;
+
+ MISC_UNUSED(result_ptr);
+
+ if (self_->fsm.current_state != ND_S_IDLE)
+ {
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->cb_inst_ptr, UCS_ND_RES_ERROR, dummy);
+ }
+ Nd_Reset_Lists(self_);
+ }
+}
+
+
+/*! \brief Callback function for the INIC.NetworkStatus status and error messages
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Pointer to the result of the INIC.NetworkStatus message
+ */
+static void Nd_NetworkStatusCb(void *self, void *result_ptr)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[ND]", "Nd_NetworkStatusCb 0x%x", 1U, result_ptr_->result.code));
+ /* check for NetOn/NetOff events */
+ if ( (self_->neton == true)
+ && ((((Inic_NetworkStatus_t *)(result_ptr_->data_info))->availability) == UCS_NW_NOT_AVAILABLE) )
+ {
+ self_->neton = false;
+ Fsm_SetEvent(&self_->fsm, ND_E_NET_OFF);
+ }
+ /* check for NetOn/NetOff events */
+ else if ( (self_->neton == false)
+ && ((((Inic_NetworkStatus_t *)(result_ptr_->data_info))->availability) == UCS_NW_AVAILABLE) )
+ {
+ self_->neton = true;
+ self_->hello_neton_request = true;
+ Fsm_SetEvent(&self_->fsm, ND_E_CHECK);
+ }
+ /* check for MPR event */
+ else if ( (((Inic_NetworkStatus_t *)(result_ptr_->data_info))->events & UCS_NETWORK_EVENT_NCE)
+ == UCS_NETWORK_EVENT_NCE)
+ {
+ self_->hello_mpr_request = true;
+ Fsm_SetEvent(&self_->fsm, ND_E_CHECK);
+ }
+ }
+
+ Srv_SetEvent(&self_->service, ND_EVENT_SERVICE);
+}
+
+
+
+/**************************************************************************************************/
+/* Helper functions */
+/**************************************************************************************************/
+/*! \brief Reset the list of new detected nodes
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_Reset_Lists(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ uint16_t i;
+
+ Dl_Ctor(&self_->new_list, self_->base->ucs_user_ptr);
+ Dl_Ctor(&self_->unused_list, self_->base->ucs_user_ptr);
+
+ for(i=0U; i < ND_NUM_NODES; ++i)
+ {
+ Dln_Ctor(&(self_->nodes[i]).node, &(self_->nodes[i]));
+ Dl_InsertTail(&(self_->unused_list), &(self_->nodes[i]).node);
+ }
+}
+
+
+/*! \brief Send the Hello.Get message
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_Send_Hello_Get(void *self)
+{
+ Ucs_Return_t ret_val;
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ ret_val = Exc_Hello_Get(self_->exc, UCS_ADDR_BROADCAST_BLOCKING,
+ ND_SIGNATURE_VERSION, &self_->nd_hello);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[ND]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+
+/*! \brief Send the Welcome.StartResult message
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *signature signature parameter
+ */
+static void Nd_Send_Welcome_SR(void *self, Ucs_Signature_t *signature)
+{
+ Ucs_Return_t ret_val;
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+ uint16_t target_address;
+
+ if (signature->node_pos_addr == 0x0400U)
+ {
+ target_address = 0x0001U;
+ }
+ else
+ {
+ target_address = signature->node_pos_addr;
+ }
+
+ ret_val = Exc_Welcome_Sr(self_->exc,
+ target_address,
+ 0xFFFFU,
+ ND_SIGNATURE_VERSION,
+ *signature,
+ &self_->nd_welcome);
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Nd_TimerCb,
+ self_,
+ ND_TIMEOUT_COMMAND,
+ 0U);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[ND]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+
+/*! \brief Send the Signature.Get message
+ *
+ * \param *self Reference to Node Discovery object
+ * \param target_address target address for the command
+ */
+static void Nd_Send_Signature_Get(void *self, uint16_t target_address)
+{
+ Ucs_Return_t ret_val;
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ ret_val = Exc_Signature_Get(self_->exc, target_address, ND_SIGNATURE_VERSION, &self_->nd_signature);
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Nd_TimerCb,
+ self_,
+ ND_TIMEOUT_COMMAND,
+ 0U);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[ND]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+/*! \brief Starts the periodic timer
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Nd_Start_Periodic_Timer(void *self)
+{
+ CNodeDiscovery *self_ = (CNodeDiscovery *)self;
+
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Nd_TimerCb,
+ self,
+ ND_TIMEOUT_PERIODIC,
+ 0U);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_nodeobserver.c b/ucs2-lib/src/ucs_nodeobserver.c
new file mode 100644
index 0000000..5832e41
--- /dev/null
+++ b/ucs2-lib/src/ucs_nodeobserver.c
@@ -0,0 +1,341 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of CNodeObserver class
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_NODEOBSERVER
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_nodeobserver.h"
+#include "ucs_misc.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+#define NOBS_ADDR_ADMIN_MIN 0xF80U /*!< \brief Start of address range to park unknown devices */
+#define NOBS_ADDR_ADMIN_MAX 0xFDFU /*!< \brief End of address range to park unknown devices */
+
+#define NOBS_ADDR_RANGE1_MIN 0x200U /*!< \brief Start of first static address range */
+#define NOBS_ADDR_RANGE1_MAX 0x2FFU /*!< \brief End of first static address range */
+#define NOBS_ADDR_RANGE2_MIN 0x500U /*!< \brief Start of second static address range */
+#define NOBS_ADDR_RANGE2_MAX 0xEFFU /*!< \brief End of second static address range */
+
+#define NOSB_JOIN_NO 0x00U
+#define NOSB_JOIN_WAIT 0x01U
+#define NOSB_JOIN_YES 0x02U
+
+#define NOBS_WAIT_TIME 200U /*!< \brief Wait time between node not_available -> available */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Nobs_OnInitComplete(void *self, void *error_code_ptr);
+static void Nobs_OnWakeupTimer(void *self);
+static bool Nobs_CheckAddrRange(CNodeObserver *self, Ucs_Signature_t *signature_ptr);
+static void Nobs_InitAllNodes(CNodeObserver *self);
+static void Nobs_InvalidateAllNodes(CNodeObserver *self);
+static void Nobs_InvalidateNode(CNodeObserver *self, Ucs_Rm_Node_t *node_ptr);
+static Ucs_Rm_Node_t* Nobs_GetNodeBySignature(CNodeObserver *self, Ucs_Signature_t *signature_ptr);
+static void Nobs_NotifyApp(CNodeObserver *self, Ucs_MgrReport_t code, uint16_t node_address, Ucs_Rm_Node_t *node_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Class methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of NodeObserver class
+ * \param self The instance
+ * \param base_ptr Reference to base component
+ * \param nd_ptr Reference to NodeDiscovery component
+ * \param rtm_ptr Reference to RoutingManagement component
+ * \param init_ptr Reference to initialization data
+ */
+void Nobs_Ctor(CNodeObserver *self, CBase *base_ptr, CNodeDiscovery *nd_ptr, CRouteManagement *rtm_ptr, Ucs_Mgr_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->base_ptr = base_ptr;
+ self->nd_ptr = nd_ptr;
+ self->rtm_ptr = rtm_ptr;
+ if (init_ptr != NULL)
+ {
+ self->init_data = *init_ptr;
+ }
+ Nobs_InitAllNodes(self);
+ T_Ctor(&self->wakeup_timer);
+
+ Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Nobs_OnInitComplete);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
+}
+
+/*! \brief Callback function which is invoked if the initialization is complete
+ * \param self The instance
+ * \param error_code_ptr Reference to the error code
+ */
+static void Nobs_OnInitComplete(void *self, void *error_code_ptr)
+{
+ CNodeObserver *self_ = (CNodeObserver*)self;
+ MISC_UNUSED(error_code_ptr);
+
+ (void)Nd_Start(self_->nd_ptr);
+ (void)Rtm_StartProcess(self_->rtm_ptr, self_->init_data.routes_list_ptr, self_->init_data.routes_list_size);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Callback Methods */
+/*------------------------------------------------------------------------------------------------*/
+Ucs_Nd_CheckResult_t Nobs_OnNdEvaluate(void *self, Ucs_Signature_t *signature_ptr)
+{
+ CNodeObserver *self_ = (CNodeObserver*)self;
+ Ucs_Rm_Node_t *node_ptr = NULL;
+ Ucs_Nd_CheckResult_t ret = UCS_ND_CHK_UNKNOWN; /* ignore by default */
+
+ if (signature_ptr != NULL)
+ {
+ if (Nobs_CheckAddrRange(self_, signature_ptr) != false)
+ {
+ node_ptr = Nobs_GetNodeBySignature(self_, signature_ptr);
+
+ if (node_ptr != NULL)
+ {
+ if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_NO)
+ {
+ ret = UCS_ND_CHK_WELCOME; /* welcome new node */
+ }
+ else if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_YES)
+ {
+ ret = UCS_ND_CHK_UNIQUE; /* node already available - check for reset */
+ }
+ /* else if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_WAIT) --> ignore waiting nodes */
+ /* future version compare node position to improve handling */
+ }
+ }
+
+ self_->eval_signature = *signature_ptr;
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdEvaluate(): node=0x%03X, node_pos=0x%03X, ret=0x%02X", 2U, signature_ptr->node_address, signature_ptr->node_pos_addr, ret));
+ }
+ else
+ {
+ MISC_MEM_SET(&self_->eval_signature, 0, sizeof(self_->eval_signature)); /* reset signature */
+ TR_FAILED_ASSERT(self_->base_ptr->ucs_user_ptr, "[NOBS]"); /* signature missing - it is evident for evaluation */
+ }
+
+ self_->eval_node_ptr = node_ptr;
+ self_->eval_action = ret;
+
+ if ((ret == UCS_ND_CHK_UNKNOWN) && (signature_ptr != NULL)) /* notify unknown node */
+ {
+ Nobs_NotifyApp(self_, UCS_MGR_REP_IGNORED_UNKNOWN, signature_ptr->node_address, NULL);
+ }
+
+ return ret;
+}
+
+void Nobs_OnNdReport(void *self, Ucs_Nd_ResCode_t code, Ucs_Signature_t *signature_ptr)
+{
+ CNodeObserver *self_ = (CNodeObserver*)self;
+ Ucs_Rm_Node_t *node_ptr = NULL;
+
+ if (signature_ptr != NULL)
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): code=0x%02X, node=0x%03X, node_pos=0x%03X", 3U, code, signature_ptr->node_address, signature_ptr->node_pos_addr));
+ node_ptr = Nobs_GetNodeBySignature(self_, signature_ptr);
+ if (node_ptr != self_->eval_node_ptr) /* if signature available -> expecting the same node_ptr as previously announced in Nobs_OnNdEvaluate */
+ {
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): sanity check failed node_ptr=%p, eval_node_ptr=%p", 2U, node_ptr, self_->eval_node_ptr));
+ node_ptr = NULL; /* do not handle node */
+ }
+ }
+ else
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): code=0x%02X", 1U, code));
+ }
+
+ if (code == UCS_ND_RES_NETOFF)
+ {
+ Nobs_InvalidateAllNodes(self_);
+ }
+ else if (node_ptr == NULL)
+ {
+ /* no not handle events with unspecified node */
+ }
+ else if ((code == UCS_ND_RES_WELCOME_SUCCESS) && (self_->eval_action == UCS_ND_CHK_WELCOME)) /* is new node? */
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): welcome of new node=0x%03X", 1U, signature_ptr->node_address));
+ node_ptr->internal_infos.mgr_joined = NOSB_JOIN_YES;
+ (void)Rtm_SetNodeAvailable(self_->rtm_ptr, node_ptr, true);
+ Nobs_NotifyApp(self_, UCS_MGR_REP_AVAILABLE, signature_ptr->node_address, node_ptr);
+ }
+ else if ((code == UCS_ND_RES_WELCOME_SUCCESS) && (self_->eval_action == UCS_ND_CHK_UNIQUE)) /* is node that previously joined */
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): welcome of existing node=0x%03X (RESET -> not_available)", 1U, signature_ptr->node_address));
+ node_ptr->internal_infos.mgr_joined = NOSB_JOIN_WAIT;
+ (void)Rtm_SetNodeAvailable(self_->rtm_ptr, node_ptr, false);
+ Nobs_NotifyApp(self_, UCS_MGR_REP_NOT_AVAILABLE, signature_ptr->node_address, node_ptr);
+ (void)Nd_Stop(self_->nd_ptr); /* stop node discovery and restart after timeout, */
+ Tm_SetTimer(&self_->base_ptr->tm, &self_->wakeup_timer, &Nobs_OnWakeupTimer, /* transition from node not_available -> available */
+ self, /* needs some time and no callback is provided. */
+ NOBS_WAIT_TIME,
+ 0U
+ );
+ }
+ else if ((code == UCS_ND_RES_MULTI) && (self_->eval_action == UCS_ND_CHK_UNIQUE)) /* is node that causes address conflict */
+ {
+ /* just ignore */
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): ignoring duplicate node=0x%03X", 1U, signature_ptr->node_address));
+ Nobs_NotifyApp(self_, UCS_MGR_REP_IGNORED_DUPLICATE, signature_ptr->node_address, NULL);
+ }
+ else if (code == UCS_ND_RES_UNKNOWN)
+ {
+ /* just ignore */
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): ignoring unknown node=0x%03X", 1U, signature_ptr->node_address));
+ }
+ else
+ {
+ /* just ignore */
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): ignoring node in unexpected, node=0x%03X, code=0x02X ", 2U, signature_ptr->node_address, code));
+ }
+}
+
+static void Nobs_OnWakeupTimer(void *self)
+{
+ CNodeObserver *self_ = (CNodeObserver*)self;
+
+ if (self_->eval_node_ptr != NULL)
+ {
+ if (self_->eval_node_ptr->internal_infos.mgr_joined == NOSB_JOIN_WAIT)
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnWakeupTimer(): welcome of existing node 0x%03X (RESET -> available)", 1U, self_->eval_node_ptr->signature_ptr->node_address));
+ self_->eval_node_ptr->internal_infos.mgr_joined = NOSB_JOIN_YES;
+ (void)Rtm_SetNodeAvailable(self_->rtm_ptr, self_->eval_node_ptr, true);
+ Nobs_NotifyApp(self_, UCS_MGR_REP_AVAILABLE, self_->eval_signature.node_address, self_->eval_node_ptr);
+ }
+ }
+ (void)Nd_Start(self_->nd_ptr);
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Helper Methods */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Checks if the node address in signature is in supported address range
+ * \param self The instance
+ * \param signature_ptr Reference to the nodes signature
+ * \return Returns \c true if the address in signature is supported, otherwise \c false.
+ */
+static bool Nobs_CheckAddrRange(CNodeObserver *self, Ucs_Signature_t *signature_ptr)
+{
+ bool ret = false;
+
+ if (((signature_ptr->node_address >= NOBS_ADDR_RANGE1_MIN) && (signature_ptr->node_address <= NOBS_ADDR_RANGE1_MAX)) ||
+ ((signature_ptr->node_address >= NOBS_ADDR_RANGE2_MIN) && (signature_ptr->node_address <= NOBS_ADDR_RANGE2_MAX)))
+ {
+ ret = true;
+ }
+ MISC_UNUSED(self);
+
+ return ret;
+}
+
+static void Nobs_InitAllNodes(CNodeObserver *self)
+{
+ if (self->init_data.nodes_list_ptr != NULL)
+ {
+ uint32_t cnt = 0U;
+
+ for (cnt = 0U; cnt < self->init_data.nodes_list_size; cnt++)
+ {
+ self->init_data.nodes_list_ptr[cnt].internal_infos.available = 0U;
+ self->init_data.nodes_list_ptr[cnt].internal_infos.mgr_joined = NOSB_JOIN_NO;
+ }
+ }
+}
+
+static void Nobs_InvalidateAllNodes(CNodeObserver *self)
+{
+ if (self->init_data.nodes_list_ptr != NULL)
+ {
+ uint32_t cnt = 0U;
+
+ for (cnt = 0U; cnt < self->init_data.nodes_list_size; cnt++)
+ {
+ Nobs_InvalidateNode(self, &self->init_data.nodes_list_ptr[cnt]);
+ }
+ }
+}
+
+static void Nobs_InvalidateNode(CNodeObserver *self, Ucs_Rm_Node_t *node_ptr)
+{
+ if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_YES) /* notify welcomed nodes as invalid */
+ {
+ Nobs_NotifyApp(self, UCS_MGR_REP_NOT_AVAILABLE, node_ptr->signature_ptr->node_address, node_ptr);
+ }
+
+ node_ptr->internal_infos.mgr_joined = NOSB_JOIN_NO;
+ /* RoutingManagement individually cares for network-not-available event */
+ /* (void)Rtm_SetNodeAvailable(self->rtm_ptr, &self->init_data.nodes_list_ptr[cnt], false); */
+}
+
+static Ucs_Rm_Node_t* Nobs_GetNodeBySignature(CNodeObserver *self, Ucs_Signature_t *signature_ptr)
+{
+ Ucs_Rm_Node_t* ret = NULL;
+
+ if ((signature_ptr != NULL) && (self->init_data.nodes_list_ptr != NULL))
+ {
+ uint32_t cnt = 0U;
+ uint16_t node_addr = signature_ptr->node_address;
+
+ for (cnt = 0U; cnt < self->init_data.nodes_list_size; cnt++)
+ {
+ if (node_addr == self->init_data.nodes_list_ptr[cnt].signature_ptr->node_address)
+ {
+ ret = &self->init_data.nodes_list_ptr[cnt];
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void Nobs_NotifyApp(CNodeObserver *self, Ucs_MgrReport_t code, uint16_t node_address, Ucs_Rm_Node_t *node_ptr)
+{
+ if (self->init_data.report_fptr != NULL)
+ {
+ self->init_data.report_fptr(code, node_address, node_ptr, self->base_ptr->ucs_user_ptr);
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_nsm.c b/ucs2-lib/src/ucs_nsm.c
new file mode 100644
index 0000000..1e3a7e5
--- /dev/null
+++ b/ucs2-lib/src/ucs_nsm.c
@@ -0,0 +1,725 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Node Scripting Management.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_NSM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_nsm.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief API locking Bitmask for Nsm_SendCurrScriptToTrcv() method. */
+#define NSM_RCMTX_API_LOCK 0x0001U
+
+/*! \brief Priority of the RSM service used by scheduler */
+static const uint8_t NSM_SRV_PRIO = 250U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Event for handling the next script */
+static const Srv_Event_t NSM_EVENT_HANDLE_NEXTSCRIPT = 0x01U;
+/*! \brief Event for handling error in scripting */
+static const Srv_Event_t NSM_EVENT_HANDLE_ERROR = 0x02U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Nsm_Service(void *self);
+static Ucs_Return_t Nsm_Start(CNodeScriptManagement * self);
+static void Nsm_HandleApiTimeout(void *self, void *method_mask_ptr);
+static void Nsm_UcsInitSucceededCb(void *self, void *event_ptr);
+static void Nsm_UninitializeService(void *self, void *error_code_ptr);
+static Ucs_Return_t Nsm_HandleNextScript(CNodeScriptManagement * self);
+static Ucs_Return_t Nsm_DeviceSync(CNodeScriptManagement * self);
+static Ucs_Return_t Nsm_SendCurrScriptToTrcv(CNodeScriptManagement * self);
+static void Nsm_HandleError(CNodeScriptManagement * self);
+static void Nsm_Finished(CNodeScriptManagement * self);
+static bool Nsm_IsNextScriptAvailable(CNodeScriptManagement * self);
+static void Nsm_IncrCurrScriptPtr (CNodeScriptManagement * self);
+static void Nsm_MsgTxStatusCb(void *self, Msg_MostTel_t *tel_ptr, Ucs_MsgTxStatus_t status);
+static bool Nsm_IsCurrDeviceSynced(CNodeScriptManagement *self);
+static void Nsm_RmtDevSyncResultCb(void *self, Rsm_Result_t result);
+static Ucs_Return_t Nsm_PauseScript(CNodeScriptManagement * self);
+static void Nsm_ResumeScriptHandling(void* self);
+static void Nsm_ApiLocking(CNodeScriptManagement *self, bool status);
+static bool Nsm_IsApiFree(CNodeScriptManagement *self);
+static void Nsm_SendScriptResult(CNodeScriptManagement *self);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CNodeScriptManagement */
+/*------------------------------------------------------------------------------------------------*/
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Node Script Manager class.
+ * \param self Instance pointer
+ * \param init_ptr init data_ptr
+ */
+void Nsm_Ctor(CNodeScriptManagement *self, Nsm_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CNodeScriptManagement));
+
+ /* Init all reference instances */
+ self->base_ptr = init_ptr->base_ptr;
+ self->rcm_ptr = init_ptr->rcm_ptr;
+ self->rsm_ptr = init_ptr->rsm_ptr;
+ self->tm_ptr = &init_ptr->base_ptr->tm;
+ self->target_address = Inic_GetTargetAddress(self->rsm_ptr->inic_ptr);
+
+ /* Initialize NSM service */
+ Srv_Ctor(&self->nsm_srv, NSM_SRV_PRIO, self, &Nsm_Service);
+
+ /* Initialize API locking mechanism */
+ Sobs_Ctor(&self->lock.observer, self, &Nsm_HandleApiTimeout);
+ Al_Ctor(&self->lock.rcm_api, &self->lock.observer, self->base_ptr->ucs_user_ptr);
+ Alm_RegisterApi(&self->base_ptr->alm, &self->lock.rcm_api);
+
+ /* Add NSM service to scheduler */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->nsm_srv);
+
+ /* Inits observer for UCS termination */
+ Mobs_Ctor(&self->ucstermination_observer, self, EH_M_TERMINATION_EVENTS, &Nsm_UninitializeService);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->ucstermination_observer);
+
+ /* Inits observer for UCS initialization */
+ Mobs_Ctor(&self->ucsinit_observer, self, EH_E_INIT_SUCCEEDED, &Nsm_UcsInitSucceededCb);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->ucsinit_observer);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Executes the script(s) in the given Node.
+ *
+ * \param self Instance pointer of the CNodeScriptManagement
+ * \param node_ptr Reference to the node instance.
+ * \param pb_result_fptr Reference to the result function pointer
+ * \return Possible return values are:
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked.
+ * - \c UCS_RET_SUCCESS if the execution was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_PARAM At least one parameter is NULL
+ * - \c UCS_RET_ERR_NOT_AVAILABLE Timer is not available for pausing the script process
+ */
+Ucs_Return_t Nsm_Run_Pb(CNodeScriptManagement * self, Ucs_Rm_Node_t * node_ptr, Ucs_Ns_ResultCb_t pb_result_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_API_LOCKED;
+
+ if(Nsm_IsApiFree(self) != false)
+ {
+ result = UCS_RET_ERR_PARAM;
+ if ((node_ptr != NULL) && (node_ptr->script_list_ptr != NULL) && (node_ptr->script_list_size > 0U))
+ {
+ /* Locked API */
+ Nsm_ApiLocking(self, true);
+
+ /* Private API is not used */
+ self->is_private_api_used = false;
+
+ /* Sets internal scripts references */
+ self->curr_pb_result_fptr = pb_result_fptr;
+ self->curr_node_ptr = node_ptr;
+ self->curr_sript_ptr = self->curr_node_ptr->script_list_ptr;
+ self->curr_sript_size = self->curr_node_ptr->script_list_size;
+ self->curr_rxfilter_fptr = NULL;
+
+ /* Runs script(s) */
+ result = Nsm_Start(self);
+ }
+
+ /* Release Locking if synchronous result is not successful */
+ if (result != UCS_RET_SUCCESS)
+ {
+ Nsm_ApiLocking(self, false);
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Executes the given script(s).
+ *
+ * \param self Instance pointer of the CNodeScriptManagement
+ * \param script Reference to the scripts table to be executed
+ * \param size Size of the scripts table
+ * \param user_ptr Reference to the caller instance
+ * \param rx_filter_fptr Reference to the optional RX filter callback function pointer
+ * \param result_fptr Reference to the optional result callback function pointer
+ * \return Possible return values are:
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked.
+ * - \c UCS_RET_SUCCESS if the execution was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_PARAM At least one parameter is NULL
+ * - \c UCS_RET_ERR_NOT_AVAILABLE Timer is not available for pausing the script process
+ */
+Ucs_Return_t Nsm_Run_Pv(CNodeScriptManagement * self, Ucs_Ns_Script_t * script, uint8_t size, void * user_ptr, Nsm_RxFilterCb_t rx_filter_fptr, Nsm_ResultCb_t result_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_API_LOCKED;
+
+ if(Nsm_IsApiFree(self) != false)
+ {
+ result = UCS_RET_ERR_PARAM;
+ if ((script != NULL) && (size > 0U))
+ {
+ /* Locked API */
+ Nsm_ApiLocking(self, true);
+
+ /* Private API is not used */
+ self->is_private_api_used = true;
+
+ /* Sets internal scripts references */
+ self->curr_sript_ptr = script;
+ self->curr_sript_size = size;
+ self->curr_user_ptr = user_ptr;
+ self->curr_rxfilter_fptr = rx_filter_fptr;
+ self->curr_pv_result_fptr = result_fptr;
+
+ /* Runs script(s) */
+ result = Nsm_Start(self);
+ }
+
+ /* Release Locking if synchronous result is not successful */
+ if (result != UCS_RET_SUCCESS)
+ {
+ Nsm_ApiLocking(self, false);
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Filters RCM Rx messages allotted to NSM.
+ * \details The filter function shall not release the message object
+ * \param self Instance pointer of the CNodeScriptManagement
+ * \param tel_ptr Reference to the RCM Rx message object
+ * \return \c true if message is allotted to NSM, otherwise \c false.
+ */
+bool Nsm_OnRcmRxFilter(void *self, Msg_MostTel_t *tel_ptr)
+{
+ bool ret_val = false;
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+
+ if (self_ != NULL)
+ {
+ if (self_->curr_rxfilter_fptr != NULL)
+ {
+ ret_val = self_->curr_rxfilter_fptr(tel_ptr, self_->curr_user_ptr);
+ }
+
+ if (!ret_val)
+ {
+ if ((self_->curr_sript_ptr != NULL) && (self_->curr_sript_ptr->exp_result != NULL))
+ {
+ Ucs_Ns_ConfigMsg_t * tmp_exp_res = self_->curr_sript_ptr->exp_result;
+ if ((tmp_exp_res->FBlockId == tel_ptr->id.fblock_id) &&
+ (tmp_exp_res->FunktId == tel_ptr->id.function_id) &&
+ (tmp_exp_res->OpCode == tel_ptr->id.op_type) &&
+ (tmp_exp_res->InstId == tel_ptr->id.instance_id) &&
+ (tmp_exp_res->DataLen == tel_ptr->tel.tel_len))
+ {
+ uint8_t k = 0U;
+ ret_val = true;
+
+ if ((tmp_exp_res->DataPtr == NULL) && (tmp_exp_res->DataLen > 0U))
+ {
+ ret_val = false;
+ }
+
+ for (; ((k < tmp_exp_res->DataLen) && (ret_val == true)); k++)
+ {
+ if (tmp_exp_res->DataPtr[k] != tel_ptr->tel.tel_data_ptr[k])
+ {
+ ret_val = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (ret_val)
+ {
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NSM]", "Transfer of script [0x%X] completed", 1U, self_->curr_sript_ptr));
+ Al_Release(&self_->lock.rcm_api, NSM_RCMTX_API_LOCK);
+ Srv_SetEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_NEXTSCRIPT);
+ Nsm_IncrCurrScriptPtr(self_);
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Checks if the API is locked.
+ * \param self Instance pointer
+ * \return \c true if the API is locked, otherwise \c false.
+ */
+bool Nsm_IsLocked(CNodeScriptManagement * self)
+{
+ bool ret_val = false;
+ if (self != NULL)
+ {
+ ret_val = !Nsm_IsApiFree(self);
+ }
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Service function of the Node Scripting management.
+ * \param self Instance pointer
+ */
+static void Nsm_Service(void *self)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->nsm_srv, &event_mask);
+
+ /* Event to process list of routes */
+ if((event_mask & NSM_EVENT_HANDLE_NEXTSCRIPT) == NSM_EVENT_HANDLE_NEXTSCRIPT)
+ {
+ Srv_ClearEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_NEXTSCRIPT);
+ (void)Nsm_HandleNextScript(self_);
+ }
+
+ /* Event to pause processing of routes list */
+ if ((event_mask & NSM_EVENT_HANDLE_ERROR) == NSM_EVENT_HANDLE_ERROR)
+ {
+ Srv_ClearEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_ERROR);
+ Nsm_HandleError(self_);
+ }
+}
+
+/*! \brief Executes the script(s).
+ *
+ * \param self Instance pointer of the CNodeScriptManagement
+ * \return Possible return values are:
+ * - \c UCS_RET_SUCCESS if the execution was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_NOT_AVAILABLE Timer is not available for pausing the script process
+ */
+static Ucs_Return_t Nsm_Start(CNodeScriptManagement * self)
+{
+ Ucs_Return_t result;
+
+ /* Inits internal result */
+ MISC_MEM_SET(&self->curr_internal_result, 0, sizeof(Nsm_Result_t));
+
+ /* Sets the pause for the current script */
+ self->curr_pause = self->curr_sript_ptr->pause;
+
+ if (Nsm_IsCurrDeviceSynced(self))
+ {
+ result = Nsm_HandleNextScript(self);
+ }
+ else
+ {
+ result = Nsm_DeviceSync(self);
+ }
+
+ return result;
+}
+
+/*! \brief Handles, if available, the next script in the list.
+ * \param self Instance pointer
+ * \return UCS_RET_SUCCESS script was transmitted successfully
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_PARAM Script to be executed is NULL
+ * \return UCS_RET_ERR_NOT_AVAILABLE Timer is not available for pausing the script process
+ */
+static Ucs_Return_t Nsm_HandleNextScript(CNodeScriptManagement * self)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ if (Nsm_IsNextScriptAvailable(self))
+ {
+ if (self->curr_pause > 0U)
+ {
+ result = Nsm_PauseScript(self);
+ }
+ else
+ {
+ result = Nsm_SendCurrScriptToTrcv(self);
+ if (result != UCS_RET_SUCCESS)
+ {
+ Srv_SetEvent(&self->nsm_srv, NSM_EVENT_HANDLE_ERROR);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[NSM]", "Synchronous error occurred while sending script to Transceiver. ErrorCode:0x%02.", 1U, result));
+ }
+ else
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[NSM]", "Start transfer of script [0x%X] to Trcvr", 1U, self->curr_sript_ptr));
+ if ((self->curr_sript_ptr != NULL) && (self->curr_sript_ptr->exp_result == NULL) &&
+ (!self->is_private_api_used))
+ {
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[NSM]", "Expected_Result_Ptr is NULL. No expected result specified in the current script [0x%X].", 1U, self->curr_sript_ptr));
+ }
+ }
+ }
+ }
+ else
+ {
+ Nsm_Finished(self);
+ }
+
+ return result;
+}
+
+/*! \brief Checks whether the next script is available.
+ * \param self Instance pointer
+ * \return \c true if script still available otherwise \c false.
+ */
+static bool Nsm_IsNextScriptAvailable(CNodeScriptManagement * self)
+{
+ return (self->curr_sript_size > 0U);
+}
+
+/*! \brief Sets the current script_ptr to the next script if available and decrements the size of script table.
+ * \param self Instance pointer
+ */
+static void Nsm_IncrCurrScriptPtr(CNodeScriptManagement * self)
+{
+ if (self->curr_sript_size > 0U)
+ {
+ self->curr_sript_size--;
+ self->curr_sript_ptr++;
+ if (self->curr_sript_ptr != NULL)
+ {
+ self->curr_pause = self->curr_sript_ptr->pause;
+ }
+ else
+ {
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[NSM]", "Corrupted data: Next script is NULL although current script size is greater than 0.", 0U));
+ }
+ }
+}
+
+/*! \brief Synchronizes to the remote target device
+ * \param self Instance pointer
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_BUFFER_OVERFLOW | no message buffer available
+ */
+static Ucs_Return_t Nsm_DeviceSync(CNodeScriptManagement * self)
+{
+ Ucs_Return_t result;
+
+ result = Rsm_SyncDev(self->rsm_ptr, self, &Nsm_RmtDevSyncResultCb);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[NSM]", "Start Synchronization of remote device", 0U));
+ }
+
+ return result;
+}
+
+/*! \brief Transmits the current script_ptr to the RCM transceiver
+ * \param self Instance pointer
+ * \return UCS_RET_SUCCESS script was transmitted successfully
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW no message buffer available
+ * \return UCS_RET_ERR_PARAM Script to be executed is NULL
+ * \return UCS_RET_ERR_API_LOCKED RCM Transceiver is currently locked
+ */
+static Ucs_Return_t Nsm_SendCurrScriptToTrcv(CNodeScriptManagement * self)
+{
+ Ucs_Return_t result = UCS_RET_ERR_API_LOCKED;
+
+ if (Al_Lock(&self->lock.rcm_api, NSM_RCMTX_API_LOCK) != false)
+ {
+ result = UCS_RET_ERR_PARAM;
+ if ((self->curr_sript_ptr != NULL) && (self->curr_sript_ptr->send_cmd != NULL))
+ {
+ Ucs_Ns_ConfigMsg_t * tmp_snd_cmd = self->curr_sript_ptr->send_cmd;
+ Msg_MostTel_t *msg_ptr = Trcv_TxAllocateMsg(self->rcm_ptr, tmp_snd_cmd->DataLen);
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+
+ if (msg_ptr != NULL)
+ {
+ uint8_t k = 0U;
+ result = UCS_RET_SUCCESS;
+
+ msg_ptr->destination_addr = self->target_address;
+ msg_ptr->id.fblock_id = tmp_snd_cmd->FBlockId;
+ msg_ptr->id.instance_id = tmp_snd_cmd->InstId;
+ msg_ptr->id.function_id = tmp_snd_cmd->FunktId;
+ msg_ptr->id.op_type = (Ucs_OpType_t)tmp_snd_cmd->OpCode;
+
+ if ((tmp_snd_cmd->DataLen > 0U) && (tmp_snd_cmd->DataPtr == NULL))
+ {
+ result = UCS_RET_ERR_PARAM;
+ }
+
+ for (; ((k < tmp_snd_cmd->DataLen) && (result == UCS_RET_SUCCESS)); k++)
+ {
+ msg_ptr->tel.tel_data_ptr[k] = tmp_snd_cmd->DataPtr[k];
+ }
+ Trcv_TxSendMsgExt(self->rcm_ptr, msg_ptr, &Nsm_MsgTxStatusCb, self);
+ }
+ else
+ {
+ result = UCS_RET_ERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ if (result != UCS_RET_SUCCESS)
+ {
+ Al_Release(&self->lock.rcm_api, NSM_RCMTX_API_LOCK);
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Check if the current device is already attached respectively sync'ed.
+ * \param self Instance pointer
+ * \return \c true if no error occurred, otherwise \c false.
+ */
+static bool Nsm_IsCurrDeviceSynced(CNodeScriptManagement *self)
+{
+ bool ret_val = true;
+
+ if (Rsm_GetDevState(self->rsm_ptr) == RSM_DEV_UNSYNCED)
+ {
+ ret_val = false;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Handles the error event
+ * \param self Instance pointer
+ */
+static void Nsm_HandleError(CNodeScriptManagement * self)
+{
+ self->curr_internal_result.code = UCS_NS_RES_ERROR;
+ Nsm_SendScriptResult(self);
+}
+
+/*! \brief Informs user that the transfer of the current script is completed.
+ * \param self Instance pointer
+ */
+static void Nsm_Finished(CNodeScriptManagement * self)
+{
+ self->curr_internal_result.code = UCS_NS_RES_SUCCESS;
+ Nsm_SendScriptResult(self);
+}
+
+/*! \brief Transmits the script result to the user callback.
+ * \param self Instance pointer
+ */
+static void Nsm_SendScriptResult(CNodeScriptManagement *self)
+{
+ Nsm_ApiLocking(self, false);
+ self->curr_rxfilter_fptr = NULL;
+ self->curr_sript_ptr = NULL;
+ if (self->is_private_api_used)
+ {
+ if (self->curr_pv_result_fptr != NULL)
+ {
+ self->curr_pv_result_fptr(self->curr_user_ptr, self->curr_internal_result);
+ }
+ }
+ else
+ {
+ if (self->curr_pb_result_fptr != NULL)
+ {
+ self->curr_pb_result_fptr(self->curr_node_ptr, self->curr_internal_result.code, self->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Starts the timer for pausing the script.
+ * \param self Instance pointer
+ * \return UCS_RET_SUCCESS Timer was started successfully
+ * \return UCS_RET_ERR_NOT_AVAILABLE Timer is not available for pausing the script process
+ */
+static Ucs_Return_t Nsm_PauseScript(CNodeScriptManagement * self)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+
+ if(T_IsTimerInUse(&self->script_pause) == false)
+ {
+ ret_val = UCS_RET_SUCCESS;
+
+ Tm_SetTimer(self->tm_ptr,
+ &self->script_pause,
+ &Nsm_ResumeScriptHandling,
+ self,
+ self->curr_pause,
+ 0U);
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[NSM]", "Start pause for %d ms", 1U, self->curr_pause));
+ }
+
+ return ret_val;
+}
+
+/*! \brief Locks/Unlocks the RTM API.
+ * \param self Instance pointer
+ * \param status Locking status. \c true = Lock, \c false = Unlock
+ */
+static void Nsm_ApiLocking(CNodeScriptManagement *self, bool status)
+{
+ self->lock.api = status;
+}
+
+/*! \brief Checks if the API is locked.
+ * \param self Instance pointer
+ * \return \c true if the API is not locked, otherwise \c false.
+ */
+static bool Nsm_IsApiFree(CNodeScriptManagement *self)
+{
+ return (self->lock.api == false);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Callback Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Called if UCS initialization has been succeeded.
+ * \param self Instance pointer
+ * \param event_ptr Reference to reported event
+ */
+static void Nsm_UcsInitSucceededCb(void *self, void *event_ptr)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+ MISC_UNUSED(event_ptr);
+
+ /* Remove ucsinit_observer */
+ Eh_DelObsrvInternalEvent(&self_->base_ptr->eh, &self_->ucsinit_observer);
+}
+
+/*! \brief Handles an API timeout
+ * \param self Instance pointer
+ * \param method_mask_ptr Bitmask to signal which API method has caused the timeout
+ */
+static void Nsm_HandleApiTimeout(void *self, void *method_mask_ptr)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+ Alm_ModuleMask_t method_mask = *((Alm_ModuleMask_t *)method_mask_ptr);
+
+ if ((method_mask & NSM_RCMTX_API_LOCK) == NSM_RCMTX_API_LOCK)
+ {
+ Srv_SetEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[NSM]", "API locking timeout occurred for Nsm_Start() method.", 0U));
+ }
+}
+
+/*! \brief Handle internal errors and un-initialize NSM service.
+ * \param self Instance pointer
+ * \param error_code_ptr Reference to internal error code
+ */
+static void Nsm_UninitializeService(void *self, void *error_code_ptr)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+ MISC_UNUSED(error_code_ptr);
+
+ /* Remove NSM service from schedulers list */
+ (void)Scd_RemoveService(&self_->base_ptr->scd, &self_->nsm_srv);
+ /* Remove error/event observers */
+ Eh_DelObsrvInternalEvent(&self_->base_ptr->eh, &self_->ucstermination_observer);
+}
+
+/*! \brief Handle message Tx status, Unlock the API and free the message objects
+ * \param self The instance
+ * \param tel_ptr Reference to transmitted message
+ * \param status Status of the transmitted message
+ */
+static void Nsm_MsgTxStatusCb(void *self, Msg_MostTel_t *tel_ptr, Ucs_MsgTxStatus_t status)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+
+ if (status != UCS_MSG_STAT_OK)
+ {
+ /* Set detailed result */
+ self_->curr_internal_result.details.result_type = NS_RESULT_TYPE_TX;
+ self_->curr_internal_result.details.tx_result = status;
+
+ Al_Release(&self_->lock.rcm_api, NSM_RCMTX_API_LOCK);
+ /* Set Handling error */
+ Srv_SetEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[NSM]", "Transmission error occurred. ErrorCode:0x%02.", 1U, status));
+ }
+ Trcv_TxReleaseMsg(tel_ptr);
+}
+
+/*! \brief Handles the result of "device.sync" operations.
+ * \param self Instance pointer
+ * \param result RSM result
+ */
+static void Nsm_RmtDevSyncResultCb(void *self, Rsm_Result_t result)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+ if (result.code == RSM_RES_SUCCESS)
+ {
+ Srv_SetEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_NEXTSCRIPT);
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NSM]", "Remote device has been successfully synchronized.", 0U));
+ }
+ else
+ {
+ /* Set internal result for private use */
+ self_->curr_internal_result.details.inic_result = result.details.inic_result;
+ self_->curr_internal_result.details.tx_result = result.details.tx_result;
+ if (result.details.tx_result != UCS_MSG_STAT_OK)
+ {
+ self_->curr_internal_result.details.result_type = NS_RESULT_TYPE_TX;
+ }
+ else
+ {
+ self_->curr_internal_result.details.result_type = NS_RESULT_TYPE_TGT_SYNC;
+ }
+
+ Srv_SetEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_ERROR);
+ if (result.details.inic_result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[NSM]", "Synchronization to the remote device failed due to transmission error. ErrorCode: 0x%02X", 1U, result.details.inic_result.code));
+ }
+ else
+ {
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[NSM]", "Synchronization to the remote device failed due to error on target device. ErrorCode: 0x%02X", 1U, result.details.inic_result.code));
+ }
+ }
+}
+
+/*! \brief Resumes the handling of script. This method is the callback function of the NSM timer.
+ * \param self Instance pointer
+ */
+static void Nsm_ResumeScriptHandling(void* self)
+{
+ CNodeScriptManagement *self_ = (CNodeScriptManagement *)self;
+ self_->curr_pause = 0U;
+ Srv_SetEvent(&self_->nsm_srv, NSM_EVENT_HANDLE_NEXTSCRIPT);
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[NSM]", "Pause completed. Resume handling of scripts", 0U));
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_obs.c b/ucs2-lib/src/ucs_obs.c
new file mode 100644
index 0000000..d67cd0c
--- /dev/null
+++ b/ucs2-lib/src/ucs_obs.c
@@ -0,0 +1,449 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the observer library module. The module consists of the two classes
+ * CSubject and CObserver.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_OBS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_obs.h"
+#include "ucs_misc.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* 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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Sub_Ctor(CSubject *self, void *ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->ucs_user_ptr = ucs_user_ptr;
+ Dl_Ctor(&self->list, self->ucs_user_ptr);
+ Dl_Ctor(&self->add_list, self->ucs_user_ptr);
+}
+
+/*! \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) &&
+ (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false) &&
+ (Dl_IsNodeInList(&self->add_list, &obs_ptr->node) == false))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[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) && (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[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->ucs_user_ptr, "[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_Remove(&self->list, &obs_ptr->node) == DL_OK))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[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(self != NULL)
+ {
+ CDlNode *n_tmp = self->list.head;
+ self->notify = true;
+ self->changed = false;
+ while(n_tmp != NULL)
+ {
+ CObserver *o_tmp = (CObserver *)n_tmp->data_ptr;
+ if((o_tmp->update_fptr != NULL) && (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(current_obs_ptr_->valid == false)
+ {
+ (void)Dl_Remove(&subject_ptr_->list, &current_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)
+{
+ 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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Ssub_Ctor(CSingleSubject *self, void *ucs_user_ptr)
+{
+ self->observer_ptr = NULL;
+ self->ucs_user_ptr = ucs_user_ptr;
+ self->user_mask = 0U;
+}
+
+/*! \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 UCS_TR_INFO
+ if(self->observer_ptr != NULL)
+ {
+ TR_INFO((self->ucs_user_ptr, "[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(self != NULL)
+ {
+ CDlNode *n_tmp = self->list.head;
+ self->notify = true;
+ self->changed = false;
+ while(n_tmp != NULL)
+ {
+ CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr;
+ if( (o_tmp->parent.update_fptr != NULL) &&
+ (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/ucs2-lib/src/ucs_pmchannel.c b/ucs2-lib/src/ucs_pmchannel.c
new file mode 100644
index 0000000..2c6bbba
--- /dev/null
+++ b/ucs2-lib/src/ucs_pmchannel.c
@@ -0,0 +1,307 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Port Message Channel
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PMC
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pmchannel.h"
+#include "ucs_pmp.h"
+#include "ucs_pmcmd.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Constants */
+/*------------------------------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal typedefs */
+/*------------------------------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+/* LLD related interface functions */
+static Ucs_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size);
+static void Pmch_RxUnused(void *self, Ucs_Lld_RxMsg_t *msg_ptr);
+static void Pmch_RxReceive(void *self, Ucs_Lld_RxMsg_t *msg_ptr);
+static void Pmch_TxRelease(void *self, Ucs_Lld_TxMsg_t *msg_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of class CPmChannel
+ * \param self The instance
+ * \param init_ptr Reference to initialization data structure
+ */
+void Pmch_Ctor(CPmChannel *self, const Pmch_InitData_t *init_ptr)
+{
+ uint16_t cnt;
+ MISC_MEM_SET(self, 0, sizeof(*self));
+
+ self->init_data = *init_ptr;
+ self->lld_active = false;
+
+ self->ucs_iface.rx_allocate_fptr = &Pmch_RxAllocate;
+ self->ucs_iface.rx_receive_fptr = &Pmch_RxReceive;
+ self->ucs_iface.rx_free_unused_fptr = &Pmch_RxUnused;
+ self->ucs_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.ucs_user_ptr);
+ 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.ucs_user_ptr, "[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.ucs_user_ptr, "[PMCH]", "Pmch_Initialize(): LLD_START()", 0U));
+ self->init_data.lld_iface.start_fptr(&self->ucs_iface, self, self->init_data.lld_iface.lld_user_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.ucs_user_ptr, "[PMCH]", "Pmch_Uninitialize(): Channel un-synchronization started", 0U));
+
+ if (self->lld_active != false)
+ {
+ self->lld_active = false;
+ TR_INFO((self->init_data.ucs_user_ptr, "[PMCH]", "Pmch_Uninitialize(): LLD_STOP()", 0U));
+ self->init_data.lld_iface.stop_fptr(self->init_data.lld_iface.lld_user_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, Ucs_Lld_TxMsg_t *msg_ptr)
+{
+ if (self->lld_active != false)
+ {
+ self->init_data.lld_iface.tx_transmit_fptr(msg_ptr, self->init_data.lld_iface.lld_user_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 Ucs_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size)
+{
+ CMessage *msg_ptr = NULL;
+ Ucs_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.ucs_user_ptr, "[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.ucs_user_ptr, "[PMCH]", "Pmch_RxAllocate(): Allocation failed, size=%u", 1U, buffer_size));
+ }
+ }
+ else
+ {
+ self_->rx_trigger_available = true;
+ TR_FAILED_ASSERT(self_->init_data.ucs_user_ptr, "[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, Ucs_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.ucs_user_ptr, "[PMCH]", (pb_handle != NULL));
+ Pmch_ReturnRxToPool(self_, pb_handle);
+}
+
+/*! \brief Pass an Rx message to UNICENS
+ * \param self The instance
+ * \param msg_ptr Reference to the Rx message object containing the received
+ * message.
+ */
+static void Pmch_RxReceive(void *self, Ucs_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.ucs_user_ptr, "[PMCH]", "Pmch_RxReceive(): received message for unregistered FIFO no=%u", 1U, fifo_no));
+ }
+ }
+ else
+ {
+ TR_ERROR((self_->init_data.ucs_user_ptr, "[PMCH]", "Pmch_RxReceive(): received incomplete message of size=%u", 1U, msg_ptr->data_size));
+ }
+ }
+ else
+ {
+ TR_ERROR((self_->init_data.ucs_user_ptr, "[PMCH]", "Pmch_RxReceive(): message data is not valid", 0U));
+ }
+
+ if (found == false)
+ {
+ 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, Ucs_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.ucs_user_ptr, "[PMCH]"); /* unknown FIFO - invalid message object */
+ }
+
+ TR_ASSERT(self_->init_data.ucs_user_ptr, "[PMCH]", (msg_ptr->custom_next_msg_ptr == NULL) ); /* 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_->init_data.lld_iface.lld_user_ptr);
+ }
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_pmcmd.c b/ucs2-lib/src/ucs_pmcmd.c
new file mode 100644
index 0000000..d4055b0
--- /dev/null
+++ b/ucs2-lib/src/ucs_pmcmd.c
@@ -0,0 +1,155 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of class CPmCommand
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PM_CMD
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pmcmd.h"
+#include "ucs_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
+ */
+Ucs_Lld_TxMsg_t* Pmcmd_GetLldTxObject(CPmCommand *self)
+{
+ return (Ucs_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/ucs2-lib/src/ucs_pmevent.c b/ucs2-lib/src/ucs_pmevent.c
new file mode 100644
index 0000000..cc3d1d1
--- /dev/null
+++ b/ucs2-lib/src/ucs_pmevent.c
@@ -0,0 +1,130 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Port Message Event Handler
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PMEH
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pmevent.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Pmev_OnFifosEvent(void *self, void *data_ptr);
+static void Pmev_OnSystemEvent(void *self, void *data_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of class CPmEventHandler
+ * \param self The instance
+ * \param base_ptr Reference to base object
+ * \param fifos_ptr Reference to CPmFifos object
+ */
+void Pmev_Ctor(CPmEventHandler *self, CBase *base_ptr, CPmFifos *fifos_ptr)
+{
+ self->base_ptr = base_ptr;
+ self->fifos_ptr = fifos_ptr;
+
+ Obs_Ctor(&self->observer, self, &Pmev_OnFifosEvent);
+
+ Mobs_Ctor(&self->sys_observer, self, (EH_E_BIST_FAILED | EH_E_INIT_FAILED), &Pmev_OnSystemEvent);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->sys_observer);
+}
+
+/*! \brief Start reporting events to EH
+ * \param self The instance
+ */
+void Pmev_Start(CPmEventHandler *self)
+{
+ Fifos_AddEventObserver(self->fifos_ptr, &self->observer);
+}
+
+/*! \brief Stops reporting events to EH
+ * \param self The instance
+ */
+void Pmev_Stop(CPmEventHandler *self)
+{
+ Fifos_RemoveEventObserver(self->fifos_ptr, &self->observer);
+}
+
+/*! \brief Callback function to handle a PMS event
+ * \param self The instance
+* \param data_ptr Reference to the PMS event
+ */
+static void Pmev_OnFifosEvent(void *self, void *data_ptr)
+{
+ CPmEventHandler *self_ = (CPmEventHandler*)self;
+ Fifos_Event_t *event_ptr = (Fifos_Event_t*)data_ptr;
+
+ switch (*event_ptr)
+ {
+ case FIFOS_EV_SYNC_LOST:
+ Eh_ReportEvent(&self_->base_ptr->eh, EH_E_SYNC_LOST);
+ break;
+ case FIFOS_EV_SYNC_ESTABLISHED:
+ /* not relevant */
+ break;
+ case FIFOS_EV_SYNC_FAILED:
+ /* not relevant */
+ break;
+ case FIFOS_EV_UNSYNC_COMPLETE:
+ Eh_ReportEvent(&self_->base_ptr->eh, EH_E_UNSYNC_COMPLETE);
+ break;
+ case FIFOS_EV_UNSYNC_FAILED:
+ Eh_ReportEvent(&self_->base_ptr->eh, EH_E_UNSYNC_FAILED);
+ break;
+ default:
+ /* not relevant */
+ break;
+ }
+}
+
+/*! \brief Callback function to handle an UCS system events
+ * \param self The instance
+* \param data_ptr Reference to the system event event
+ */
+static void Pmev_OnSystemEvent(void *self, void *data_ptr)
+{
+ CPmEventHandler *self_ = (CPmEventHandler*)self;
+ Fifos_ForceTermination(self_->fifos_ptr);
+ MISC_UNUSED(data_ptr);
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_pmfifo.c b/ucs2-lib/src/ucs_pmfifo.c
new file mode 100644
index 0000000..90f8db7
--- /dev/null
+++ b/ucs2-lib/src/ucs_pmfifo.c
@@ -0,0 +1,1366 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Port Message FIFO
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PMF
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pmfifo.h"
+#include "ucs_pmp.h"
+#include "ucs_pmcmd.h"
+#include "ucs_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, Ucs_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, Ucs_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->ucs_user_ptr);
+
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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->ucs_user_ptr);
+ 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->ucs_user_ptr, "[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->ucs_user_ptr);
+ Dl_Ctor(&self->tx.pending_q, self->init.base_ptr->ucs_user_ptr);
+
+ 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->ucs_user_ptr);
+
+ 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->ucs_user_ptr, "[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->ucs_user_ptr, "[FIFO]", (self->sync_state == FIFO_S_UNSYNCED_INIT));
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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, UCS_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, UCS_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((event_mask & FIFO_SE_TX_APPLY_STATUS) == 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->ucs_user_ptr, "[FIFO]", (msg_ptr != NULL));
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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 (node_ptr == NULL)
+ {
+ 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, UCS_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->ucs_user_ptr, "[FIFO]", (msg_ptr != NULL));
+ TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (lld_tx_ptr != NULL));
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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, (Ucs_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->ucs_user_ptr, "[FIFO]");
+ }
+ }
+ else
+ {
+ TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[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, Ucs_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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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, (Ucs_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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[FIFO]", "Fifo_TxRestorePending(): FIFO: %u, msg_ptr: 0x%p", 2U, self->config.fifo_id, msg_ptr));
+ TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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, Ucs_MsgTxStatus_t status)
+{
+ CDlNode *node_ptr;
+ CDlList temp_queue;
+
+ Dl_Ctor(&temp_queue, self->init.base_ptr->ucs_user_ptr);
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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, Ucs_MsgTxStatus_t status)
+{
+ bool ret = true;
+ uint8_t acks = Fifo_TxGetValidAcknowledges(self, sid);
+
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[FIFO]", (tx_ptr != NULL));
+ TR_INFO((self->init.base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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, (Ucs_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, UCS_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, UCS_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, (Ucs_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, UCS_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->ucs_user_ptr, "[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 (self->rx.wait_processing == false) /* 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 (node_ptr == NULL)
+ {
+ 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->ucs_user_ptr, "[FIFO]"); /* unknown FIFO message type */
+ break;
+ }
+ }
+ else
+ {
+ TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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 UCS_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)UCS_MSG_STAT_ERROR_CFG_NO_RCVR:
+ case (uint8_t)UCS_MSG_STAT_ERROR_BF:
+ case (uint8_t)UCS_MSG_STAT_ERROR_CRC:
+ case (uint8_t)UCS_MSG_STAT_ERROR_ID:
+ case (uint8_t)UCS_MSG_STAT_ERROR_ACK:
+ case (uint8_t)UCS_MSG_STAT_ERROR_TIMEOUT:
+ case (uint8_t)UCS_MSG_STAT_ERROR_FATAL_WT:
+ case (uint8_t)UCS_MSG_STAT_ERROR_FATAL_OA:
+ case (uint8_t)UCS_MSG_STAT_ERROR_NA_TRANS:
+ case (uint8_t)UCS_MSG_STAT_ERROR_NA_OFF:
+ ret = failure_code;
+ break;
+ default:
+ ret = (uint8_t)UCS_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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[FIFO]", "Fifo_Unsynchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state));
+ if (self->sync_state != FIFO_S_UNSYNCED_READY)
+ {
+ 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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Missing response on status request", 0U));
+ Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true);
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_pmfifos.c b/ucs2-lib/src/ucs_pmfifos.c
new file mode 100644
index 0000000..347c1b9
--- /dev/null
+++ b/ucs2-lib/src/ucs_pmfifos.c
@@ -0,0 +1,448 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of class CPmFifos
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PMFIFOS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pmfifos.h"
+#include "ucs_misc.h"
+#include "ucs_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->ucs_user_ptr);
+ Obs_Ctor(&self->obs_icm, self, &Fifos_OnFifoEvent);
+ Obs_Ctor(&self->obs_rcm, self, &Fifos_OnFifoEvent);
+ Obs_Ctor(&self->obs_mcm, self, &Fifos_OnFifoEvent);
+
+ TR_ASSERT(self->base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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 UCS 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->ucs_user_ptr, "[FIFOS]", "Fifos_ForceTermination(): Termination started, state: %d", 1U, self->state));
+ Fifos_Cleanup(self);
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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->ucs_user_ptr, "[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)
+{
+ uint8_t cnt;
+ self->state = FIFOS_S_UNSYNCING;
+ self->unsync_initial = initial;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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 */
+
+ for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++)
+ {
+ if (self->fifos[cnt] != NULL)
+ {
+ if (initial || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_UNSYNCED_READY))
+ {
+ Fifo_Unsynchronize(self->fifos[cnt]);
+ }
+ }
+ }
+
+ 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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Sudden un-synchronization of Port Message channel completed", 0U));
+ }
+ break;
+
+ case FIFOS_S_UNSYNCED:
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFO event in state unsynced", 0U));
+ break;
+
+ default:
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[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/ucs2-lib/src/ucs_pmp.c b/ucs2-lib/src/ucs_pmp.c
new file mode 100644
index 0000000..ae33306
--- /dev/null
+++ b/ucs2-lib/src/ucs_pmp.c
@@ -0,0 +1,350 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of Port Message Protocol
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PMH
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pmp.h"
+#include "ucs_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/ucs2-lib/src/ucs_pool.c b/ucs2-lib/src/ucs_pool.c
new file mode 100644
index 0000000..1824b27
--- /dev/null
+++ b/ucs2-lib/src/ucs_pool.c
@@ -0,0 +1,126 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of message pool class
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_POOL
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_pool.h"
+#include "ucs_misc.h"
+#include "ucs_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 ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Pool_Ctor(CPool *self, CMessage messages[], uint16_t size, void *ucs_user_ptr)
+{
+ uint16_t index;
+
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->ucs_user_ptr = ucs_user_ptr;
+ self->initial_size = size;
+ self->messages = messages;
+
+ Dl_Ctor(&self->message_list, self->ucs_user_ptr);
+
+ 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->ucs_user_ptr, "[POOL]", (Pool_GetCurrentSize(pool_ptr) < pool_ptr->initial_size));
+ Dl_InsertTail(&pool_ptr->message_list, Msg_GetNode(msg_ptr));
+ }
+ else
+ {
+ TR_ERROR((0U, "[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/ucs2-lib/src/ucs_prog.c b/ucs2-lib/src/ucs_prog.c
new file mode 100644
index 0000000..e64d519
--- /dev/null
+++ b/ucs2-lib/src/ucs_prog.c
@@ -0,0 +1,957 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Programming Service.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_PROG_MODE
+ * @{
+
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_inic_pb.h"
+#include "ucs_prog.h"
+#include "ucs_misc.h"
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+#define PRG_NUM_STATES 6U /*!< \brief Number of state machine states */
+#define PRG_NUM_EVENTS 13U /*!< \brief Number of state machine events */
+
+#define PRG_TIMEOUT_COMMAND 100U /*!< \brief supervise EXC commands */
+
+#define PRG_SIGNATURE_VERSION 1U /*!< \brief signature version used for Node Discovery */
+
+#define PRG_ADMIN_BASE_ADDR 0x0F00U /*!< \brief bas admin address */
+
+
+/* Error values */
+#define PRG_HW_RESET_REQ 0x200110U /* HW reset required */
+#define PRG_SESSION_ACTIVE 0x200111U /* Session already active */
+#define PRG_CFG_STRING_ERROR 0x200220U /* A configuration string erase error has occurred. */
+#define PRG_MEM_ERASE_ERROR 0x200221U /* An error memory erase error has occurred.*/
+#define PRG_CFG_WRITE_ERROR 0x200225U /* Configuration memory write error. */
+#define PRG_CFG_FULL_ERROR 0x200226U /* Configuration memory is full. */
+#define PRG_HDL_MATCH_ERROR 0x200330U /* The SessionHandle does not match the current memory session. */
+#define PRG_MEMID_ERROR 0x200331U /* The memory session does not support the requested MemID. */
+#define PRG_ADDR_EVEN_ERROR 0x200332U /* The Address is not even when writing the configuration memory. */
+#define PRG_LEN_EVEN_ERROR 0x200333U /* The UnitLen is not even when writing the configuration memory. */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the Programming service used by scheduler */
+static const uint8_t PRG_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! Main event for the Programming service */
+static const Srv_Event_t PRG_EVENT_SERVICE = 1U;
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal enumerators */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Possible events of the system diagnosis state machine */
+typedef enum Prg_Events_
+{
+ PRG_E_NIL = 0U, /*!< \brief NIL Event */
+ PRG_E_START = 1U, /*!< \brief API start command was called. */
+ PRG_E_STOP = 2U, /*!< \brief Stop request occurred. */
+ PRG_E_WELCOME_SUCCESS = 3U, /*!< \brief Welcome command was successful. */
+ PRG_E_WELCOME_NOSUCCESS = 4U, /*!< \brief Welcome command was not successful. */
+ PRG_E_MEM_WRITE_CMD = 5U, /*!< \brief MemorySessionOpen command was succcessful */
+ PRG_E_MEM_WRITE_FINISH = 6U, /*!< \brief MemoryWrite command was succcessful */
+ PRG_E_MEM_CLOSE_SUCCESS = 7U, /*!< \brief MemorySessionClose command was succcessful */
+ PRG_E_NET_OFF = 8U, /*!< \brief NetOff occurred. */
+ PRG_E_TIMEOUT = 9U, /*!< \brief Timeout occurred. */
+ PRG_E_ERROR = 10U, /*!< \brief An error occurred which requires no command to be sent to the INIC. */
+ PRG_E_ERROR_INIT = 11U, /*!< \brief Error requires Init.Start to be sent. */
+ PRG_E_ERROR_CLOSE_INIT = 12U /*!< \brief Error requires MemorySessionClose.SR and Init.Start to be sent. */
+} Prg_Events_t;
+
+
+/*! \brief States of the node discovery state machine */
+typedef enum Prg_State_
+{
+ PRG_S_IDLE = 0U, /*!< \brief Idle state. */
+ PRG_S_WAIT_WELCOME = 1U, /*!< \brief Programming started. */
+ PRG_S_WAIT_MEM_OPEN = 2U, /*!< \brief Wait for MemorySessionOpen result. */
+ PRG_S_WAIT_MEM_WRITE = 3U, /*!< \brief Wait for MemoryWrite result. */
+ PRG_S_WAIT_MEM_CLOSE = 4U, /*!< \brief Wait for MemorySessionClose result. */
+ PRG_S_WAIT_MEM_ERR_CLOSE = 5U /*!< \brief Wait for MemorySessionClose result in error case. */
+} Prg_State_t;
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Prg_Service(void *self);
+
+static void Prg_WelcomeResultCb(void *self, void *result_ptr);
+static void Prg_MemOpenResultCb(void *self, void *result_ptr);
+static void Prg_MemWriteResultCb(void *self, void *result_ptr);
+static void Prg_MemCloseResultCb(void *self, void *result_ptr);
+
+static void Prg_OnTerminateEventCb(void *self, void *result_ptr);
+static void Prg_NetworkStatusCb(void *self, void *result_ptr);
+
+static void Prg_A_Start(void *self);
+static void Prg_A_MemOpen(void *self);
+static void Prg_A_MemWrite(void *self);
+static void Prg_A_MemClose(void *self);
+static void Prg_A_InitDevice(void *self);
+static void Prg_A_NetOff(void *self);
+static void Prg_A_Timeout(void *self);
+static void Prg_A_Error(void *self);
+static void Prg_A_Error_Init(void *self);
+static void Prg_A_Error_Close_Init(void *self);
+
+
+static void Prg_Check_RetVal(CProgramming *self, Ucs_Return_t ret_val);
+static uint32_t Prg_CalcError(uint8_t val[]);
+
+static void Prg_TimerCb(void *self);
+
+/*------------------------------------------------------------------------------------------------*/
+/* State transition table (used by finite state machine) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief State transition table */
+static const Fsm_StateElem_t prg_trans_tab[PRG_NUM_STATES][PRG_NUM_EVENTS] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+ { /* State PRG_S_IDLE */
+ /* PRG_E_NIL */ {NULL, PRG_S_IDLE },
+ /* PRG_E_START */ {Prg_A_Start, PRG_S_WAIT_WELCOME },
+ /* PRG_E_STOP */ {NULL, PRG_S_IDLE },
+ /* PRG_E_WELCOME_SUCCESS */ {NULL, PRG_S_IDLE },
+ /* PRG_E_WELCOME_NOSUCCESS */ {NULL, PRG_S_IDLE },
+ /* PRG_E_MEM_WRITE_CMD */ {NULL, PRG_S_IDLE },
+ /* PRG_E_MEM_WRITE_FINISH */ {NULL, PRG_S_IDLE },
+ /* PRG_E_MEM_CLOSE_SUCCESS */ {NULL, PRG_S_IDLE },
+ /* PRG_E_NET_OFF */ {Prg_A_NetOff, PRG_S_IDLE },
+ /* PRG_E_TIMEOUT */ {Prg_A_Timeout, PRG_S_IDLE },
+ /* PRG_E_ERROR */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_ERROR_INIT */ {NULL, PRG_S_IDLE },
+ /* PRG_E_ERROR_CLOSE_INIT */ {NULL, PRG_S_IDLE },
+ },
+ { /* State PRG_S_WAIT_WELCOME */
+ /* PRG_E_NIL */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_START */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_STOP */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_WELCOME_SUCCESS */ {Prg_A_MemOpen, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_WELCOME_NOSUCCESS */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_MEM_WRITE_CMD */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_MEM_WRITE_FINISH */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_MEM_CLOSE_SUCCESS */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_NET_OFF */ {Prg_A_NetOff, PRG_S_IDLE },
+ /* PRG_E_TIMEOUT */ {Prg_A_Timeout, PRG_S_IDLE },
+ /* PRG_E_ERROR */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_ERROR_INIT */ {NULL, PRG_S_WAIT_WELCOME },
+ /* PRG_E_ERROR_CLOSE_INIT */ {NULL, PRG_S_WAIT_WELCOME }
+ },
+ { /* State PRG_S_WAIT_MEM_OPEN */
+ /* PRG_E_NIL */ {NULL, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_START */ {NULL, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_STOP */ {NULL, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_WELCOME_SUCCESS */ {NULL, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_WELCOME_NOSUCCESS */ {NULL, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_MEM_WRITE_CMD */ {Prg_A_MemWrite, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_MEM_WRITE_FINISH */ {Prg_A_MemClose, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_MEM_CLOSE_SUCCESS */ {NULL, PRG_S_WAIT_MEM_OPEN },
+ /* PRG_E_NET_OFF */ {Prg_A_NetOff, PRG_S_IDLE },
+ /* PRG_E_TIMEOUT */ {Prg_A_Timeout, PRG_S_IDLE },
+ /* PRG_E_ERROR */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_ERROR_INIT */ {Prg_A_Error_Init, PRG_S_IDLE },
+ /* PRG_E_ERROR_CLOSE_INIT */ {Prg_A_Error_Close_Init, PRG_S_WAIT_MEM_ERR_CLOSE }
+ },
+ { /* State PRG_S_WAIT_MEM_WRITE */
+ /* PRG_E_NIL */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_START */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_STOP */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_WELCOME_SUCCESS */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_WELCOME_NOSUCCESS */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_MEM_WRITE_CMD */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_MEM_WRITE_FINISH */ {Prg_A_MemClose, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_MEM_CLOSE_SUCCESS */ {NULL, PRG_S_WAIT_MEM_WRITE },
+ /* PRG_E_NET_OFF */ {Prg_A_NetOff, PRG_S_IDLE },
+ /* PRG_E_TIMEOUT */ {Prg_A_Timeout, PRG_S_IDLE },
+ /* PRG_E_ERROR */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_ERROR_INIT */ {Prg_A_Error_Init, PRG_S_IDLE },
+ /* PRG_E_ERROR_CLOSE_INIT */ {Prg_A_Error_Close_Init, PRG_S_WAIT_MEM_ERR_CLOSE }
+ },
+ { /* State PRG_S_WAIT_MEM_CLOSE */
+ /* PRG_E_NIL */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_START */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_STOP */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_WELCOME_SUCCESS */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_WELCOME_NOSUCCESS */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_MEM_WRITE_CMD */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_MEM_WRITE_FINISH */ {NULL, PRG_S_WAIT_MEM_CLOSE },
+ /* PRG_E_MEM_CLOSE_SUCCESS */ {Prg_A_InitDevice, PRG_S_IDLE },
+ /* PRG_E_NET_OFF */ {Prg_A_NetOff, PRG_S_IDLE },
+ /* PRG_E_TIMEOUT */ {Prg_A_Timeout, PRG_S_IDLE },
+ /* PRG_E_ERROR */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_ERROR_INIT */ {Prg_A_Error_Init, PRG_S_IDLE },
+ /* PRG_E_ERROR_CLOSE_INIT */ {Prg_A_Error, PRG_S_IDLE },
+ },
+ { /* State PRG_S_WAIT_MEM_ERR_CLOSE */
+ /* PRG_E_NIL */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_START */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_STOP */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_WELCOME_SUCCESS */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_WELCOME_NOSUCCESS */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_MEM_WRITE_CMD */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_MEM_WRITE_FINISH */ {NULL, PRG_S_WAIT_MEM_ERR_CLOSE },
+ /* PRG_E_MEM_CLOSE_SUCCESS */ {Prg_A_Error_Init, PRG_S_IDLE },
+ /* PRG_E_NET_OFF */ {Prg_A_NetOff, PRG_S_IDLE },
+ /* PRG_E_TIMEOUT */ {Prg_A_Timeout, PRG_S_IDLE },
+ /* PRG_E_ERROR */ {Prg_A_Error, PRG_S_IDLE },
+ /* PRG_E_ERROR_INIT */ {Prg_A_Error_Init, PRG_S_IDLE },
+ /* PRG_E_ERROR_CLOSE_INIT */ {Prg_A_Error, PRG_S_IDLE },
+ }
+
+};
+
+
+/*! \brief Constructor of class CProgramming.
+ * \param self Reference to CProgramming instance
+ * \param inic Reference to CInic instance
+ * \param base Reference to CBase instance
+ * \param exc Reference to CExc instance
+ */
+ /* \param init_ptr Report callback function*/
+void Prg_Ctor(CProgramming *self, CInic *inic, CBase *base, CExc *exc)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self));
+
+ self->inic = inic;
+ self->exc = exc;
+ self->base = base;
+
+ Fsm_Ctor(&self->fsm, self, &(prg_trans_tab[0][0]), PRG_NUM_EVENTS, PRG_E_NIL);
+
+ Sobs_Ctor(&self->prg_welcome, self, &Prg_WelcomeResultCb);
+ Sobs_Ctor(&self->prg_memopen, self, &Prg_MemOpenResultCb);
+ Sobs_Ctor(&self->prg_memwrite, self, &Prg_MemWriteResultCb);
+ Sobs_Ctor(&self->prg_memclose, self, &Prg_MemCloseResultCb);
+
+ /* register termination events */
+ Mobs_Ctor(&self->prg_terminate, self, EH_M_TERMINATION_EVENTS, &Prg_OnTerminateEventCb);
+ Eh_AddObsrvInternalEvent(&self->base->eh, &self->prg_terminate);
+
+ /* Register NetOn and MPR events */
+ Obs_Ctor(&self->prg_nwstatus, self, &Prg_NetworkStatusCb);
+ Inic_AddObsrvNwStatus(self->inic, &self->prg_nwstatus);
+ self->neton = false;
+
+ /* Initialize Programming service */
+ Srv_Ctor(&self->service, PRG_SRV_PRIO, self, &Prg_Service);
+ /* Add Programming service to scheduler */
+ (void)Scd_AddService(&self->base->scd, &self->service);
+
+}
+
+
+/*! \brief Service function of the Node Discovery service.
+ * \param self Reference to Programming service object
+ */
+static void Prg_Service(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->service, &event_mask);
+ if(PRG_EVENT_SERVICE == (event_mask & PRG_EVENT_SERVICE)) /* Is event pending? */
+ {
+ Fsm_State_t result;
+ Srv_ClearEvent(&self_->service, PRG_EVENT_SERVICE);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "FSM __ %d %d", 2U, self_->fsm.current_state, self_->fsm.event_occured));
+ result = Fsm_Service(&self_->fsm);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[PRG]", (result != FSM_STATE_ERROR));
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "FSM -> %d", 1U, self_->fsm.current_state));
+ MISC_UNUSED(result);
+ }
+}
+
+
+
+/**************************************************************************************************/
+/* API functions */
+/**************************************************************************************************/
+/*!
+ *
+ * \param *self Reference to Programming service object
+ */
+/*! \brief Program a node
+ *
+ * \param *self Reference to Programming service object
+ * \param node_id Node position address of the node to be programmed
+ * \param *signature Signature of the node to be programmed
+ * \param session_type Defines the memory access type.
+ * \param command_list Refers to array of programming tasks.
+ * \param report_fptr Report callback function
+ */
+void Prg_Start(CProgramming *self,
+ uint16_t node_id,
+ Ucs_Signature_t *signature,
+ Ucs_Prg_SessionType_t session_type,
+ Ucs_Prg_Command_t* command_list,
+ Ucs_Prg_ReportCb_t report_fptr)
+{
+
+
+ self->node_id = node_id;
+ self->signature = *signature;
+ self->session_type = session_type;
+ self->command_list = command_list;
+ self->report_fptr = report_fptr;
+ self->current_function = UCS_PRG_FKT_DUMMY;
+
+ if (self->neton == true)
+ {
+ Fsm_SetEvent(&self->fsm, PRG_E_START);
+ Srv_SetEvent(&self->service, PRG_EVENT_SERVICE);
+
+ TR_INFO((self->base->ucs_user_ptr, "[PRG]", "Prg_Start", 0U));
+ }
+ else
+ {
+ if (self->report_fptr != NULL)
+ {
+ self->report_fptr(UCS_PRG_RES_NET_OFF,
+ self->current_function,
+ 0U,
+ NULL,
+ self->base->ucs_user_ptr);
+ }
+ TR_INFO((self->base->ucs_user_ptr, "[PRG]", "Prg_Start failed: NET_OFF", 0U));
+ }
+}
+
+
+
+/**************************************************************************************************/
+/* FSM Actions */
+/**************************************************************************************************/
+/*! Action on Start command
+ *
+ * \param *self Reference to Node Discovery object
+ */
+static void Prg_A_Start(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Ucs_Return_t ret_val;
+
+ if (self_->node_id == 0x0400U)
+ {
+ self_->target_address = UCS_ADDR_LOCAL_INIC;
+ }
+ else
+ {
+ self_->target_address = self_->node_id;
+ }
+
+ self_->admin_node_address = PRG_ADMIN_BASE_ADDR + ((self_->node_id) & 0x00FFU);
+ self_->current_function = UCS_PRG_FKT_WELCOME;
+
+ ret_val = Exc_Welcome_Sr(self_->exc,
+ self_->target_address,
+ self_->admin_node_address,
+ PRG_SIGNATURE_VERSION,
+ self_->signature,
+ &self_->prg_welcome);
+ Prg_Check_RetVal(self_, ret_val);
+}
+
+static void Prg_A_MemOpen(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Ucs_Return_t ret_val;
+
+ self_->current_function = UCS_PRG_FKT_MEM_OPEN;
+
+ ret_val = Exc_MemSessionOpen_Sr(self_->exc,
+ self_->admin_node_address,
+ self_->session_type,
+ &self_->prg_memopen);
+ Prg_Check_RetVal(self_, ret_val);
+}
+
+static void Prg_A_MemWrite(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Ucs_Return_t ret_val;
+
+ self_->current_function = UCS_PRG_FKT_MEM_WRITE;
+
+ ret_val = Exc_MemoryWrite_Sr(self_->exc,
+ self_->admin_node_address,
+ self_->session_handle,
+ self_->command_list[self_->command_index].mem_id,
+ self_->command_list[self_->command_index].address,
+ self_->command_list[self_->command_index].unit_length,
+ self_->command_list[self_->command_index].data,
+ &self_->prg_memwrite);
+ Prg_Check_RetVal(self_, ret_val);
+}
+
+static void Prg_A_MemClose(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Ucs_Return_t ret_val;
+
+ self_->current_function = UCS_PRG_FKT_MEM_CLOSE;
+ ret_val = Exc_MemSessionClose_Sr(self_->exc,
+ self_->admin_node_address,
+ self_->session_handle,
+ &self_->prg_memclose);
+ Prg_Check_RetVal(self_, ret_val);
+}
+
+static void Prg_A_InitDevice(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Ucs_Return_t ret_val;
+
+ self_->current_function = UCS_PRG_FKT_INIT;
+ ret_val = Exc_DeviceInit_Start(self_->exc,
+ self_->admin_node_address,
+ NULL);
+ Prg_Check_RetVal(self_, ret_val);
+
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_PRG_RES_SUCCESS,
+ UCS_PRG_FKT_DUMMY,
+ 0U,
+ NULL,
+ self_->base->ucs_user_ptr);
+ }
+ }
+}
+
+static void Prg_A_NetOff(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_PRG_RES_NET_OFF,
+ self_->current_function,
+ 0U,
+ NULL,
+ self_->base->ucs_user_ptr);
+ }
+}
+
+static void Prg_A_Timeout(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_PRG_RES_TIMEOUT,
+ self_->current_function,
+ 0U,
+ NULL,
+ self_->base->ucs_user_ptr);
+ }
+}
+
+static void Prg_A_Error(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ uint8_t *data_ptr = NULL;
+
+ if ( (self_->error.code == UCS_PRG_RES_FKT_ASYNCH)
+ && (self_->error.ret_len != 0U))
+ {
+ data_ptr = &(self_->error.parm[0]);
+ }
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->error.code,
+ self_->error.function,
+ self_->error.ret_len,
+ data_ptr,
+ self_->base->ucs_user_ptr);
+ }
+}
+
+
+static void Prg_A_Error_Init(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ uint8_t *data_ptr = NULL;
+ Ucs_Return_t ret_val;
+
+ ret_val = Exc_DeviceInit_Start(self_->exc,
+ self_->admin_node_address,
+ NULL);
+ Prg_Check_RetVal(self_, ret_val);
+
+ if ( (self_->error.code == UCS_PRG_RES_FKT_ASYNCH)
+ && (self_->error.ret_len != 0U))
+ {
+ data_ptr = &(self_->error.parm[0]);
+ }
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->error.code,
+ self_->error.function,
+ self_->error.ret_len,
+ data_ptr,
+ self_->base->ucs_user_ptr);
+ }
+}
+
+static void Prg_A_Error_Close_Init(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ uint8_t *data_ptr = NULL;
+ Ucs_Return_t ret_val;
+
+ ret_val = Exc_DeviceInit_Start(self_->exc,
+ self_->admin_node_address,
+ NULL);
+ Prg_Check_RetVal(self_, ret_val);
+
+
+ if ( (self_->error.code == UCS_PRG_RES_FKT_ASYNCH)
+ && (self_->error.ret_len != 0U))
+ {
+ data_ptr = &(self_->error.parm[0]);
+ }
+
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(self_->error.code,
+ self_->error.function,
+ self_->error.ret_len,
+ data_ptr,
+ self_->base->ucs_user_ptr);
+ }
+}
+
+
+/**************************************************************************************************/
+/* Callback functions */
+/**************************************************************************************************/
+
+/*! \brief Function is called on reception of the Welcome.Result messsage
+ * \param self Reference to Programming service object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Prg_WelcomeResultCb(void *self, void *result_ptr)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Exc_WelcomeResult_t welcome_result;
+ /* read signature and store it */
+ welcome_result = *(Exc_WelcomeResult_t *)(result_ptr_->data_info);
+ if (welcome_result.res == EXC_WELCOME_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_WELCOME_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_WelcomeResultCb PRG_E_WELCOME_SUCCESS", 0U));
+ }
+ else
+ {
+ /* store error paramters */
+ self_->error.code = UCS_PRG_RES_FKT_ASYNCH;
+ self_->error.function = UCS_PRG_FKT_WELCOME_NOSUCCESS;
+ self_->error.ret_len = 0U;
+
+ Fsm_SetEvent(&self_->fsm, PRG_E_WELCOME_NOSUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_WelcomeResultCb PRG_E_WELCOME_NOSUCCESS", 0U));
+ }
+ }
+ else
+ {
+ uint8_t i;
+ /* store error paramters */
+ self_->error.code = UCS_PRG_RES_FKT_ASYNCH;
+ self_->error.function = UCS_PRG_FKT_WELCOME;
+ self_->error.ret_len = result_ptr_->result.info_size;
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ self_->error.parm[i] = result_ptr_->result.info_ptr[i];
+ }
+
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR);
+
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_WelcomeResultCb Error (code) 0x%x", 1U, result_ptr_->result.code));
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_WelcomeResultCb Error (info) 0x%x", 1U, result_ptr_->result.info_ptr[i]));
+ }
+ }
+
+ Srv_SetEvent(&self_->service, PRG_EVENT_SERVICE);
+}
+
+
+
+/*! \brief Function is called on reception of the MemorySessionOpen.Result messsage
+ * \param self Reference to Programming service object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Prg_MemOpenResultCb(void *self, void *result_ptr)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ self_->session_handle = *(uint16_t *)(result_ptr_->data_info);
+ self_->command_index = 0U;
+
+ if ( (self_->command_list[self_->command_index].data_length == 0U)
+ || (self_->command_list[self_->command_index].data == NULL))
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_MEM_WRITE_FINISH);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemOpenResultCb No Tasks", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_MEM_WRITE_CMD);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemOpenResultCb successful", 0U));
+ }
+ }
+ else
+ {
+ uint8_t i;
+ uint32_t fs_error;
+
+ /* store error paramters */
+ self_->error.code = UCS_PRG_RES_FKT_ASYNCH;
+ self_->error.function = UCS_PRG_FKT_MEM_OPEN;
+ self_->error.ret_len = result_ptr_->result.info_size;
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ self_->error.parm[i] = result_ptr_->result.info_ptr[i];
+ }
+
+ fs_error = Prg_CalcError(&(self_->error.parm[0]));
+
+ switch (fs_error)
+ {
+ case PRG_HW_RESET_REQ:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_INIT);
+ break;
+
+ case PRG_SESSION_ACTIVE:
+ self_->session_handle = (uint16_t)(((uint16_t)(self_->error.parm[3])) << 8U) + self_->error.parm[4]; /* get correct session handle */
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_CLOSE_INIT);
+ break;
+
+ default:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR);
+ break;
+ }
+
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemOpenResultCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, PRG_EVENT_SERVICE);
+}
+
+
+/*! \brief Function is called on reception of the MemoryWrite.Result messsage
+ * \param self Reference to Programming service object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Prg_MemWriteResultCb(void *self, void *result_ptr)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ self_->command_index++;
+ if ( (self_->command_list[self_->command_index].data_length == 0U)
+ || (self_->command_list[self_->command_index].data == NULL))
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_MEM_WRITE_FINISH);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemWriteResultCb PRG_E_MEM_WRITE_FINISH", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_MEM_WRITE_CMD);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemWriteResultCb successful", 0U));
+ }
+ }
+ else
+ {
+ uint8_t i;
+ uint32_t fs_error;
+
+ /* store error paramters */
+ self_->error.code = UCS_PRG_RES_FKT_ASYNCH;
+ self_->error.function = UCS_PRG_FKT_MEM_WRITE;
+ self_->error.ret_len = result_ptr_->result.info_size;
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ self_->error.parm[i] = result_ptr_->result.info_ptr[i];
+ }
+
+ fs_error = Prg_CalcError(&(self_->error.parm[0]));
+
+ switch (fs_error)
+ {
+ case PRG_CFG_WRITE_ERROR:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_CLOSE_INIT);
+ break;
+
+ case PRG_CFG_FULL_ERROR:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_CLOSE_INIT);
+ break;
+
+ case PRG_HDL_MATCH_ERROR:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_INIT);
+ break;
+
+ case PRG_MEMID_ERROR:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_CLOSE_INIT);
+ break;
+
+ case PRG_ADDR_EVEN_ERROR:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_CLOSE_INIT);
+ break;
+
+ case PRG_LEN_EVEN_ERROR:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_CLOSE_INIT);
+ break;
+
+ default:
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR);
+ break;
+ }
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemWriteResultCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, PRG_EVENT_SERVICE);
+}
+
+
+/*! \brief Function is called on reception of the MemorySessionClose.Result messsage
+ * \param self Reference to Programming service object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Prg_MemCloseResultCb(void *self, void *result_ptr)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ uint8_t session_result = *(uint8_t *)(result_ptr_->data_info);
+
+ if (session_result == 0U)
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_MEM_CLOSE_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemCloseResultCb PRG_E_MEM_CLOSE_SUCCESS", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_INIT);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemCloseResultCb ErrResult PRG_E_ERROR_INIT", 0U));
+ }
+ }
+ else
+ {
+ uint8_t i;
+ uint32_t fs_error;
+
+ /* store error paramters */
+ self_->error.code = UCS_PRG_RES_FKT_ASYNCH;
+ self_->error.function = UCS_PRG_FKT_MEM_CLOSE;
+ self_->error.ret_len = result_ptr_->result.info_size;
+ for (i=0U; i< result_ptr_->result.info_size; ++i)
+ {
+ self_->error.parm[i] = result_ptr_->result.info_ptr[i];
+ }
+
+ fs_error = Prg_CalcError(&(self_->error.parm[0]));
+
+ if (fs_error == PRG_HDL_MATCH_ERROR)
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR_INIT);
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, PRG_E_ERROR);
+ }
+
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_MemCloseResultCb Error 0x%x", 1U, result_ptr_->result.code));
+ }
+
+ Srv_SetEvent(&self_->service, PRG_EVENT_SERVICE);
+}
+
+
+
+
+/*! Function is called on severe internal errors
+ *
+ * \param *self Reference to Programming object
+ * \param *result_ptr Reference to data
+ */
+static void Prg_OnTerminateEventCb(void *self, void *result_ptr)
+{
+ CProgramming *self_ = (CProgramming *)self;
+
+ MISC_UNUSED(result_ptr);
+
+ if (self_->fsm.current_state != PRG_S_IDLE)
+ {
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+ if (self_->report_fptr != NULL)
+ {
+ self_->report_fptr(UCS_PRG_RES_ERROR,
+ self_->current_function,
+ 0U,
+ NULL,
+ self_->base->ucs_user_ptr);
+ }
+
+ /* reset FSM */
+ self_->fsm.current_state = PRG_S_IDLE;
+ }
+}
+
+
+/*! \brief Callback function for the INIC.NetworkStatus status and error messages
+ *
+ * \param *self Reference to Node Discovery object
+ * \param *result_ptr Pointer to the result of the INIC.NetworkStatus message
+ */
+static void Prg_NetworkStatusCb(void *self, void *result_ptr)
+{
+ CProgramming *self_ = (CProgramming *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_NetworkStatusCb 0x%x", 1U, result_ptr_->result.code));
+ /* check for NetOn/NetOff events */
+ if ( (self_->neton == true)
+ && ((((Inic_NetworkStatus_t *)(result_ptr_->data_info))->availability) == UCS_NW_NOT_AVAILABLE) )
+ {
+ self_->neton = false;
+ Fsm_SetEvent(&self_->fsm, PRG_E_NET_OFF);
+ }
+ else if ( (self_->neton == false)
+ && ((((Inic_NetworkStatus_t *)(result_ptr_->data_info))->availability) == UCS_NW_AVAILABLE) )
+ {
+ self_->neton = true;
+ }
+ }
+
+ Srv_SetEvent(&self_->service, PRG_EVENT_SERVICE);
+}
+
+
+
+/*! \brief Timer callback used for supervising INIC command timeouts.
+ * \param self Reference to System Diagnosis object
+ */
+static void Prg_TimerCb(void *self)
+{
+ CProgramming *self_ = (CProgramming *)self;
+
+ Fsm_SetEvent(&self_->fsm, PRG_E_TIMEOUT);
+ TR_INFO((self_->base->ucs_user_ptr, "[PRG]", "Prg_TimerCb PRG_E_TIMEOUT", 0U));
+
+ Srv_SetEvent(&self_->service, PRG_EVENT_SERVICE);
+}
+
+
+
+/**************************************************************************************************/
+/* Helper functions */
+/**************************************************************************************************/
+
+static void Prg_Check_RetVal(CProgramming *self, Ucs_Return_t ret_val)
+{
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ Tm_SetTimer(&self->base->tm,
+ &self->timer,
+ &Prg_TimerCb,
+ self,
+ PRG_TIMEOUT_COMMAND,
+ 0U);
+ }
+ else
+ {
+ TR_ASSERT(self->base->ucs_user_ptr, "[PRG]", ret_val == UCS_RET_SUCCESS);
+
+ /* store error paramter */
+ self->error.code = UCS_PRG_RES_FKT_SYNCH;
+ self->error.function = self->current_function;
+ self->error.ret_len = (uint8_t)ret_val;
+
+ Fsm_SetEvent(&self->fsm, PRG_E_ERROR);
+ Srv_SetEvent(&self->service, PRG_EVENT_SERVICE);
+ }
+}
+
+
+static uint32_t Prg_CalcError(uint8_t val[])
+{
+ uint32_t temp;
+
+ temp = val[0] + (((uint32_t)val[1]) << 8U) + (((uint32_t)val[2]) << 16U);
+
+ return temp;
+}
+
+
+
+
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_rsm.c b/ucs2-lib/src/ucs_rsm.c
new file mode 100644
index 0000000..bb40743
--- /dev/null
+++ b/ucs2-lib/src/ucs_rsm.c
@@ -0,0 +1,640 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Sync Management.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_RSM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_rsm.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the RSM service used by scheduler */
+static const uint8_t RSM_SRV_PRIO = 250U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Event for processing one of the below tasks */
+static const Srv_Event_t RSM_EVENT_PROCESS_DEV = 0x01U;
+/*! \brief Event for signaling the SyncLost event */
+static const Srv_Event_t RSM_EVENT_SIG_SYNCLOST = 0x02U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Mask for the Network Availability Info */
+static const uint32_t RSM_MASK_NETWORK_AVAILABILITY = 0x0002U;
+/*! \brief Mask for the Network NodeAddress Info */
+static const uint32_t RSM_MASK_NETWORK_NODE_ADDRESS = 0x0010U;
+/*! \brief Mask for the Network MaxPosition Info */
+static const uint32_t RSM_MASK_NETWORK_MAX_POSITION = 0x0040U;
+
+/*! \brief Invalid device node address */
+static const uint16_t RSM_INVALID_NODE_ADDRESS = 0x0000U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Rsm_Service (void *self);
+static void Rsm_ProcessJob (CRemoteSyncManagement *self);
+static Ucs_Return_t Rsm_SendSync (CRemoteSyncManagement *self);
+static Ucs_Return_t Rsm_RequestNtfDevId (CRemoteSyncManagement *self);
+static Ucs_Return_t Rsm_ClearLastNtfDevId (CRemoteSyncManagement *self);
+static Ucs_Return_t Rsm_SetNotificationAll (CRemoteSyncManagement * self);
+static Ucs_Return_t Rsm_SetNotificationGpio (CRemoteSyncManagement * self);
+static Ucs_Return_t Rsm_ProcessDeviceJob (CRemoteSyncManagement *self);
+static bool Rsm_IsLocal (CRemoteSyncManagement * self);
+static void Rsm_MnsInitSucceededCb(void *self, void *event_ptr);
+static void Rsm_MnsNwStatusInfosCb(void *self, void *event_ptr);
+static void Rsm_SyncResultCb(void *self, void *result_ptr);
+static void Rsm_MsgObjAvailCb(void *self, void *result_ptr);
+static void Rsm_SignalSyncCompleted (CRemoteSyncManagement * self);
+static void Rsm_SignalSyncError (CRemoteSyncManagement * self);
+static void Rsm_SignalSyncLost (CRemoteSyncManagement * self);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CRemoteSyncManager */
+/*------------------------------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Remote Sync Manager class.
+ * \param self Instance pointer
+ * \param init_ptr init data_ptr
+ */
+void Rsm_Ctor(CRemoteSyncManagement *self, Rsm_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CRemoteSyncManagement));
+
+ /* Init all instances */
+ self->base_ptr = init_ptr->base_ptr;
+ self->inic_ptr = init_ptr->inic_ptr;
+ self->net_ptr = init_ptr->net_ptr;
+
+ /* Init observer */
+ Mobs_Ctor(&self->event_param.ucsinit_observer, self, EH_E_INIT_SUCCEEDED, &Rsm_MnsInitSucceededCb);
+ Sobs_Ctor(&self->event_param.stdresult_observer, self, &Rsm_SyncResultCb);
+ Obs_Ctor(&self->event_param.txavailability_observer, self, &Rsm_MsgObjAvailCb);
+
+ /* Init event_param variables */
+ self->event_param.own_device_address = RSM_INVALID_NODE_ADDRESS;
+
+ /* Initialize Sync Management service */
+ Srv_Ctor(&self->rsm_srv, RSM_SRV_PRIO, self, &Rsm_Service);
+ /* Add RSM service to scheduler */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->rsm_srv);
+ /* Add Observer for MNS initialization Result */
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_param.ucsinit_observer);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Add an observer to the SyncLost Event subject
+ * \param self Instance pointer
+ * \param obs init data_ptr
+ */
+void Rsm_AddObserver(CRemoteSyncManagement * self, CObserver * obs)
+{
+ (void)Sub_AddObserver(&self->event_param.subject, obs);
+}
+
+/*! \brief Removes an observer registered by Rsm_AddObserver
+ * \param self Instance pointer
+ * \param obs_ptr observer to be removed
+ */
+void Rsm_DelObserver(CRemoteSyncManagement * self, CObserver * obs_ptr)
+{
+ (void)Sub_RemoveObserver(&self->event_param.subject, obs_ptr);
+}
+
+/*! \brief Synchronizes to the given device
+ * \param self Instance pointer
+ * \param user_data reference to the user data that'll be attached in the sync_complete_fptr callback function
+ * \param sync_complete_fptr result callback function to the device to be synchronized
+ * \return Possible return values are
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked.
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_PARAM parameter exceeds its admissible range
+ */
+Ucs_Return_t Rsm_SyncDev(CRemoteSyncManagement * self, void* user_data, Rsm_ResultCb_t sync_complete_fptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_API_LOCKED;
+
+ if (self->dev_infos.sync_state != RSM_DEV_SYNCING)
+ {
+ self->dev_infos.curr_user_data = user_data;
+ self->dev_infos.curr_res_cb_fptr = sync_complete_fptr;
+ if (self->dev_infos.sync_state == RSM_DEV_SYNCED)
+ {
+ self->dev_infos.next_st = RSM_ST_SYNC_SUCC;
+ Srv_SetEvent(&self->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ result = UCS_RET_SUCCESS;
+ }
+ else
+ {
+ if (Rsm_IsLocal(self))
+ {
+ self->dev_infos.next_st = RSM_ST_NTF_GPIO;
+ }
+ else
+ {
+ self->dev_infos.next_st = RSM_ST_SYNC_REQ;
+ }
+ result = Rsm_ProcessDeviceJob (self);
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Returns the state (ready or busy) of the given device.
+ * \param self Instance pointer.
+ * \return state of the given device.
+ */
+Rsm_DevSyncState_t Rsm_GetDevState(CRemoteSyncManagement * self)
+{
+ return self->dev_infos.sync_state;
+}
+
+/*! \brief Reports SyncLost for the given RSM instance.
+ * \param self Reference to the instance ptr
+ */
+void Rsm_ReportSyncLost (CRemoteSyncManagement * self)
+{
+ self->last_synclost_cause = RSM_SLC_CFGNOTOK;
+ Srv_SetEvent(&self->rsm_srv, RSM_EVENT_SIG_SYNCLOST);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Service function of the Sync management.
+ * \param self Instance pointer
+ */
+static void Rsm_Service (void *self)
+{
+ CRemoteSyncManagement *self_ = (CRemoteSyncManagement *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->rsm_srv, &event_mask);
+
+ /* Handle event to process jobs within devices */
+ if((event_mask & RSM_EVENT_PROCESS_DEV) == RSM_EVENT_PROCESS_DEV)
+ {
+ Srv_ClearEvent(&self_->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ Rsm_ProcessJob(self_);
+ }
+
+ /* Handle event to signal "SyncLost" */
+ if((event_mask & RSM_EVENT_SIG_SYNCLOST) == RSM_EVENT_SIG_SYNCLOST)
+ {
+ Srv_ClearEvent(&self_->rsm_srv, RSM_EVENT_SIG_SYNCLOST);
+ Rsm_SignalSyncLost(self_);
+ }
+}
+
+/*! \brief Processes the next job, if available, in a device.
+ * \param self Instance pointer
+ */
+static void Rsm_ProcessJob (CRemoteSyncManagement *self)
+{
+ if (self->dev_infos.next_st != RSM_ST_IDLE)
+ {
+ (void)Rsm_ProcessDeviceJob(self);
+ }
+}
+
+/*! \brief Processes the next job for the given device.
+ * \param self Instance pointer
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_API_LOCKED The INIC API is locked
+ * - \c UCS_RET_ERR_PARAM parameter exceeds its admissible range
+ */
+static Ucs_Return_t Rsm_ProcessDeviceJob (CRemoteSyncManagement *self)
+{
+ Ucs_Return_t result = UCS_RET_SUCCESS;
+
+ switch (self->dev_infos.next_st)
+ {
+ case RSM_ST_SYNC_REQ:
+ result = Rsm_SendSync (self);
+ break;
+
+ case RSM_ST_NTF_REQ:
+ result = Rsm_RequestNtfDevId (self);
+ break;
+
+ case RSM_ST_NTF_CLEAR:
+ result = Rsm_ClearLastNtfDevId (self);
+ break;
+
+ case RSM_ST_NTF_ALL:
+ result = Rsm_SetNotificationAll (self);
+ break;
+
+ case RSM_ST_NTF_GPIO:
+ result = Rsm_SetNotificationGpio (self);
+ break;
+
+ case RSM_ST_SYNC_SUCC:
+ Rsm_SignalSyncCompleted (self);
+ break;
+
+ case RSM_ST_SYNC_ERR:
+ Rsm_SignalSyncError (self);
+ break;
+
+ default:
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[RSM]", "Unexpected State Transition: 0x%02X", 1U, (Rsm_StateTransition_t)(self->dev_infos.next_st)));
+ break;
+ }
+
+ return result;
+}
+
+/*! \brief Sends a Sync command to the given device.
+ * \param self Instance pointer
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ */
+static Ucs_Return_t Rsm_SendSync (CRemoteSyncManagement *self)
+{
+ Ucs_Return_t result;
+
+ result = Inic_DeviceSync (self->inic_ptr,
+ &self->event_param.stdresult_observer);
+
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->dev_infos.sync_state = RSM_DEV_SYNCING;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RSM]", "Start synchronization to remote device", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Inic_AddObsrvOnTxMsgObjAvail(self->inic_ptr, &self->event_param.txavailability_observer);
+ }
+
+ return result;
+}
+
+/*! \brief Retrieves the ID of the device that has notified to the INIC.DeviceStatus() FktIDs on the given remote device.
+ * \param self Instance pointer
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_API_LOCKED The INIC API is locked
+ */
+static Ucs_Return_t Rsm_RequestNtfDevId (CRemoteSyncManagement *self)
+{
+ Ucs_Return_t result;
+ uint16_t funcid_devstatus = (uint16_t)0x0220;
+
+ result = Inic_Notification_Get(self->inic_ptr, funcid_devstatus, &self->event_param.stdresult_observer);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RSM]", "DeviceId Request for INIC.DeviceStatus() succeeded", 0U));
+ }
+ else if (result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Inic_AddObsrvOnTxMsgObjAvail(self->inic_ptr, &self->event_param.txavailability_observer);
+ }
+ else
+ {
+ Srv_SetEvent(&self->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ }
+
+ return result;
+}
+
+/*! \brief Clears the current DevId of all remote functions on the given remote device.
+ * \param self Instance pointer
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_PARAM parameter exceeds its admissible range
+ */
+static Ucs_Return_t Rsm_ClearLastNtfDevId (CRemoteSyncManagement *self)
+{
+ Ucs_Return_t result;
+ Ucs_Inic_NotificationCtrl_t control = UCS_INIC_NTF_CLEAR_ALL;
+ Inic_FktIdList_t rm_fktid_list;
+ rm_fktid_list.fktids_ptr = NULL;
+ rm_fktid_list.num_fktids = 0U;
+
+ result = Inic_Notification_Set(self->inic_ptr, control, self->event_param.own_device_address, rm_fktid_list);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->dev_infos.next_st = RSM_ST_NTF_ALL;
+ Srv_SetEvent(&self->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RSM]", "Clear DevId for all Remote functions succeeded", 0U));
+ }
+ else if (result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Inic_AddObsrvOnTxMsgObjAvail(self->inic_ptr, &self->event_param.txavailability_observer);
+ }
+
+ return result;
+}
+
+/*! \brief Sets all notification for the given remote device.
+ * \param self Instance pointer
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_PARAM parameter exceeds its admissible range
+ */
+static Ucs_Return_t Rsm_SetNotificationAll (CRemoteSyncManagement * self)
+{
+ Ucs_Return_t result;
+ Ucs_Inic_NotificationCtrl_t control = UCS_INIC_NTF_SET_FUNC;
+
+ uint16_t funcid_list[2] = {0x0705U, 0x0802U};
+ Inic_FktIdList_t rm_fktid_list;
+ rm_fktid_list.fktids_ptr = &funcid_list[0];
+ rm_fktid_list.num_fktids = 2U;
+
+ result = Inic_Notification_Set(self->inic_ptr, control, self->event_param.own_device_address, rm_fktid_list);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->dev_infos.next_st = RSM_ST_SYNC_SUCC;
+ Srv_SetEvent(&self->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RSM]", "Set Notification for All Remote functions succeeded", 0U));
+ }
+ else if (result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Inic_AddObsrvOnTxMsgObjAvail(self->inic_ptr, &self->event_param.txavailability_observer);
+ }
+
+ return result;
+}
+
+/*! \brief Sets Gpio notification for the local device.
+ * \param self Instance pointer
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ */
+static Ucs_Return_t Rsm_SetNotificationGpio (CRemoteSyncManagement * self)
+{
+ Ucs_Return_t result;
+ /*! \brief GPIO TriggerEvent function id */
+ uint16_t RSM_GPIOTREVENT_FUNCID = 0x0705U;
+ /*! \brief Local EHC address */
+ uint16_t RSM_EHC_ADDRESS = 0x0002U;
+
+ Ucs_Inic_NotificationCtrl_t control = UCS_INIC_NTF_SET_FUNC;
+ uint16_t funcid_resmonitor = RSM_GPIOTREVENT_FUNCID;
+ Inic_FktIdList_t rm_fktid_list;
+ rm_fktid_list.fktids_ptr = &funcid_resmonitor;
+ rm_fktid_list.num_fktids = 1U;
+
+ result = Inic_Notification_Set(self->inic_ptr, control, RSM_EHC_ADDRESS, rm_fktid_list);
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->dev_infos.next_st = RSM_ST_SYNC_SUCC;
+ Srv_SetEvent(&self->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ }
+ else if (result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Inic_AddObsrvOnTxMsgObjAvail(self->inic_ptr, &self->event_param.txavailability_observer);
+ }
+
+ return result;
+}
+
+/*! \brief Check whether the given device is local or remote.
+ * \param self Instance pointer
+ * \return Returns \c true if the device is local, otherwise \c false.
+ */
+static bool Rsm_IsLocal (CRemoteSyncManagement *self)
+{
+ return (Inic_GetTargetAddress(self->inic_ptr) == UCS_ADDR_LOCAL_DEV);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Callback Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Called if MNS initialization has been succeeded.
+ * \param self Instance pointer
+ * \param event_ptr Reference to reported event
+ */
+static void Rsm_MnsInitSucceededCb(void *self, void *event_ptr)
+{
+ CRemoteSyncManagement *self_ = (CRemoteSyncManagement *)self;
+ MISC_UNUSED(event_ptr);
+
+ /* Remove ucsinit_observer */
+ Eh_DelObsrvInternalEvent(&self_->base_ptr->eh, &self_->event_param.ucsinit_observer);
+
+ /* Add network status observer */
+ Mobs_Ctor(&self_->event_param.nwstatus_observer, self, RSM_MASK_NETWORK_NODE_ADDRESS, &Rsm_MnsNwStatusInfosCb);
+ Net_AddObserverNetworkStatus(self_->net_ptr, &self_->event_param.nwstatus_observer);
+}
+
+/*! \brief Result callback for the "Sync Request".
+ * \param self Instance pointer
+ * \param result_ptr Reference to the result.
+ */
+static void Rsm_SyncResultCb(void *self, void *result_ptr)
+{
+ CRemoteSyncManagement *self_ = (CRemoteSyncManagement *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ Inic_NotificationResult_t * res_inf = NULL;
+
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ if (self_->dev_infos.sync_state == RSM_DEV_SYNCING)
+ {
+ switch (self_->dev_infos.next_st)
+ {
+ case RSM_ST_SYNC_REQ:
+ self_->dev_infos.next_st = RSM_ST_NTF_REQ;
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[RSM]", "Remote DeviceSync has been successfully created.", 0U));
+ break;
+
+ case RSM_ST_NTF_REQ:
+ res_inf = (Inic_NotificationResult_t *)result_ptr_->data_info;
+ if (res_inf != NULL)
+ {
+ if (res_inf->device_id == RSM_INVALID_NODE_ADDRESS)
+ {
+ self_->dev_infos.next_st = RSM_ST_NTF_ALL;
+ }
+ else
+ {
+ self_->dev_infos.next_st = RSM_ST_NTF_CLEAR;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ Srv_SetEvent(&self_->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ }
+ }
+ else
+ {
+ self_->dev_infos.next_st = RSM_ST_SYNC_ERR;
+ self_->dev_infos.curr_result.code = RSM_RES_ERR_SYNC;
+ self_->dev_infos.curr_result.details.inic_result = result_ptr_->result;
+ if (result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ self_->dev_infos.curr_result.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ }
+ else
+ {
+ self_->dev_infos.curr_result.details.tx_result = UCS_MSG_STAT_OK;
+ }
+
+ Srv_SetEvent(&self_->rsm_srv, RSM_EVENT_PROCESS_DEV);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[RSM]", "Synchronization to the remote device failed. Result code: 0x%02X", 1U, result_ptr_->result.code));
+ if (result_ptr_->result.info_ptr != NULL)
+ {
+ TR_ERROR_INIC_RESULT(self_->base_ptr->ucs_user_ptr, "[RSM]", result_ptr_->result.info_ptr, result_ptr_->result.info_size);
+ }
+ }
+}
+
+/*! \brief Event Callback function for the network status.
+ * \param self Instance pointer
+ * \param event_ptr Reference to the events
+ */
+static void Rsm_MnsNwStatusInfosCb(void *self, void *event_ptr)
+{
+ CRemoteSyncManagement *self_ = (CRemoteSyncManagement *)self;
+ Net_NetworkStatusParam_t *result_ptr_ = (Net_NetworkStatusParam_t *)event_ptr;
+ bool signal_synclost = false;
+
+ if ((RSM_MASK_NETWORK_NODE_ADDRESS & result_ptr_->change_mask) == RSM_MASK_NETWORK_NODE_ADDRESS)
+ {
+ if (result_ptr_->node_address != 0xFFFFU)
+ {
+ if ((self_->event_param.own_device_address != RSM_INVALID_NODE_ADDRESS) &&
+ (result_ptr_->node_address != self_->event_param.own_device_address))
+ {
+ self_->last_synclost_cause = RSM_SLC_SYSMODIF;
+ signal_synclost = true;
+ }
+
+ self_->event_param.own_device_address = result_ptr_->node_address;
+ }
+ }
+
+ if (signal_synclost)
+ {
+ Srv_SetEvent(&self_->rsm_srv, RSM_EVENT_SIG_SYNCLOST);
+ }
+}
+
+/*! \brief Event Callback function that signals that a TxMsgObj is now available.
+ * \param self Instance pointer
+ * \param result_ptr Reference to the results
+ */
+static void Rsm_MsgObjAvailCb(void *self, void *result_ptr)
+{
+ CRemoteSyncManagement *self_ = (CRemoteSyncManagement *)self;
+ MISC_UNUSED(result_ptr);
+
+ Srv_SetEvent(&self_->rsm_srv, RSM_EVENT_PROCESS_DEV);
+
+ /* delete observer */
+ Inic_DelObsrvOnTxMsgObjAvail(self_->inic_ptr, &self_->event_param.txavailability_observer);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Notification Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Signals the successful synchronization to a remote device.
+ * \param self Instance pointer
+ */
+static void Rsm_SignalSyncCompleted (CRemoteSyncManagement * self)
+{
+ Rsm_ResultCb_t tmp_res_cb = self->dev_infos.curr_res_cb_fptr;
+ void * data = self->dev_infos.curr_user_data;
+ Rsm_Result_t res = {RSM_RES_SUCCESS, {UCS_MSG_STAT_OK, {UCS_RES_SUCCESS, NULL, 0}}};
+
+ self->dev_infos.sync_state = RSM_DEV_SYNCED;
+ self->dev_infos.next_st = RSM_ST_IDLE;
+ self->dev_infos.curr_res_cb_fptr = NULL;
+ self->dev_infos.curr_user_data = NULL;
+
+ if (tmp_res_cb != NULL)
+ {
+ tmp_res_cb(data, res);
+ }
+}
+
+/*! \brief Signals that the synchronization to a remote device failed.
+ * \param self Instance pointer
+ */
+static void Rsm_SignalSyncError (CRemoteSyncManagement * self)
+{
+ Rsm_ResultCb_t tmp_res_cb = self->dev_infos.curr_res_cb_fptr;
+ void * data = self->dev_infos.curr_user_data;
+
+ self->dev_infos.sync_state = RSM_DEV_UNSYNCED;
+ self->dev_infos.next_st = RSM_ST_IDLE;
+ self->dev_infos.curr_res_cb_fptr = NULL;
+ self->dev_infos.curr_user_data = NULL;
+
+ if (tmp_res_cb != NULL)
+ {
+ tmp_res_cb(data, self->dev_infos.curr_result);
+ }
+}
+
+/*! \brief Signals that the synchronization to a remote device has been lost.
+ * \param self Instance pointer
+ */
+static void Rsm_SignalSyncLost (CRemoteSyncManagement * self)
+{
+ if ((self->dev_infos.sync_state == RSM_DEV_SYNCED) &&
+ (!Rsm_IsLocal(self)))
+ {
+ self->dev_infos.sync_state = RSM_DEV_UNSYNCED;
+ if(Sub_GetNumObservers(&self->event_param.subject) > 0U)
+ {
+ Sub_Notify(&self->event_param.subject, &self->last_synclost_cause);
+ }
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_rtm.c b/ucs2-lib/src/ucs_rtm.c
new file mode 100644
index 0000000..6bc2ef1
--- /dev/null
+++ b/ucs2-lib/src/ucs_rtm.c
@@ -0,0 +1,1366 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Route Management.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_RTM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_rtm.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Priority of the RSM service used by scheduler */
+static const uint8_t RTM_SRV_PRIO = 250U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Event for resuming the handling of routes */
+static const Srv_Event_t RTM_EVENT_HANDLE_NEXTROUTE = 0x01U;
+/*! \brief Event for pausing the processing of routes */
+static const Srv_Event_t RTM_EVENT_PROCESS_PAUSE = 0x02U;
+/*! \brief Interval (in ms) for checking the RoutingJob queue */
+static const uint16_t RTM_JOB_CHECK_INTERVAL = 50U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Mask for the Network Availability Info */
+static const uint32_t RTM_MASK_NETWORK_AVAILABILITY = 0x0002U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Rtm_Service(void *self);
+static void Rtm_HandleNextRoute(CRouteManagement * self);
+static void Rtm_BuildRoute(CRouteManagement * self);
+static void Rtm_DestroyRoute(CRouteManagement * self);
+static bool Rtm_SetNextRouteIndex(CRouteManagement * self);
+static void Rtm_HandleRoutingError(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static void Rtm_ApiLocking(CRouteManagement *self, bool status);
+static bool Rtm_IsApiFree(CRouteManagement *self);
+static Ucs_Return_t Rtm_BuildEndPoint(CRouteManagement * self, Ucs_Rm_EndPoint_t * endpoint_ptr);
+static Ucs_Return_t Rtm_DeactivateRouteEndPoint(CRouteManagement * self, Ucs_Rm_EndPoint_t * endpoint_ptr);
+static Ucs_Rm_Route_t * Rtm_GetNextRoute(CRouteManagement * self);
+static bool Rtm_IsRouteBuildable(CRouteManagement * self);
+static bool Rtm_IsRouteDestructible(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static bool Rtm_IsRouteActivatable(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static void Rtm_DisableRoute(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static void Rtm_EnableRoute(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static bool Rtm_CheckEpResultSeverity(CRouteManagement * self, Ucs_Rm_Route_t * tgt_route_ptr, Ucs_Rm_EndPoint_t * endpoint_ptr);
+static void Rtm_EndPointDeterioredCb(void *self, void *result_ptr);
+static void Rtm_StartTmr4HandlingRoutes(CRouteManagement * self);
+static void Rtm_ExecRoutesHandling(void * self);
+static void Rtm_HandleProcessTermination(CRouteManagement * self);
+static void Rtm_StopRoutesHandling(CRouteManagement * self);
+static void Rtm_StartRoutingTimer (CRouteManagement * self);
+static void Rtm_ResetNodesAvailable(CRouteManagement * self);
+static bool Rtm_AreRouteNodesAvailable(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static bool Rtm_UnlockPossibleBlockings(CRouteManagement * self, Ucs_Rm_Route_t * tgt_route_ptr, Ucs_Rm_EndPoint_t * endpoint_ptr);
+static void Rtm_ReleaseSuspendedRoutes(CRouteManagement * self, Ucs_Rm_Node_t *node_ptr);
+static void Rtm_ForcesRouteToIdle(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr);
+static void Rtm_UcsInitSucceededCb(void *self, void *event_ptr);
+static void Rtm_MnsNwStatusInfosCb(void *self, void *event_ptr);
+static void Rtm_UninitializeService(void *self, void *error_code_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CRouteManagement */
+/*------------------------------------------------------------------------------------------------*/
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Remote Sync Manager class.
+ * \param self Instance pointer
+ * \param init_ptr init data_ptr
+ */
+void Rtm_Ctor(CRouteManagement *self, Rtm_InitData_t *init_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CRouteManagement));
+
+ /* Init all reference instances */
+ self->base_ptr = init_ptr->base_ptr;
+ self->epm_ptr = init_ptr->epm_ptr;
+ self->tm_ptr = &init_ptr->base_ptr->tm;
+ self->net_ptr = init_ptr->net_ptr;
+ self->report_fptr = init_ptr->report_fptr;
+
+ /* Initialize Route Management service */
+ Srv_Ctor(&self->rtm_srv, RTM_SRV_PRIO, self, &Rtm_Service);
+
+ /* Add Observer for UCS initialization Result */
+ Mobs_Ctor(&self->ucsinit_observer, self, EH_E_INIT_SUCCEEDED, &Rtm_UcsInitSucceededCb);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->ucsinit_observer);
+
+ /* Init and Add observer to the UCS termination event */
+ Mobs_Ctor(&self->ucstermination_observer, self, EH_M_TERMINATION_EVENTS, &Rtm_UninitializeService);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->ucstermination_observer);
+
+ /* Add RTM service to scheduler */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->rtm_srv);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Starts the process to buildup the given routes list.
+ *
+ * This function shall only be called once.
+ * \param self Instance pointer
+ * \param routes_list Routes list to be built
+ * \param size Size of the routes list
+ * \return Possible return values are
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked.
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_BUFFER_OVERFLOW if no TxHandles available
+ * - \c UCS_RET_ERR_PARAM At least one parameter is wrong
+ */
+Ucs_Return_t Rtm_StartProcess(CRouteManagement * self, Ucs_Rm_Route_t routes_list[], uint16_t size)
+{
+ Ucs_Return_t result = UCS_RET_ERR_API_LOCKED;
+
+ if (Rtm_IsApiFree(self) != false)
+ {
+ result = UCS_RET_ERR_PARAM;
+ if ((self != NULL) && (routes_list != NULL) && (size > 0U))
+ {
+ uint8_t k = 0U;
+ /* Function remains from now locked */
+ Rtm_ApiLocking(self, true);
+
+ /* Initializes private variables */
+ self->routes_list_size = size;
+ self->curr_route_index = 0U;
+ self->routes_list_ptr = &routes_list[0];
+
+ /* Initializes internal data structure */
+ for (; k < size; k++)
+ {
+ MISC_MEM_SET(&routes_list[k].internal_infos, 0, sizeof(Ucs_Rm_RouteInt_t));
+ }
+
+ Rtm_StartTmr4HandlingRoutes(self);
+ result = UCS_RET_SUCCESS;
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Deactivates respectively destroys the given route reference
+ * \param self Instance pointer
+ * \param route_ptr Reference to the route to be destroyed
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_PARAM At least one parameter is NULL
+ * - \c UCS_RET_ERR_ALREADY_SET Route is already inactive
+ */
+Ucs_Return_t Rtm_DeactivateRoute (CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((self != NULL) && (route_ptr != NULL))
+ {
+ if (Rtm_IsRouteDestructible(self, route_ptr))
+ {
+ Rtm_DisableRoute(self, route_ptr);
+ Rtm_StartTmr4HandlingRoutes(self);
+ result = UCS_RET_SUCCESS;
+ }
+ else
+ {
+ result = UCS_RET_ERR_ALREADY_SET;
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Builds respectively activates the given route reference
+ * \param self Instance pointer
+ * \param route_ptr Reference to the route to be destroyed
+ * \return Possible return values are
+ * - \c UCS_RET_SUCCESS if the transmission was started successfully
+ * - \c UCS_RET_ERR_PARAM At least one parameter is NULL
+ * - \c UCS_RET_ERR_ALREADY_SET Route is already active
+ */
+Ucs_Return_t Rtm_ActivateRoute(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((self != NULL) && (route_ptr != NULL))
+ {
+ if (Rtm_IsRouteActivatable(self, route_ptr))
+ {
+ Rtm_EnableRoute(self, route_ptr);
+ Rtm_StartTmr4HandlingRoutes(self);
+ result = UCS_RET_SUCCESS;
+ }
+ else
+ {
+ result = UCS_RET_ERR_ALREADY_SET;
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Sets the given node to \c available or \c not \c available and triggers the routing process to handle this change.
+ * \details In case of \c Available the function starts the routing process that checks whether there are endpoints to build on this node.
+ * In case of \c Unavailable the function informs sub modules like XRM to check whether there are resources to release and simultaneously unlock \c suspended routes that
+ * link to this node.
+ * \param self The routing instance pointer
+ * \param node_ptr Reference to the node to be looked for.
+ * \param available Specifies whether the node is available or not
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ---------------------------------------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_ALREADY_SET | Node is already set to "available" or "not available"
+ * UCS_RET_ERR_PARAM | At least one parameter is NULL.
+ * UCS_RET_ERR_NOT_INITIALIZED | UNICENS is not initialized
+ * UCS_RET_ERR_NOT_AVAILABLE | The function cannot be processed because the network is not available
+ */
+Ucs_Return_t Rtm_SetNodeAvailable(CRouteManagement * self, Ucs_Rm_Node_t *node_ptr, bool available)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ if ((self != NULL) && (node_ptr != NULL) && (node_ptr->signature_ptr != NULL))
+ {
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ if (self->nw_available)
+ {
+ ret_val = UCS_RET_ERR_ALREADY_SET;
+ if (available)
+ {
+ if (node_ptr->internal_infos.available == 0x00U)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Node with Addr {%X} is available", 1U, node_ptr->signature_ptr->node_address));
+ node_ptr->internal_infos.available = 0x01U;
+ Rtm_StartRoutingTimer(self);
+ ret_val = UCS_RET_SUCCESS;
+ }
+ }
+ else
+ {
+ if (node_ptr->internal_infos.available == 0x01U)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Node with Addr {%X} is not available", 1U, node_ptr->signature_ptr->node_address));
+ node_ptr->internal_infos.available = 0x00U;
+ Rtm_ReleaseSuspendedRoutes(self, node_ptr);
+ Epm_ReportInvalidDevice (self->epm_ptr, node_ptr->signature_ptr->node_address);
+ ret_val = UCS_RET_SUCCESS;
+ }
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Retrieves the "available" flag of the given node.
+ * \param self The routing instance pointer
+ * \param node_ptr Reference to the node to be looked for.
+ * \return The "available" flag of the node.
+ */
+bool Rtm_GetNodeAvailable(CRouteManagement * self, Ucs_Rm_Node_t *node_ptr)
+{
+ bool ret_val = false;
+
+ MISC_UNUSED (self);
+
+ if (node_ptr != NULL)
+ {
+ ret_val = (node_ptr->internal_infos.available == 0x01U) ? true:false;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Retrieves currently references of all routes attached to the given endpoint and stores It into an external routes table provided by user application.
+ * Thus, User application should provide an external reference to an empty routes table where the potential routes will be stored.
+ * That is, user application is responsible to allocate enough space to store the found routes. Otherwise, the max routes found will
+ * equal the list size.
+ * \param self The routing instance pointer
+ * \param ep_inst Reference to the endpoint to be looked for.
+ * \param ext_routes_list External empty table allocated by user application
+ * \param size_list Size of the provided list
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ---------------------------------------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is NULL.
+ */
+Ucs_Return_t Rtm_GetAttachedRoutes(CRouteManagement * self, Ucs_Rm_EndPoint_t * ep_inst,
+ Ucs_Rm_Route_t * ext_routes_list[], uint16_t size_list)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_PARAM;
+
+ MISC_UNUSED (self);
+
+ if ((ep_inst != NULL) && (ext_routes_list != NULL) && (size_list > 0U))
+ {
+ bool curr_index_empty = true;
+ uint8_t k = 0U, num_attached_routes = Sub_GetNumObservers(&ep_inst->internal_infos.subject_obj);
+ CDlNode *n_tmp = (&(ep_inst->internal_infos.subject_obj))->list.head;
+ Ucs_Rm_Route_t * tmp_rt = NULL;
+ ret_val = UCS_RET_SUCCESS;
+
+ for (; ((k < size_list) && (num_attached_routes > 0U) && (n_tmp != NULL)); k++)
+ {
+ ext_routes_list[k] = NULL;
+ do
+ {
+ CObserver *o_tmp = (CObserver *)n_tmp->data_ptr;
+ tmp_rt = (Ucs_Rm_Route_t *)o_tmp->inst_ptr;
+ if ((tmp_rt != NULL) && ((tmp_rt->internal_infos.route_state == UCS_RM_ROUTE_BUILT) ||
+ (tmp_rt->internal_infos.route_state == UCS_RM_ROUTE_CONSTRUCTION) ||
+ (tmp_rt->internal_infos.route_state == UCS_RM_ROUTE_DESTRUCTION)))
+ {
+ curr_index_empty = false;
+ ext_routes_list[k] = tmp_rt;
+ }
+ n_tmp = n_tmp->next;
+ num_attached_routes--;
+
+ } while ((curr_index_empty) && (num_attached_routes > 0U));
+ curr_index_empty = true;
+ }
+
+ if (k < size_list)
+ {
+ ext_routes_list[k] = NULL;
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Retrieves the \c ConnectionLabel of the given route.
+ * \param self The routing instance pointer
+ * \param route_ptr Reference to the route to be looked for.
+ * \return The "ConnectionLabel" of this route.
+ */
+uint16_t Rtm_GetConnectionLabel (CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ uint16_t conn_label = 0U;
+
+ if ((self != NULL) && (route_ptr != NULL) && (route_ptr->internal_infos.route_state == UCS_RM_ROUTE_BUILT))
+ {
+ conn_label = Epm_GetConnectionLabel(self->epm_ptr, route_ptr->source_endpoint_ptr);
+ }
+
+ return conn_label;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Private Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Service function of the Sync management.
+ * \param self Instance pointer
+ */
+static void Rtm_Service(void *self)
+{
+ CRouteManagement *self_ = (CRouteManagement *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->rtm_srv, &event_mask);
+
+ /* Event to process list of routes */
+ if((event_mask & RTM_EVENT_HANDLE_NEXTROUTE) == RTM_EVENT_HANDLE_NEXTROUTE)
+ {
+ Srv_ClearEvent(&self_->rtm_srv, RTM_EVENT_HANDLE_NEXTROUTE);
+ Rtm_HandleNextRoute(self_);
+ }
+
+ /* Event to pause processing of routes list */
+ if ((event_mask & RTM_EVENT_PROCESS_PAUSE) == RTM_EVENT_PROCESS_PAUSE)
+ {
+ Srv_ClearEvent(&self_->rtm_srv, RTM_EVENT_PROCESS_PAUSE);
+ Rtm_StopRoutesHandling(self_);
+ }
+}
+
+/*! \brief This function starts the routing timer.
+ *
+ * Whenever this function is called the routing management process will resume in case it has been paused.
+ * \param self Instance pointer
+ */
+static void Rtm_StartRoutingTimer (CRouteManagement * self)
+{
+ if ((NULL != self) && (NULL != self->routes_list_ptr) &&
+ (0U < self->routes_list_size))
+ {
+ Rtm_StartTmr4HandlingRoutes(self);
+ }
+}
+
+/*! \brief Handles the next route in the list.
+ * \param self Instance pointer
+ */
+static void Rtm_HandleNextRoute(CRouteManagement * self)
+{
+ Ucs_Rm_Route_t * tmp_route;
+ self->curr_route_ptr = Rtm_GetNextRoute(self);
+ tmp_route = self->curr_route_ptr;
+
+ switch (tmp_route->internal_infos.route_state)
+ {
+ case UCS_RM_ROUTE_IDLE:
+ if (Rtm_IsRouteBuildable(self) == true)
+ {
+ Rtm_BuildRoute(self);
+ }
+ break;
+
+ case UCS_RM_ROUTE_CONSTRUCTION:
+ Rtm_BuildRoute(self);
+ break;
+
+ case UCS_RM_ROUTE_DETERIORATED:
+ Rtm_HandleRoutingError(self, tmp_route);
+ break;
+
+ case UCS_RM_ROUTE_DESTRUCTION:
+ Rtm_DestroyRoute(self);
+ break;
+
+ case UCS_RM_ROUTE_SUSPENDED:
+ case UCS_RM_ROUTE_BUILT:
+ if (tmp_route->active == false)
+ {
+ Rtm_DestroyRoute(self);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*! \brief Checks whether the given route is buildable.
+ * \param self Instance pointer
+ * \return \c true if the route is buildable, otherwise \c false.
+ */
+static bool Rtm_IsRouteBuildable(CRouteManagement * self)
+{
+ bool result_check = false;
+
+ if (self->curr_route_ptr != NULL)
+ {
+ if ((self->curr_route_ptr->internal_infos.route_state == UCS_RM_ROUTE_IDLE) &&
+ (self->curr_route_ptr->active == true) &&
+ (self->curr_route_ptr->source_endpoint_ptr != NULL) &&
+ (self->curr_route_ptr->sink_endpoint_ptr != NULL))
+ {
+ result_check = true;
+ }
+ }
+
+ return result_check;
+}
+
+/*! \brief Checks whether the given route is destructible.
+ * \param self Instance pointer
+ * \param route_ptr Reference route to be checked
+ * \return \c true if the route is destructible, otherwise \c false.
+ */
+static bool Rtm_IsRouteDestructible(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ bool result_check = false;
+ MISC_UNUSED (self);
+
+ if ((route_ptr != NULL) && (route_ptr->active == 0x01U) && ((route_ptr->internal_infos.route_state == UCS_RM_ROUTE_BUILT) ||
+ (route_ptr->internal_infos.route_state == UCS_RM_ROUTE_SUSPENDED)))
+ {
+ result_check = true;
+ }
+
+ return result_check;
+}
+
+/*! \brief Checks whether the given route can be activated.
+ * \param self Instance pointer
+ * \param route_ptr Reference route to be checked
+ * \return \c true if the route is destructible, otherwise \c false.
+ */
+static bool Rtm_IsRouteActivatable(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ bool result_check = false;
+ MISC_UNUSED (self);
+
+ if ((route_ptr != NULL) && (route_ptr->internal_infos.route_state == UCS_RM_ROUTE_IDLE) && (route_ptr->active == 0x0U))
+ {
+ result_check = true;
+ }
+
+ return result_check;
+}
+
+/*! \brief Deactivates the given route reference.
+ * \param self Instance pointer
+ * \param route_ptr Reference route to be deactivated
+ */
+static void Rtm_DisableRoute(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ MISC_UNUSED (self);
+
+ if (route_ptr != NULL)
+ {
+ route_ptr->active = false;
+ }
+}
+
+/*! \brief Activates the given route reference.
+ * \param self Instance pointer
+ * \param route_ptr Reference route to be activated
+ */
+static void Rtm_EnableRoute(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ MISC_UNUSED (self);
+
+ if (route_ptr != NULL)
+ {
+ route_ptr->active = true;
+ }
+}
+
+/*! \brief Builds the current Route.
+ * \param self Instance pointer
+ */
+static void Rtm_BuildRoute(CRouteManagement * self)
+{
+ bool result_critical = false;
+ Ucs_Rm_EndPointState_t ep_state = Epm_GetState(self->epm_ptr, self->curr_route_ptr->source_endpoint_ptr);
+ switch (ep_state)
+ {
+ case UCS_RM_EP_IDLE:
+ result_critical = Rtm_CheckEpResultSeverity(self, self->curr_route_ptr, self->curr_route_ptr->source_endpoint_ptr);
+ if (!result_critical)
+ {
+ if (self->curr_route_ptr->internal_infos.src_obsvr_initialized == 0U)
+ {
+ self->curr_route_ptr->internal_infos.src_obsvr_initialized = 1U;
+ Epm_DelObserver(self->curr_route_ptr->source_endpoint_ptr, &self->curr_route_ptr->internal_infos.source_ep_observer);
+ Obs_Ctor(&self->curr_route_ptr->internal_infos.source_ep_observer, self->curr_route_ptr, &Rtm_EndPointDeterioredCb);
+ /* Initializes source endpoint internal data */
+ Epm_InitInternalInfos (self->epm_ptr, self->curr_route_ptr->source_endpoint_ptr);
+ }
+ (void)Rtm_BuildEndPoint(self, self->curr_route_ptr->source_endpoint_ptr);
+ }
+ break;
+
+ case UCS_RM_EP_BUILT:
+ /* In case of shared source endpoint by another route */
+ if (self->curr_route_ptr->internal_infos.src_obsvr_initialized == 0U)
+ {
+ self->curr_route_ptr->internal_infos.src_obsvr_initialized = 1U;
+ Epm_DelObserver(self->curr_route_ptr->source_endpoint_ptr, &self->curr_route_ptr->internal_infos.source_ep_observer);
+ Obs_Ctor(&self->curr_route_ptr->internal_infos.source_ep_observer, self->curr_route_ptr, &Rtm_EndPointDeterioredCb);
+ Epm_AddObserver(self->curr_route_ptr->source_endpoint_ptr, &self->curr_route_ptr->internal_infos.source_ep_observer);
+ }
+ ep_state = Epm_GetState(self->epm_ptr, self->curr_route_ptr->sink_endpoint_ptr);
+ switch(ep_state)
+ {
+ case UCS_RM_EP_IDLE:
+ result_critical = Rtm_CheckEpResultSeverity(self, self->curr_route_ptr, self->curr_route_ptr->sink_endpoint_ptr);
+ if (!result_critical)
+ {
+ if (self->curr_route_ptr->internal_infos.sink_obsvr_initialized == 0U)
+ {
+ self->curr_route_ptr->internal_infos.sink_obsvr_initialized = 1U;
+ Obs_Ctor(&self->curr_route_ptr->internal_infos.sink_ep_observer, self->curr_route_ptr, &Rtm_EndPointDeterioredCb);
+ /* Initializes sink endpoint internal data */
+ Epm_InitInternalInfos (self->epm_ptr, self->curr_route_ptr->sink_endpoint_ptr);
+ }
+ Epm_SetConnectionLabel(self->epm_ptr, self->curr_route_ptr->sink_endpoint_ptr, Epm_GetConnectionLabel(self->epm_ptr, self->curr_route_ptr->source_endpoint_ptr));
+ (void)Rtm_BuildEndPoint(self, self->curr_route_ptr->sink_endpoint_ptr);
+ }
+ break;
+
+ case UCS_RM_EP_BUILT:
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Route id {%X} is built", 1U, self->curr_route_ptr->route_id));
+ self->curr_route_ptr->internal_infos.route_state = UCS_RM_ROUTE_BUILT;
+ if (self->report_fptr != NULL)
+ {
+ self->report_fptr(self->curr_route_ptr, UCS_RM_ROUTE_INFOS_BUILT, self->base_ptr->ucs_user_ptr);
+ }
+ break;
+
+ case UCS_RM_EP_XRMPROCESSING:
+ default:
+ result_critical = Rtm_UnlockPossibleBlockings(self, self->curr_route_ptr, self->curr_route_ptr->sink_endpoint_ptr);
+ break;
+ }
+ break;
+
+ case UCS_RM_EP_XRMPROCESSING:
+ default:
+ result_critical = Rtm_UnlockPossibleBlockings(self, self->curr_route_ptr, self->curr_route_ptr->source_endpoint_ptr);
+ break;
+ }
+
+ if (result_critical)
+ {
+ self->curr_route_ptr->internal_infos.route_state = UCS_RM_ROUTE_DETERIORATED;
+ }
+}
+
+/*! \brief Destroys the current Route.
+ * \param self Instance pointer
+ */
+static void Rtm_DestroyRoute(CRouteManagement * self)
+{
+ bool result_critical = false;
+ bool destruction_completed = false;
+
+ Ucs_Rm_EndPointState_t ep_state = Epm_GetState(self->epm_ptr, self->curr_route_ptr->sink_endpoint_ptr);
+ switch (ep_state)
+ {
+ case UCS_RM_EP_BUILT:
+ (void)Rtm_DeactivateRouteEndPoint(self, self->curr_route_ptr->sink_endpoint_ptr);
+ break;
+
+ case UCS_RM_EP_IDLE:
+ ep_state = Epm_GetState(self->epm_ptr, self->curr_route_ptr->source_endpoint_ptr);
+ switch(ep_state)
+ {
+ case UCS_RM_EP_BUILT:
+ /* if source endpoint cannot be built since it's used in another route(s), however consider that the route is destroyed. */
+ if (Rtm_DeactivateRouteEndPoint(self, self->curr_route_ptr->source_endpoint_ptr) == UCS_RET_ERR_INVALID_SHADOW)
+ {
+ destruction_completed = true;
+ }
+ break;
+
+ case UCS_RM_EP_IDLE:
+ destruction_completed = true;
+ break;
+
+ case UCS_RM_EP_XRMPROCESSING:
+ default:
+ result_critical = Rtm_UnlockPossibleBlockings(self, self->curr_route_ptr, self->curr_route_ptr->source_endpoint_ptr);
+ break;
+ }
+ break;
+
+ case UCS_RM_EP_XRMPROCESSING:
+ default:
+ result_critical = Rtm_UnlockPossibleBlockings(self, self->curr_route_ptr, self->curr_route_ptr->sink_endpoint_ptr);
+ break;
+ }
+
+ if (result_critical)
+ {
+ self->curr_route_ptr->internal_infos.route_state = UCS_RM_ROUTE_DETERIORATED;
+ }
+ else if (destruction_completed)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Route id {%X} has been destroyed", 1U, self->curr_route_ptr->route_id));
+ self->curr_route_ptr->internal_infos.route_state = UCS_RM_ROUTE_IDLE;
+ self->curr_route_ptr->internal_infos.src_obsvr_initialized = 0U;
+
+ if (self->report_fptr != NULL)
+ {
+ self->report_fptr(self->curr_route_ptr, UCS_RM_ROUTE_INFOS_DESTROYED, self->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Builds the given endpoint.
+ * \param self Instance pointer
+ * \param endpoint_ptr Reference to the endpoint to be looked for
+ * \return Possible return values are
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked. Endpoint is currently being processed.
+ * - \c UCS_RET_SUCCESS the build process was set successfully
+ * - \c UCS_RET_ERR_PARAM NULL pointer detected in the parameter list
+ * - \c UCS_RET_ERR_ALREADY_SET the endpoint has already been set
+ */
+static Ucs_Return_t Rtm_BuildEndPoint(CRouteManagement * self, Ucs_Rm_EndPoint_t * endpoint_ptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((self != NULL) && (endpoint_ptr != NULL))
+ {
+ result = Epm_SetBuildProcess(self->epm_ptr, endpoint_ptr);
+ if (result == UCS_RET_SUCCESS)
+ {
+ Epm_AddObserver (endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? &self->curr_route_ptr->internal_infos.source_ep_observer: &self->curr_route_ptr->internal_infos.sink_ep_observer);
+ self->curr_route_ptr->internal_infos.route_state = UCS_RM_ROUTE_CONSTRUCTION;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Start Building Endpoint {%X} of type %s for route id %X", 3U, endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source":"Sink", self->curr_route_ptr->route_id));
+ }
+ else if (result == UCS_RET_ERR_ALREADY_SET)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Endpoint {%X} of type %s for route id %X has already been built", 3U, endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source":"Sink", self->curr_route_ptr->route_id));
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Destroys the given endpoint.
+ * \param self Instance pointer
+ * \param endpoint_ptr Reference to the endpoint to be looked for
+ * \return Possible return values are
+ * - \c UCS_RET_ERR_API_LOCKED the API is locked. Endpoint is currently being processed.
+ * - \c UCS_RET_SUCCESS the build process was set successfully
+ * - \c UCS_RET_ERR_PARAM NULL pointer detected in the parameter list
+ * - \c UCS_RET_ERR_ALREADY_SET the endpoint has already been set
+ * - \c UCS_RET_ERR_NOT_AVAILABLE the endpoint is no more available.
+ * - \c UCS_RET_ERR_INVALID_SHADOW the endpoint cannot be destroyed since it's still in use by another routes.
+ */
+static Ucs_Return_t Rtm_DeactivateRouteEndPoint(CRouteManagement * self, Ucs_Rm_EndPoint_t * endpoint_ptr)
+{
+ Ucs_Return_t result = UCS_RET_ERR_PARAM;
+
+ if ((self != NULL) && (endpoint_ptr != NULL) && (endpoint_ptr->node_obj_ptr != NULL) &&
+ (endpoint_ptr->node_obj_ptr->signature_ptr != NULL))
+ {
+ if ((endpoint_ptr->node_obj_ptr->internal_infos.available == 1U) ||
+ (endpoint_ptr->node_obj_ptr->signature_ptr->node_address == UCS_ADDR_LOCAL_DEV))
+ {
+ result = Epm_SetDestroyProcess(self->epm_ptr, endpoint_ptr);
+ if (result == UCS_RET_SUCCESS)
+ {
+ self->curr_route_ptr->internal_infos.route_state = UCS_RM_ROUTE_DESTRUCTION;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Start Destroying Endpoint {%X} of type %s for route id %X", 3U, endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source":"Sink", self->curr_route_ptr->route_id));
+ }
+ else if (result == UCS_RET_ERR_ALREADY_SET)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Endpoint {%X} of type %s for route id %X has already been destroyed", 3U, endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source":"Sink", self->curr_route_ptr->route_id));
+ }
+ else if (result == UCS_RET_ERR_INVALID_SHADOW)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Endpoint {%X} of type %s for route id %X cannot be destroyed since it's still used", 3U, endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source":"Sink", self->curr_route_ptr->route_id));
+ }
+ else if (result == UCS_RET_ERR_NOT_AVAILABLE)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Endpoint {%X} of type %s for route id %X is no more available", 3U, endpoint_ptr, (endpoint_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source" : "Sink", self->curr_route_ptr->route_id));
+ }
+ }
+ else
+ {
+ /* Completed */
+ Epm_ResetState(self->epm_ptr, endpoint_ptr);
+ }
+ }
+
+ return result;
+}
+
+/*! \brief Classifies and sets the corresponding route error and then informs user about the new state.
+ * \param self Instance pointer
+ * \param route_ptr Reference to the route to be looked for
+ */
+static void Rtm_HandleRoutingError(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ Ucs_Rm_Route_t * tmp_route = route_ptr;
+ Ucs_Rm_RouteInfos_t result_route = UCS_RM_ROUTE_INFOS_DESTROYED;
+ Ucs_Rm_RouteResult_t res_rt = tmp_route->internal_infos.last_route_result;
+
+ tmp_route->internal_infos.route_state = UCS_RM_ROUTE_IDLE;
+ tmp_route->internal_infos.last_route_result = UCS_RM_ROUTE_NOERROR;
+
+ if (res_rt != UCS_RM_ROUTE_CRITICAL)
+ {
+ if (tmp_route->source_endpoint_ptr->internal_infos.endpoint_state == UCS_RM_EP_IDLE)
+ {
+ if (Rtm_CheckEpResultSeverity(self, tmp_route, tmp_route->source_endpoint_ptr))
+ {
+ Epm_ResetState(self->epm_ptr, tmp_route->source_endpoint_ptr);
+ tmp_route->internal_infos.route_state = UCS_RM_ROUTE_SUSPENDED;
+ result_route = UCS_RM_ROUTE_INFOS_SUSPENDED;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Route id {%X} is suspended", 1U, tmp_route->route_id));
+ }
+ }
+ else if (tmp_route->sink_endpoint_ptr->internal_infos.endpoint_state == UCS_RM_EP_IDLE)
+ {
+ if (Rtm_CheckEpResultSeverity(self, tmp_route, tmp_route->sink_endpoint_ptr))
+ {
+ Epm_ResetState(self->epm_ptr, tmp_route->sink_endpoint_ptr);
+ tmp_route->internal_infos.route_state = UCS_RM_ROUTE_SUSPENDED;
+ result_route = UCS_RM_ROUTE_INFOS_SUSPENDED;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Route id {%X} is suspended", 1U, tmp_route->route_id));
+ }
+ }
+ else
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Route id {%X} is destroyed", 1U, tmp_route->route_id));
+ }
+ }
+ else
+ {
+ Epm_ResetState(self->epm_ptr, tmp_route->source_endpoint_ptr);
+ Epm_ResetState(self->epm_ptr, tmp_route->sink_endpoint_ptr);
+ tmp_route->internal_infos.route_state = UCS_RM_ROUTE_SUSPENDED;
+ result_route = UCS_RM_ROUTE_INFOS_SUSPENDED;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[RTM]", "Route id {%X} is suspended", 1U, tmp_route->route_id));
+ }
+
+ if (self->report_fptr != NULL)
+ {
+ self->report_fptr(tmp_route, result_route, self->base_ptr->ucs_user_ptr);
+ }
+}
+
+/*! \brief Checks whether the endpoint's result is critical or not and stores the result into the target route.
+ * \param self Instance pointer
+ * \param tgt_route_ptr Reference to the route that contains the endpoint to be looked for
+ * \param endpoint_ptr Reference to the endpoint to be looked for
+ * \return \c true if the endpoint result is critical, otherwise \c false.
+ */
+static bool Rtm_CheckEpResultSeverity(CRouteManagement * self, Ucs_Rm_Route_t * tgt_route_ptr, Ucs_Rm_EndPoint_t * endpoint_ptr)
+{
+ bool result_check = false;
+ Ucs_Rm_RouteResult_t result = UCS_RM_ROUTE_NOERROR;
+ /*! \brief Maximum number of retries allowed in error situation */
+ uint8_t RTM_MAX_NUM_RETRIES_IN_ERR = 0xFFU;
+
+ if ((endpoint_ptr != NULL) && (tgt_route_ptr != NULL))
+ {
+ switch (endpoint_ptr->internal_infos.xrm_result.code)
+ {
+ case UCS_XRM_RES_ERR_BUILD:
+ case UCS_XRM_RES_ERR_DESTROY:
+ case UCS_XRM_RES_ERR_SYNC:
+ switch (endpoint_ptr->internal_infos.xrm_result.details.result_type)
+ {
+ case UCS_XRM_RESULT_TYPE_TX:
+ if ((UCS_MSG_STAT_ERROR_CFG_NO_RCVR == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_FATAL_OA == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (endpoint_ptr->internal_infos.num_retries == RTM_MAX_NUM_RETRIES_IN_ERR))
+ {
+ result = UCS_RM_ROUTE_CRITICAL;
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[RTM]", "Critical error occurred on route id {%X} due to the transmission error code {Ucs_MsgTxStatus_t:0x%02X} observed in XRM.", 2U,
+ tgt_route_ptr->route_id, endpoint_ptr->internal_infos.xrm_result.details.tx_result));
+ }
+ else if ((UCS_MSG_STAT_ERROR_UNKNOWN == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_FATAL_WT == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_TIMEOUT == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_BF == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_CRC == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_NA_TRANS == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_ACK == endpoint_ptr->internal_infos.xrm_result.details.tx_result) ||
+ (UCS_MSG_STAT_ERROR_ID == endpoint_ptr->internal_infos.xrm_result.details.tx_result))
+ {
+ endpoint_ptr->internal_infos.num_retries++;
+ result = UCS_RM_ROUTE_UNCRITICAL;
+ }
+ break;
+
+ case UCS_XRM_RESULT_TYPE_TGT:
+ if ((UCS_RES_ERR_CONFIGURATION == endpoint_ptr->internal_infos.xrm_result.details.inic_result.code) ||
+ (UCS_RES_ERR_MOST_STANDARD == endpoint_ptr->internal_infos.xrm_result.details.inic_result.code) ||
+ (UCS_RES_ERR_SYSTEM == endpoint_ptr->internal_infos.xrm_result.details.inic_result.code) ||
+ (endpoint_ptr->internal_infos.num_retries == RTM_MAX_NUM_RETRIES_IN_ERR))
+ {
+ result = UCS_RM_ROUTE_CRITICAL;
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[RTM]", "Critical error occurred on route id {%X} due to the INIC result code {Ucs_Result_t:0x%02X} observed in XRM.", 2U,
+ tgt_route_ptr->route_id, endpoint_ptr->internal_infos.xrm_result.details.inic_result.code));
+ }
+ else if ((UCS_RES_ERR_BUSY == endpoint_ptr->internal_infos.xrm_result.details.inic_result.code) ||
+ (UCS_RES_ERR_TIMEOUT == endpoint_ptr->internal_infos.xrm_result.details.inic_result.code) ||
+ (UCS_RES_ERR_PROCESSING == endpoint_ptr->internal_infos.xrm_result.details.inic_result.code))
+ {
+ endpoint_ptr->internal_infos.num_retries++;
+ result = UCS_RM_ROUTE_UNCRITICAL;
+ }
+ break;
+
+ case UCS_XRM_RESULT_TYPE_INT:
+ if ((endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_NOT_AVAILABLE) ||
+ (endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_NOT_SUPPORTED) ||
+ (endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_PARAM) ||
+ (endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_NOT_INITIALIZED) ||
+ (endpoint_ptr->internal_infos.num_retries == RTM_MAX_NUM_RETRIES_IN_ERR))
+ {
+ result = UCS_RM_ROUTE_CRITICAL;
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[RTM]", "Critical error occurred on route id {%X} due to the internal error code {Ucs_Return_t:0x%02X} observed in XRM.", 2U,
+ tgt_route_ptr->route_id, endpoint_ptr->internal_infos.xrm_result.details.int_result));
+ }
+ else if ((endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_BUFFER_OVERFLOW) ||
+ (endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_API_LOCKED) ||
+ (endpoint_ptr->internal_infos.xrm_result.details.int_result == UCS_RET_ERR_INVALID_SHADOW))
+ {
+ endpoint_ptr->internal_infos.num_retries++;
+ result = UCS_RM_ROUTE_UNCRITICAL;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case UCS_XRM_RES_ERR_CONFIG:
+ result = UCS_RM_ROUTE_CRITICAL;
+ break;
+
+ case UCS_XRM_RES_SUCCESS_BUILD:
+ case UCS_XRM_RES_SUCCESS_DESTROY:
+ endpoint_ptr->internal_infos.num_retries = 0U;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Sets route result */
+ tgt_route_ptr->internal_infos.last_route_result = result;
+ if (result == UCS_RM_ROUTE_CRITICAL)
+ {
+ result_check = true;
+ }
+ }
+
+ MISC_UNUSED(self);
+
+ return result_check;
+}
+
+/*! \brief Sets curr_route_ptr to the next route.
+ * \param self Instance pointer
+ * \return \c true if the endpoint result is critical, otherwise \c false.
+ */
+static bool Rtm_SetNextRouteIndex(CRouteManagement * self)
+{
+ bool found = false;
+
+ if ((self->routes_list_size > 0U) && (self->nw_available))
+ {
+ uint16_t tmp_idx;
+ self->curr_route_index++;
+ self->curr_route_index = self->curr_route_index%self->routes_list_size;
+ tmp_idx = self->curr_route_index;
+
+ do
+ {
+ if (((self->routes_list_ptr[self->curr_route_index].internal_infos.route_state == UCS_RM_ROUTE_SUSPENDED) &&
+ (self->routes_list_ptr[self->curr_route_index].active == true)) ||
+ ((self->routes_list_ptr[self->curr_route_index].active == true) &&
+ (self->routes_list_ptr[self->curr_route_index].internal_infos.route_state == UCS_RM_ROUTE_BUILT)) ||
+ ((self->routes_list_ptr[self->curr_route_index].active == false) &&
+ (self->routes_list_ptr[self->curr_route_index].internal_infos.route_state == UCS_RM_ROUTE_IDLE)) ||
+ ((Rtm_AreRouteNodesAvailable(self, &self->routes_list_ptr[self->curr_route_index]) == false) &&
+ (self->routes_list_ptr[self->curr_route_index].internal_infos.route_state == UCS_RM_ROUTE_IDLE)))
+ {
+ self->curr_route_index++;
+ self->curr_route_index = self->curr_route_index%self->routes_list_size;
+ }
+ else
+ {
+ found = true;
+ }
+ }
+ while ((tmp_idx != self->curr_route_index) &&
+ (found == false));
+ }
+
+ return found;
+}
+
+/*! \brief Starts the timer for handling routes.
+ * \param self Instance pointer
+ */
+static void Rtm_StartTmr4HandlingRoutes(CRouteManagement * self)
+{
+ if((T_IsTimerInUse(&self->route_check) == false) &&
+ (!self->ucs_is_stopping))
+ {
+ Tm_SetTimer(self->tm_ptr,
+ &self->route_check,
+ &Rtm_ExecRoutesHandling,
+ self,
+ RTM_JOB_CHECK_INTERVAL,
+ RTM_JOB_CHECK_INTERVAL);
+ }
+}
+
+/*! \brief Gets the next route.
+ * \param self Instance pointer
+ * \return the next route to be handled
+ */
+static Ucs_Rm_Route_t * Rtm_GetNextRoute(CRouteManagement * self)
+{
+ self->routes_list_ptr[self->curr_route_index].internal_infos.rtm_inst = (Rtm_Inst_t *)(void *)self;
+ return &self->routes_list_ptr[self->curr_route_index];
+}
+
+/*! \brief Checks if the API is locked.
+ * \param self Instance pointer
+ * \return \c true if the API is not locked and the UCS are initialized, otherwise \c false.
+ */
+static bool Rtm_IsApiFree(CRouteManagement *self)
+{
+ return (self->lock_api == false);
+}
+
+/*! \brief Locks/Unlocks the RTM API.
+ * \param self Instance pointer
+ * \param status Locking status. \c true = Lock, \c false = Unlock
+ */
+static void Rtm_ApiLocking(CRouteManagement *self, bool status)
+{
+ self->lock_api = status;
+}
+
+/*! \brief Checks whether the nodes (source and sink) of the current route is available.
+ * \param self Instance pointer
+ * \param route_ptr Reference to the Route to be looked for
+ * \return \c true if the source endpoint's node is available, otherwise \c false.
+ */
+static bool Rtm_AreRouteNodesAvailable(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ bool result = false;
+ MISC_UNUSED (self);
+
+ if ((route_ptr->source_endpoint_ptr != NULL) && (route_ptr->sink_endpoint_ptr != NULL))
+ {
+ if ((route_ptr->source_endpoint_ptr->node_obj_ptr != NULL) &&
+ (route_ptr->source_endpoint_ptr->node_obj_ptr->signature_ptr != NULL) &&
+ (route_ptr->sink_endpoint_ptr->node_obj_ptr != NULL) &&
+ (route_ptr->sink_endpoint_ptr->node_obj_ptr->signature_ptr != NULL))
+ {
+ if (((1U == route_ptr->source_endpoint_ptr->node_obj_ptr->internal_infos.available) ||
+ (route_ptr->source_endpoint_ptr->node_obj_ptr->signature_ptr->node_address == UCS_ADDR_LOCAL_DEV) ||
+ (Net_IsOwnAddress(self->net_ptr, route_ptr->source_endpoint_ptr->node_obj_ptr->signature_ptr->node_address) == NET_IS_OWN_ADDR_NODE)) &&
+ ((Net_IsOwnAddress(self->net_ptr, route_ptr->sink_endpoint_ptr->node_obj_ptr->signature_ptr->node_address) == NET_IS_OWN_ADDR_NODE) ||
+ (route_ptr->sink_endpoint_ptr->node_obj_ptr->signature_ptr->node_address == UCS_ADDR_LOCAL_DEV) ||
+ (1U == route_ptr->sink_endpoint_ptr->node_obj_ptr->internal_infos.available)))
+ {
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[RTM]", "ERROR PARAMETER on route id {%X}: At least one endpoint is NULL.", 1U, route_ptr->route_id));
+ }
+
+ return result;
+}
+
+/*! \brief Checks if we encountered a deadlock situation with the given route and if we do, resolves It by resetting the endpoint concerned.
+ *
+ * Since we can encounter the situation that the construction of an endpoint fails and the routing management is not aware of that (synchronous vs asynchronous response)
+ * and still consider that the route is processing. In such a case the RTM process will never get a response and will wait in vain for It.
+ * Therefore, the role of this function is to release the blocking situation in resetting the concerned endpoint.
+ * \param self Instance pointer
+ * \param tgt_route_ptr Reference to the route that contains the endpoint to be looked for
+ * \param endpoint_ptr Reference to the endpoint to be looked for
+ * \return \c true if the endpoint's result is critical, otherwise \c false
+ */
+static bool Rtm_UnlockPossibleBlockings(CRouteManagement * self, Ucs_Rm_Route_t * tgt_route_ptr, Ucs_Rm_EndPoint_t * endpoint_ptr)
+{
+ bool result_critical = Rtm_CheckEpResultSeverity(self, tgt_route_ptr, endpoint_ptr);
+ if (!result_critical)
+ {
+ if (UCS_RM_ROUTE_UNCRITICAL == self->curr_route_ptr->internal_infos.last_route_result)
+ {
+ Epm_ResetState(self->epm_ptr, endpoint_ptr);
+ }
+ }
+ return result_critical;
+}
+
+/*! \brief Stops routes handling.
+ * \param self Instance pointer
+ */
+static void Rtm_StopRoutesHandling(CRouteManagement * self)
+{
+ Tm_ClearTimer(self->tm_ptr, &self->route_check);
+}
+
+/*! \brief Releases all routes endpoints and notifies that the process is terminated for all "active" routes, which are not built or suspended.
+ * \param self Instance pointer
+ */
+static void Rtm_HandleProcessTermination(CRouteManagement * self)
+{
+ if ((self->routes_list_ptr != NULL) && (self->routes_list_size > 0U))
+ {
+ uint8_t k = 0U;
+
+ for (; k < self->routes_list_size; k++)
+ {
+ Epm_ClearIntInfos(self->epm_ptr, self->routes_list_ptr[k].source_endpoint_ptr);
+ Epm_ClearIntInfos(self->epm_ptr, self->routes_list_ptr[k].sink_endpoint_ptr);
+
+ if ((self->routes_list_ptr[k].active == 1U) &&
+ (self->routes_list_ptr[k].internal_infos.notify_termination == 0U) &&
+ (self->routes_list_ptr[k].internal_infos.route_state != UCS_RM_ROUTE_BUILT) &&
+ (self->routes_list_ptr[k].internal_infos.route_state != UCS_RM_ROUTE_SUSPENDED))
+ {
+ if ((self->routes_list_ptr[k].internal_infos.route_state == UCS_RM_ROUTE_CONSTRUCTION) ||
+ (self->routes_list_ptr[k].internal_infos.route_state == UCS_RM_ROUTE_DESTRUCTION))
+ {
+ self->routes_list_ptr[k].internal_infos.route_state = UCS_RM_ROUTE_IDLE;
+ }
+
+ self->routes_list_ptr[k].internal_infos.notify_termination = 0x01U;
+ if (self->report_fptr != NULL)
+ {
+ self->report_fptr(&self->routes_list_ptr[k], UCS_RM_ROUTE_INFOS_PROCESS_STOP, self->base_ptr->ucs_user_ptr);
+ }
+ }
+ }
+ }
+}
+
+/*! \brief Resets the availability flag of all nodes involved in routing process.
+ * \param self Instance pointer
+ */
+static void Rtm_ResetNodesAvailable(CRouteManagement * self)
+{
+ uint8_t k = 0U;
+
+ if ((self->routes_list_ptr != NULL) && (self->routes_list_size > 0U))
+ {
+ for (; k < self->routes_list_size; k++)
+ {
+ if ((self->routes_list_ptr[k].sink_endpoint_ptr != NULL) &&
+ (self->routes_list_ptr[k].sink_endpoint_ptr->node_obj_ptr != NULL))
+ {
+ self->routes_list_ptr[k].sink_endpoint_ptr->node_obj_ptr->internal_infos.available = 0U;
+ }
+
+ if ((self->routes_list_ptr[k].source_endpoint_ptr != NULL) &&
+ (self->routes_list_ptr[k].source_endpoint_ptr->node_obj_ptr != NULL))
+ {
+ self->routes_list_ptr[k].source_endpoint_ptr->node_obj_ptr->internal_infos.available = 0U;
+ }
+ }
+ }
+}
+
+/*! \brief Releases all suspended routes of the given node.
+ * \details This function should only be called when the provided node, on which the suspended routes are,
+ * is set to "not available".
+ * \param self Instance pointer
+ * \param node_ptr Reference to the node to be looked for
+ */
+static void Rtm_ReleaseSuspendedRoutes(CRouteManagement * self, Ucs_Rm_Node_t *node_ptr)
+{
+ uint8_t k = 0U;
+ bool is_ep_result_critical = false;
+
+ if ((self != NULL) && (self->routes_list_ptr != NULL) &&
+ (self->routes_list_size > 0U) && (node_ptr != NULL))
+ {
+ for (; k < self->routes_list_size; k++)
+ {
+ is_ep_result_critical = Rtm_CheckEpResultSeverity(self, &self->routes_list_ptr[k], self->routes_list_ptr[k].sink_endpoint_ptr);
+ if ((self->routes_list_ptr[k].internal_infos.route_state == UCS_RM_ROUTE_SUSPENDED) ||
+ ((self->routes_list_ptr[k].internal_infos.route_state == UCS_RM_ROUTE_DETERIORATED) &&
+ (self->routes_list_ptr[k].internal_infos.last_route_result == UCS_RM_ROUTE_CRITICAL)) ||
+ ((self->routes_list_ptr[k].internal_infos.route_state == UCS_RM_ROUTE_CONSTRUCTION) &&
+ (is_ep_result_critical)))
+ {
+ if (((self->routes_list_ptr[k].source_endpoint_ptr != NULL) &&
+ (self->routes_list_ptr[k].source_endpoint_ptr->node_obj_ptr == node_ptr)) ||
+ ((self->routes_list_ptr[k].sink_endpoint_ptr != NULL) &&
+ (self->routes_list_ptr[k].sink_endpoint_ptr->node_obj_ptr == node_ptr)))
+ {
+ Rtm_ForcesRouteToIdle(self, &self->routes_list_ptr[k]);
+ }
+ }
+ }
+ }
+}
+
+/*! \brief Sets the given routes to the "Idle" state and resets its internal variables.
+ * \details This function is risky and should only be used in Rtm_ReleaseSuspendedRoutes(). Because it forces a route's state to "Idle"
+ * without any external events.
+ * \param self Instance pointer
+ * \param route_ptr Reference to the route to be set
+ */
+static void Rtm_ForcesRouteToIdle(CRouteManagement * self, Ucs_Rm_Route_t * route_ptr)
+{
+ if ((self != NULL) && (route_ptr != NULL))
+ {
+ route_ptr->internal_infos.route_state = UCS_RM_ROUTE_IDLE;
+ route_ptr->internal_infos.last_route_result = UCS_RM_ROUTE_NOERROR;
+ if (route_ptr->source_endpoint_ptr != NULL)
+ {
+ if (Rtm_CheckEpResultSeverity(self, route_ptr, route_ptr->source_endpoint_ptr))
+ {
+ Epm_ResetState(self->epm_ptr, route_ptr->source_endpoint_ptr);
+ }
+ }
+ if (route_ptr->sink_endpoint_ptr != NULL)
+ {
+ if (Rtm_CheckEpResultSeverity(self, route_ptr, route_ptr->sink_endpoint_ptr))
+ {
+ Epm_ResetState(self->epm_ptr, route_ptr->sink_endpoint_ptr);
+ }
+ }
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Callback Functions */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Called if UCS initialization has been succeeded.
+ * \param self Instance pointer
+ * \param event_ptr Reference to reported event
+ */
+static void Rtm_UcsInitSucceededCb(void *self, void *event_ptr)
+{
+ CRouteManagement *self_ = (CRouteManagement *)self;
+ MISC_UNUSED(event_ptr);
+
+ /* Remove ucsinit_observer */
+ Eh_DelObsrvInternalEvent(&self_->base_ptr->eh, &self_->ucsinit_observer);
+
+ /* Add network status observer */
+ Mobs_Ctor(&self_->nwstatus_observer, self, RTM_MASK_NETWORK_AVAILABILITY, &Rtm_MnsNwStatusInfosCb);
+ Net_AddObserverNetworkStatus(self_->net_ptr, &self_->nwstatus_observer);
+}
+
+/*! \brief Handle internal errors and un-initialize RTM service.
+ * \param self Instance pointer
+ * \param error_code_ptr Reference to internal error code
+ */
+static void Rtm_UninitializeService(void *self, void *error_code_ptr)
+{
+ CRouteManagement *self_ = (CRouteManagement *)self;
+ MISC_UNUSED(error_code_ptr);
+
+ self_->ucs_is_stopping = true;
+
+ /* Notify destruction of current routes */
+ Rtm_HandleProcessTermination(self_);
+
+ /* Remove RTM service from schedulers list */
+ (void)Scd_RemoveService(&self_->base_ptr->scd, &self_->rtm_srv);
+ /* Remove error/event observers */
+ Eh_DelObsrvInternalEvent(&self_->base_ptr->eh, &self_->ucstermination_observer);
+ Net_DelObserverNetworkStatus(self_->net_ptr, &self_->nwstatus_observer);
+
+ /* Unlock API */
+ Rtm_ApiLocking(self_, false);
+}
+
+/*! \brief Event Callback function for the network status.
+ * \param self Instance pointer
+ * \param event_ptr Reference to the events
+ */
+static void Rtm_MnsNwStatusInfosCb(void *self, void *event_ptr)
+{
+ CRouteManagement *self_ = (CRouteManagement *)self;
+ Net_NetworkStatusParam_t *result_ptr_ = (Net_NetworkStatusParam_t *)event_ptr;
+
+ if (RTM_MASK_NETWORK_AVAILABILITY == (RTM_MASK_NETWORK_AVAILABILITY & result_ptr_->change_mask))
+ {
+ if (UCS_NW_NOT_AVAILABLE == result_ptr_->availability)
+ {
+ self_->nw_available = false;
+ /* Resets Nodes availability flag */
+ Rtm_ResetNodesAvailable(self_);
+ /* Reports Network Status "NotAvailabe" */
+ Epm_ReportShutDown(self_->epm_ptr);
+ }
+ else
+ {
+ self_->nw_available = true;
+ /* Check whether there are routes to be processed */
+ Rtm_StartRoutingTimer (self_);
+ }
+ }
+}
+
+/*! \brief Event Callback function that signals that an endpoint is unavailable.
+ * \param self Instance pointer
+ * \param result_ptr Reference to the results
+ */
+static void Rtm_EndPointDeterioredCb(void *self, void *result_ptr)
+{
+ Ucs_Rm_Route_t * route_ptr = (Ucs_Rm_Route_t *)self;
+ Ucs_Rm_EndPoint_t * ep_ptr = (Ucs_Rm_EndPoint_t *)result_ptr;
+
+ if ((route_ptr->source_endpoint_ptr == ep_ptr) ||
+ (route_ptr->sink_endpoint_ptr == ep_ptr))
+ {
+ if (route_ptr->internal_infos.route_state == UCS_RM_ROUTE_BUILT)
+ {
+ TR_INFO((((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst)->base_ptr->ucs_user_ptr, "[RTM]", "Route id %X is deteriorated", 1U, route_ptr->route_id));
+ if (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE)
+ {
+ route_ptr->internal_infos.src_obsvr_initialized = 0U;
+ }
+
+ Rtm_HandleRoutingError((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst, route_ptr);
+
+ if ((((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst)->nw_available) &&
+ (!((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst)->ucs_is_stopping))
+ {
+ Rtm_StartTmr4HandlingRoutes((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst);
+ }
+ else if (((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst)->ucs_is_stopping)
+ {
+ Rtm_HandleProcessTermination((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst);
+ }
+ }
+ }
+ else
+ {
+ TR_ERROR((((CRouteManagement *)(void *)route_ptr->internal_infos.rtm_inst)->base_ptr->ucs_user_ptr, "[RTM]", "Wrong endpoint {%X} of type %s on route id {%X}.", 3U,
+ ep_ptr, (ep_ptr->endpoint_type == UCS_RM_EP_SOURCE) ? "Source":"Sink", route_ptr->route_id));
+ }
+}
+
+/*! \brief Processes the handling of all routes. This method is the callback function of the routing timer
+ * \c route_chek.
+ * \param self Instance pointer
+ */
+static void Rtm_ExecRoutesHandling(void* self)
+{
+ CRouteManagement *self_ = (CRouteManagement *)self;
+ if (!self_->ucs_is_stopping)
+ {
+ bool index_set = Rtm_SetNextRouteIndex(self_);
+ if (index_set)
+ {
+ Srv_SetEvent(&self_->rtm_srv, RTM_EVENT_HANDLE_NEXTROUTE);
+ }
+ else
+ {
+ Srv_SetEvent(&self_->rtm_srv, RTM_EVENT_PROCESS_PAUSE);
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[RTM]", "Handling process of routes is paused", 0U));
+ }
+ }
+ else
+ {
+ Rtm_HandleProcessTermination(self_);
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_scheduler.c b/ucs2-lib/src/ucs_scheduler.c
new file mode 100644
index 0000000..be7f7b8
--- /dev/null
+++ b/ucs2-lib/src/ucs_scheduler.c
@@ -0,0 +1,258 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the scheduler module. The module consists of the two classes
+ * CScheduler and CService.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_SCHEDULER
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_scheduler.h"
+#include "ucs_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
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Scd_Ctor(CScheduler *self, Scd_InitData_t *init_ptr, void * ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->ucs_user_ptr = ucs_user_ptr;
+ Dl_Ctor(&self->srv_list, ucs_user_ptr);
+ Ssub_Ctor(&self->service_request_subject, ucs_user_ptr);
+ (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(Dl_IsNodeInList(&self->srv_list, &srv_ptr->list_node) == false)
+ {
+ /* 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(result_ptr != NULL) /* 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_Remove(&self->srv_list, &srv_ptr->list_node) == DL_UNKNOWN_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(current_srv_ptr != NULL) /* Process registered services */
+ {
+ if(current_srv_ptr->service_fptr != NULL)
+ {
+ /* Are events pending for the current service */
+ if(current_srv_ptr->event_mask != SRV_EMPTY_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(current_srv_ptr != NULL)
+ {
+ if(current_srv_ptr->event_mask != SRV_EMPTY_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(self->scd_ptr->scd_srv_is_running == false)
+ {
+ 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/ucs2-lib/src/ucs_segmentation.c b/ucs2-lib/src/ucs_segmentation.c
new file mode 100644
index 0000000..bbdd786
--- /dev/null
+++ b/ucs2-lib/src/ucs_segmentation.c
@@ -0,0 +1,550 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of AMS Segmentation Class
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_AMSSEGM
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_segmentation.h"
+#include "ucs_ams.h"
+#include "ucs_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 Ucs_AmsRx_Msg_t *Segm_RxRetrieveProcessingHandle(CSegmentation *self, Msg_MostTel_t *tel_ptr);
+static void Segm_RxStoreProcessingHandle(CSegmentation *self, Ucs_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 Ucs_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 Ucs_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->ucs_user_ptr);
+ 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))
+ {
+ Ucs_AmsRx_Msg_t *rx_ptr = (Ucs_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, Ucs_AmsTx_Msg_t* msg_ptr, Msg_MostTel_t *tel_ptr)
+{
+ bool finished = false;
+ MISC_UNUSED(self);
+
+ tel_ptr->destination_addr = msg_ptr->destination_address;
+ Msg_SetAltMsgId((CMessage*)(void*)tel_ptr, msg_ptr->msg_id);
+ 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 (index == 0U)
+ {
+ 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 Ucs_AmsRx_Msg_t* Segm_RxRetrieveProcessingHandle(CSegmentation *self, Msg_MostTel_t *tel_ptr)
+{
+ Ucs_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->ucs_user_ptr, "[SEGM]", (ret == DL_OK));
+ msg_ptr = (Ucs_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, Ucs_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)
+ {
+ Ucs_AmsRx_Msg_t *msg_ptr = (Ucs_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)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = (Ucs_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)
+{
+ Ucs_AmsRx_Msg_t *msg_ptr = (Ucs_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.
+ */
+Ucs_AmsRx_Msg_t* Segm_RxExecuteSegmentation(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr)
+{
+ Ucs_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 Ucs_AmsRx_Msg_t* Segm_RxProcessTelId0(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr)
+{
+ Ucs_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 */
+ {
+ Ucs_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)
+{
+ Ucs_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 Ucs_AmsRx_Msg_t* Segm_RxProcessTelId3(CSegmentation *self, Msg_MostTel_t *tel_ptr)
+{
+ Ucs_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 */
+ {
+ Ucs_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/ucs2-lib/src/ucs_smm.c b/ucs2-lib/src/ucs_smm.c
new file mode 100644
index 0000000..f201d36
--- /dev/null
+++ b/ucs2-lib/src/ucs_smm.c
@@ -0,0 +1,219 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the class CStaticMemoryManager.
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_UCS_SMM_CLASS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_smm.h"
+#include "ucs_misc.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static Smm_Descriptor_t* Smm_GetTypeDescriptor(CStaticMemoryManager *self, Ams_MemUsage_t type);
+static void* Smm_Allocate(void *self, uint16_t mem_size, Ams_MemUsage_t type, void** custom_info_pptr);
+static void Smm_Free(void *self, void *mem_ptr, Ams_MemUsage_t type, void* custom_info_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the static memory manager
+ * \param self The instance
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Smm_Ctor(CStaticMemoryManager *self, void *ucs_user_ptr)
+{
+ uint8_t index;
+ self->ucs_user_ptr = ucs_user_ptr;
+
+ Dl_Ctor(&self->tx_object_descr.list, ucs_user_ptr); /* initialize descriptor lists */
+ Dl_Ctor(&self->rx_object_descr.list, ucs_user_ptr);
+ Dl_Ctor(&self->tx_payload_descr.list, ucs_user_ptr);
+ Dl_Ctor(&self->rx_payload_descr.list, ucs_user_ptr);
+ Dl_Ctor(&self->null_descr.list, ucs_user_ptr);
+
+ self->tx_object_descr.max_mem_size = AMSG_TX_OBJECT_SZ; /* initialize descriptor memory sizes */
+ self->rx_object_descr.max_mem_size = AMSG_RX_OBJECT_SZ;
+ self->tx_payload_descr.max_mem_size = SMM_SIZE_TX_MSG;
+ self->rx_payload_descr.max_mem_size = SMM_SIZE_RX_MSG;
+ self->null_descr.max_mem_size = 0U;
+
+ for (index = 0U; index < SMM_NUM_TX_MSGS; index++) /* initialize Tx objects and payload */
+ { /* CDlNode::data_ptr has to point to the memory */
+ Dln_Ctor(&self->resources.tx_objects[index].node, &self->resources.tx_objects[index].object);
+ Dl_InsertTail(&self->tx_object_descr.list, &self->resources.tx_objects[index].node);
+
+ Dln_Ctor(&self->resources.tx_payload[index].node, &self->resources.tx_payload[index].data);
+ Dl_InsertTail(&self->tx_payload_descr.list, &self->resources.tx_payload[index].node);
+ }
+
+ for (index = 0U; index < SMM_NUM_RX_MSGS; index++) /* initialize Rx objects and payload */
+ { /* CDlNode::data_ptr has to point to the memory */
+ Dln_Ctor(&self->resources.rx_objects[index].node, &self->resources.rx_objects[index].object);
+ Dl_InsertTail(&self->rx_object_descr.list, &self->resources.rx_objects[index].node);
+
+ Dln_Ctor(&self->resources.rx_payload[index].node, &self->resources.rx_payload[index].data);
+ Dl_InsertTail(&self->rx_payload_descr.list, &self->resources.rx_payload[index].node);
+ }
+}
+
+/*! \brief Load function of the static memory management plug-in.
+ * \param self The instance
+ * \param allocator_ptr Assignable interface for allocate and free functions
+ * \param rx_def_payload_size The default Rx allocation size the AMS uses if TelId "4" is missing.
+ * Just use for checks. Do not overrule.
+ * \return Returns \c UCS_RET_SUCCESS if the initialization succeeded, otherwise \c UCS_RET_ERR_PARAM.
+ */
+Ucs_Return_t Smm_LoadPlugin(CStaticMemoryManager *self, Ams_MemAllocator_t *allocator_ptr, uint16_t rx_def_payload_size)
+{
+ Ucs_Return_t ret = UCS_RET_SUCCESS;
+
+ allocator_ptr->inst_ptr = self; /* assign instance to allocator */
+ allocator_ptr->alloc_fptr = &Smm_Allocate; /* assign callback functions */
+ allocator_ptr->free_fptr = &Smm_Free;
+
+ if (rx_def_payload_size != SMM_SIZE_RX_MSG)
+ {
+ ret = UCS_RET_ERR_PARAM;
+ TR_ERROR((self->ucs_user_ptr, "[SMM]", "SMM initialization failed: wrong configuration of rx_def_payload_size.", 0U));
+ }
+
+ return ret;
+}
+
+/*! \brief Retrieves a descriptor for a memory type
+ * \param self The instance
+ * \param type Usage type of the requested memory
+ * \return Returns the respective descriptor for a memory type
+ */
+static Smm_Descriptor_t* Smm_GetTypeDescriptor(CStaticMemoryManager *self, Ams_MemUsage_t type)
+{
+ Smm_Descriptor_t* descr_ptr = NULL;
+
+ switch (type)
+ {
+ case AMS_MU_RX_OBJECT:
+ descr_ptr = &self->rx_object_descr;
+ break;
+ case AMS_MU_RX_PAYLOAD:
+ descr_ptr = &self->rx_payload_descr;
+ break;
+ case AMS_MU_TX_OBJECT:
+ descr_ptr = &self->tx_object_descr;
+ break;
+ case AMS_MU_TX_PAYLOAD:
+ descr_ptr = &self->tx_payload_descr;
+ break;
+ default:
+ TR_FAILED_ASSERT(self->ucs_user_ptr, "[SMM]"); /* requested memory for unknown type */
+ descr_ptr = &self->null_descr;
+ break;
+ }
+
+ return descr_ptr;
+}
+
+/*! \brief Allocates memory of a certain type
+ * \param self The instance
+ * \param mem_size Size of the memory in bytes
+ * \param type The memory usage type
+ * \param custom_info_pptr Reference to custom information
+ * \return Returns a reference to the allocated memory or \c NULL if the allocation is not possible
+ */
+static void* Smm_Allocate(void *self, uint16_t mem_size, Ams_MemUsage_t type, void** custom_info_pptr)
+{
+ CStaticMemoryManager *self_ = (CStaticMemoryManager*)self;
+ void *mem_ptr = NULL;
+ CDlNode *node_ptr = NULL;
+
+ Smm_Descriptor_t* descr_ptr = Smm_GetTypeDescriptor(self_, type);
+
+ if (mem_size <= descr_ptr->max_mem_size)
+ {
+ node_ptr = Dl_PopHead(&descr_ptr->list); /* size is ok, retrieve a node from the list */
+ }
+
+ if (node_ptr != NULL)
+ {
+ mem_ptr = Dln_GetData(node_ptr); /* retrieve reference of whole message object */
+ *custom_info_pptr = node_ptr;
+ }
+
+ return mem_ptr;
+}
+
+/*! \brief Frees memory of a certain type
+ * \param self The instance
+ * \param mem_ptr Reference to the memory chunk
+ * \param type The memory usage type
+ * \param custom_info_ptr Reference to custom information
+ */
+static void Smm_Free(void *self, void *mem_ptr, Ams_MemUsage_t type, void* custom_info_ptr)
+{
+ CStaticMemoryManager *self_ = (CStaticMemoryManager*)self;
+ Smm_Descriptor_t* descr_ptr = Smm_GetTypeDescriptor(self_, type);
+
+ Dl_InsertHead(&descr_ptr->list, (CDlNode*)custom_info_ptr);
+ MISC_UNUSED(mem_ptr);
+}
+
+/*! \brief Retrieves the current number of unused message objects.
+ * \param self The instance
+ * \param rx_cnt_ptr Application provided reference to write the current number of unused Rx message objects.
+ * \param tx_cnt_ptr Application provided reference to write the current number of unused Tx message objects.
+ * \return Returns \c UCS_RET_ERR_PARAM if \c NULL is provided otherwise \c UCS_RET_SUCCESS.
+ */
+Ucs_Return_t Smm_GetFreeBufferCnt(CStaticMemoryManager *self, uint16_t *rx_cnt_ptr, uint16_t *tx_cnt_ptr)
+{
+ Ucs_Return_t ret = UCS_RET_SUCCESS;
+
+ if ((tx_cnt_ptr != NULL) && (rx_cnt_ptr != NULL))
+ {
+ *rx_cnt_ptr = Dl_GetSize(&self->rx_object_descr.list);
+ *tx_cnt_ptr = Dl_GetSize(&self->tx_object_descr.list);
+ }
+ else
+ {
+ ret = UCS_RET_ERR_PARAM;
+ }
+
+ return ret;
+}
+
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_sys_diag.c b/ucs2-lib/src/ucs_sys_diag.c
new file mode 100644
index 0000000..0c80eb6
--- /dev/null
+++ b/ucs2-lib/src/ucs_sys_diag.c
@@ -0,0 +1,1343 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the System Diagnosis class
+ * \details Performs the System Diagnosis
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_SYS_DIAG
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_misc.h"
+#include "ucs_ret_pb.h"
+#include "ucs_sys_diag.h"
+/*#include "ucs_mnsa.h"*/
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+#define SYS_DIAG_NUM_STATES 10U /*!< \brief Number of state machine states */
+#define SYS_DIAG_NUM_EVENTS 17U /*!< \brief Number of state machine events */
+
+#define SD_NUM_HELLO 10U /*!< \brief Number of Hello.Get Retries */
+#define SD_TIMEOUT_HELLO 150U /*!< \brief timeout used for repeating Hello.Get messages */
+#define SD_TIMEOUT_COMMAND 100U /*!< \brief timeout used for supervising INIC commands */
+#define SD_TIMEOUT_CABLE_DIAGNOSIS 3000U /*!< \brief timeout used for supervising cable link diagnosis */
+#define SD_DIAG_ADDR_BASE 0x0500U /*!< \brief Diagnosis Node Address of own node */
+
+#define SD_WELCOME_SUCCESS 0U /*!< \brief Welcome.Result reports success */
+
+#define SD_SIGNATURE_VERSION 1U /*!< \brief signature version used for System Diagnosis */
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the System Diagnosis service used by scheduler */
+static const uint8_t SD_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! Main event for the System Diagnosis service */
+static const Srv_Event_t SD_EVENT_SERVICE = 1U;
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal enumerators */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Possible events of the system diagnosis state machine */
+typedef enum SysDiag_Events_
+{
+ SD_E_NIL = 0U, /*!< \brief NIL Event */
+ SD_E_STARTDIAG = 1U, /*!< \brief StartDiag API function was called */
+ SD_E_SD_RES_OK = 2U, /*!< \brief MOSTNetworkSystemDiagnosis.Result received */
+ SD_E_ABORT = 3U, /*!< \brief Application requires stop of System Diagnosis */
+ SD_E_HELLO_OK = 4U, /*!< \brief Hello.Status received */
+ SD_E_HELLO_RETRY = 5U, /*!< \brief Retry the Hello.Get command */
+ SD_E_HELLO_ALL_DONE = 6U, /*!< \brief All retries of the Hello.Get command are done */
+ SD_E_WELCOME = 7U, /*!< \brief Welcome.Result, may be Ok or NotOk*/
+ SD_E_ALL_DONE = 8U, /*!< \brief All branches and segments of the network were explored*/
+ SD_E_PORT_FOUND = 9U, /*!< \brief An unexplored port was found */
+ SD_E_PORT_ENABLED = 10U, /*!< \brief A port was succesful enabled */
+ SD_E_PORT_DISABLED = 11U, /*!< \brief A port was succesful disabled */
+ SD_E_BRANCH_FOUND = 12U, /*!< \brief Another branch was found */
+ SD_E_CABLE_LINK_RES = 13U, /*!< \brief The CableLinkDiagnosis reported a result */
+ SD_E_ERROR = 14U, /*!< \brief An error was detected */
+ SD_E_TIMEOUT = 15U, /*!< \brief An timeout has been occurred */
+ SD_E_NO_SUCCESS = 16U /*!< \brief Welcome result was NoSuccess */
+} SysDiag_Events_t;
+
+/*! \brief States of the system diagnosis state machine */
+typedef enum SysDiag_State_
+{
+ SD_S_IDLE = 0U, /*!< \brief Idle state */
+ SD_S_WAIT_DIAG = 1U, /*!< \brief System Diagnosis started */
+ SD_S_WAIT_HELLO = 2U, /*!< \brief Hello command sent */
+ SD_S_HELLO_TIMEOUT = 3U, /*!< \brief Hello command timed out */
+ SD_S_WAIT_WELCOME = 4U, /*!< \brief Welcome sent */
+ SD_S_NEXT_PORT = 5U, /*!< \brief Next port found to be tested */
+ SD_S_WAIT_ENABLE = 6U, /*!< \brief Port Enable sent */
+ SD_S_WAIT_DISABLE = 7U, /*!< \brief Port Disable sent */
+ SD_S_CABLE_LINK_DIAG = 8U, /*!< \brief Wait for CableL Link Diagnosis Result */
+ SD_S_END = 9U /*!< \brief Wait for System Diagnosis stop */
+} SysDiag_State_t;
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Sd_Service(void *self);
+
+static void Sd_SysDiagInit(void* self);
+static void Sd_SysDiagStart(void *self);
+static void Sd_SysDiagStop(void *self);
+static void Sd_SendHello(void *self);
+static void Sd_Error(void *self);
+static void Sd_ErrorWelcome(void *self);
+static void Sd_SendWelcome(void *self);
+static void Sd_CableLinkDiagnosis(void *self);
+static void Sd_CalcPort(void *self);
+static void Sd_AllDone(void *self);
+static void Sd_EnablePort(void *self);
+static void Sd_DisablePort(void *self);
+static void Sd_Finish(void *self);
+static void Sd_Abort(void *self);
+static void Sd_StopDiagFailed(void *self);
+
+static void Sd_HelloTimeout(void *self);
+static void Sd_SysDiagTimeout(void *self);
+static void Sd_WelcomeTimeout(void *self);
+static void Sd_EnablePortTimeout(void *self);
+static void Sd_DisablePortTimeout(void *self);
+static void Sd_CableLinkDiagnosisTimeout(void *self);
+
+static void Sd_SysDiagStartResultCb(void *self, void *result_ptr);
+static void Sd_SysDiagStopResultCb(void *self, void *result_ptr);
+static void Sd_HelloStatusCb(void *self, void *result_ptr);
+static void Sd_WelcomeResultCb(void *self, void *result_ptr);
+static void Sd_EnablePortResultCb(void *self, void *result_ptr);
+static void Sd_DisablePortResultCb(void *self, void *result_ptr);
+static void Sd_CableLinkDiagnosisResultCb(void *self, void *result_ptr);
+static void Sd_OnTerminateEventCb(void *self, void *result_ptr);
+static void Sd_TimerCb(void *self);
+
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* State transition table (used by finite state machine) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief State transition table */
+static const Fsm_StateElem_t sys_diag_trans_tab[SYS_DIAG_NUM_STATES][SYS_DIAG_NUM_EVENTS] = /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+{
+
+ { /* State SD_S_IDLE */
+ /* SD_E_NIL */ {NULL, SD_S_IDLE },
+ /* SD_E_STARTDIAG */ {&Sd_SysDiagStart, SD_S_WAIT_DIAG },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_IDLE },
+ /* SD_E_ABORT */ {NULL, SD_S_IDLE },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_IDLE },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_IDLE },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_IDLE },
+ /* SD_E_WELCOME */ {NULL, SD_S_IDLE },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_IDLE },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_IDLE },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_IDLE },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_IDLE },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_IDLE },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_IDLE },
+ /* SD_E_ERROR */ {NULL, SD_S_IDLE },
+ /* SD_E_TIMEOUT */ {NULL, SD_S_IDLE },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_IDLE }
+ },
+
+ { /* State SD_S_WAIT_DIAG */
+ /* SD_E_NIL */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_SD_RES_OK */ {&Sd_SendHello, SD_S_WAIT_HELLO },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_WELCOME */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_WAIT_DIAG },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {&Sd_SysDiagTimeout, SD_S_END },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_WAIT_DIAG }
+ },
+
+ { /* State SD_S_WAIT_HELLO*/
+ /* SD_E_NIL */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {&Sd_SendWelcome, SD_S_WAIT_WELCOME },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_WELCOME */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_WAIT_HELLO },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {&Sd_HelloTimeout, SD_S_HELLO_TIMEOUT },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_WAIT_HELLO }
+ },
+
+ { /* State SD_S_HELLO_TIMEOUT */
+ /* SD_E_NIL */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_HELLO_RETRY */ {&Sd_SendHello, SD_S_WAIT_HELLO },
+ /* SD_E_HELLO_ALL_DONE */ {&Sd_CableLinkDiagnosis, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_WELCOME */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {NULL, SD_S_HELLO_TIMEOUT },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_HELLO_TIMEOUT }
+ },
+
+ { /* State SD_S_WAIT_WELCOME */
+ /* SD_E_NIL */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_WELCOME */ {&Sd_CalcPort, SD_S_NEXT_PORT },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_WAIT_WELCOME },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {&Sd_WelcomeTimeout, SD_S_END },
+ /* SD_E_NO_SUCCESS */ {&Sd_ErrorWelcome, SD_S_END }
+ },
+
+ { /* State SD_S_NEXT_PORT */
+ /* SD_E_NIL */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_WELCOME */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_ALL_DONE */ {&Sd_AllDone, SD_S_END },
+ /* SD_E_PORT_FOUND */ {&Sd_EnablePort, SD_S_WAIT_ENABLE },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_BRANCH_FOUND */ {&Sd_DisablePort, SD_S_WAIT_DISABLE },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {NULL, SD_S_NEXT_PORT },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_NEXT_PORT }
+ },
+
+ { /* State SD_S_WAIT_ENABLE */
+ /* SD_E_NIL */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_WELCOME */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_PORT_ENABLED */ {&Sd_SendHello, SD_S_WAIT_HELLO },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_WAIT_ENABLE },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {&Sd_EnablePortTimeout, SD_S_END },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_WAIT_ENABLE }
+ },
+
+ { /* State SD_S_WAIT_DISABLE */
+ /* SD_E_NIL */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_WELCOME */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_PORT_DISABLED */ {&Sd_EnablePort, SD_S_WAIT_ENABLE },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_WAIT_DISABLE },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {&Sd_DisablePortTimeout, SD_S_END },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_WAIT_DISABLE }
+ },
+
+ { /* State SD_S_CABLE_LINK_DIAG */
+ /* SD_E_NIL */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_SD_RES_OK */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_ABORT */ {&Sd_Abort, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_WELCOME */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_CABLE_LINK_DIAG },
+ /* SD_E_CABLE_LINK_RES */ {&Sd_CalcPort, SD_S_NEXT_PORT },
+ /* SD_E_ERROR */ {&Sd_Error, SD_S_END },
+ /* SD_E_TIMEOUT */ {&Sd_CableLinkDiagnosisTimeout, SD_S_END },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_CABLE_LINK_DIAG }
+ },
+
+ { /* State SD_S_END */
+ /* SD_E_NIL */ {NULL, SD_S_END },
+ /* SD_E_STARTDIAG */ {NULL, SD_S_END },
+ /* SD_E_SD_RES_OK */ {Sd_Finish, SD_S_IDLE },
+ /* SD_E_ABORT */ {NULL, SD_S_END },
+ /* SD_E_HELLO_OK */ {NULL, SD_S_END },
+ /* SD_E_HELLO_RETRY */ {NULL, SD_S_END },
+ /* SD_E_HELLO_ALL_DONE */ {NULL, SD_S_END },
+ /* SD_E_WELCOME */ {NULL, SD_S_END },
+ /* SD_E_ALL_DONE */ {NULL, SD_S_END },
+ /* SD_E_PORT_FOUND */ {NULL, SD_S_END },
+ /* SD_E_PORT_ENABLED */ {NULL, SD_S_END },
+ /* SD_E_PORT_DISABLED */ {NULL, SD_S_END },
+ /* SD_E_BRANCH_FOUND */ {NULL, SD_S_END },
+ /* SD_E_CABLE_LINK_RES */ {NULL, SD_S_END },
+ /* SD_E_ERROR */ {Sd_StopDiagFailed, SD_S_IDLE },
+ /* SD_E_TIMEOUT */ {Sd_StopDiagFailed, SD_S_IDLE },
+ /* SD_E_NO_SUCCESS */ {NULL, SD_S_END }
+ }
+
+};
+
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+
+/*! \brief Constructor of class CSysDiag.
+ * \param self Reference to CSysDiag instance
+ * \param inic Reference to CInic instance
+ * \param base Reference to CBase instance
+ * \param exc Reference to CExc instance
+ */
+ void SysDiag_Ctor(CSysDiag *self, CInic *inic, CBase *base, CExc *exc)
+{
+ MISC_MEM_SET((void *)self, 0, sizeof(*self));
+
+ self->inic = inic;
+ self->exc = exc;
+ self->base = base;
+
+ Fsm_Ctor(&self->fsm, self, &(sys_diag_trans_tab[0][0]), SYS_DIAG_NUM_EVENTS, SD_E_NIL);
+
+ Sobs_Ctor(&self->sys_diag_start, self, &Sd_SysDiagStartResultCb);
+ Sobs_Ctor(&self->sys_diag_stop, self, &Sd_SysDiagStopResultCb);
+ Sobs_Ctor(&self->sys_hello, self, &Sd_HelloStatusCb);
+ Sobs_Ctor(&self->sys_welcome, self, &Sd_WelcomeResultCb);
+ Sobs_Ctor(&self->sys_enable_port, self, &Sd_EnablePortResultCb);
+ Sobs_Ctor(&self->sys_disable_port, self, &Sd_DisablePortResultCb);
+ Sobs_Ctor(&self->sys_cable_link_diagnosis, self, &Sd_CableLinkDiagnosisResultCb);
+
+ /* register termination events */
+ Mobs_Ctor(&self->sys_terminate, self, EH_M_TERMINATION_EVENTS, &Sd_OnTerminateEventCb);
+ Eh_AddObsrvInternalEvent(&self->base->eh, &self->sys_terminate);
+
+ /* Initialize System Diagnosis service */
+ Srv_Ctor(&self->sd_srv, SD_SRV_PRIO, self, &Sd_Service);
+ /* Add System Diagnosis service to scheduler */
+ (void)Scd_AddService(&self->base->scd, &self->sd_srv);
+
+}
+
+/*! \brief Service function of the System Diagnosis service.
+ * \param self Reference to System Diagnosis object
+ */
+static void Sd_Service(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->sd_srv, &event_mask);
+ if(SD_EVENT_SERVICE == (event_mask & SD_EVENT_SERVICE)) /* Is event pending? */
+ {
+ Fsm_State_t result;
+ Srv_ClearEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "FSM __ %d %d", 2U, self_->fsm.current_state, self_->fsm.event_occured));
+ result = Fsm_Service(&self_->fsm);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", (result != FSM_STATE_ERROR));
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "FSM -> %d", 1U, self_->fsm.current_state));
+ MISC_UNUSED(result);
+ }
+}
+
+
+/*! \brief Starts the System Diagnosis State machine
+ *
+ * \param *self Reference to System Diagnosis object
+ * \param *obs_ptr Observer pointer
+ * \return UCS_RET_SUCCESS Operation successful
+ * \return UCS_RET_ERR_API_LOCKED System Diagnosis was already started
+ * \return UCS_RET_ERR_BUFFER_OVERFLOW Invalid observer
+ */
+Ucs_Return_t SysDiag_Run(CSysDiag *self, CSingleObserver *obs_ptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_SUCCESS;
+
+ if (self->startup_locked == false)
+ {
+ Ssub_Ret_t ret_ssub;
+
+ ret_ssub = Ssub_AddObserver(&self->sysdiag, obs_ptr);
+ if (ret_ssub != SSUB_UNKNOWN_OBSERVER) /* obs_ptr == NULL ? */
+ {
+ self->startup_locked = true;
+
+ Sd_SysDiagInit(self);
+
+ Fsm_SetEvent(&self->fsm, SD_E_STARTDIAG);
+ Srv_SetEvent(&self->sd_srv, SD_EVENT_SERVICE);
+
+ TR_INFO((self->base->ucs_user_ptr, "[SD]", "SysDiag_Run", 0U));
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_BUFFER_OVERFLOW; /* obs_ptr was invalid */
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_API_LOCKED;
+ }
+
+ return ret_val;
+}
+
+
+/*! \brief Aborts the System Diagnosis State machine
+ *
+ * \param *self Reference to System Diagnosis object
+ * \return UCS_RET_SUCCESS Operation successful
+ * \return UCS_RET_ERR_NOT_AVAILABLE System Diagnosis not running
+ */
+Ucs_Return_t SysDiag_Abort(CSysDiag *self)
+{
+ Ucs_Return_t ret_val = UCS_RET_SUCCESS;
+
+ if (self->startup_locked == true) /* check if System Diagnosis was started */
+ {
+ Tm_ClearTimer(&self->base->tm, &self->timer);
+
+ Fsm_SetEvent(&self->fsm, SD_E_ABORT);
+ Srv_SetEvent(&self->sd_srv, SD_EVENT_SERVICE);
+ TR_INFO((self->base->ucs_user_ptr, "[SD]", "SysDiag_Abort", 0U));
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ }
+
+ return ret_val;
+}
+
+/*! Initialize the System Diagnosis
+ *
+ * \param self Reference to System Diagnosis object
+ */
+static void Sd_SysDiagInit(void* self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ self_->hello_retry = SD_NUM_HELLO;
+ self_->segment_nr = 0U;
+ self_->num_ports = 0U;
+ self_->curr_branch = 0U;
+ self_->source.node_address = 0xFFFFU;
+ self_->source.available = false;
+ self_->last_result = SD_INIT;
+
+ self_->target.node_address = 0x0001U; /* address of own INIC */
+ self_->target.available = false;
+
+ self_->admin_node_address = SD_DIAG_ADDR_BASE;
+}
+
+
+/*! FSM action function: sets the INIC into System Diagnosis Mode
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_SysDiagStart(void *self)
+{
+ Ucs_Return_t ret_val;
+
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ ret_val = Inic_NwSysDiagnosis(self_->inic, &self_->sys_diag_start);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_SysDiagStart", 0U));
+
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_COMMAND,
+ 0U);
+
+ MISC_UNUSED(ret_val);
+}
+
+
+/*! Callback function for the Inic_NwSysDiagnosis() command
+ *
+ * \param *self Reference to System Diagnosis object
+ * \param *result_ptr Result of the Inic_NwSysDiagnosis() command
+ */
+static void Sd_SysDiagStartResultCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_SD_RES_OK);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_SysDiagStartResultCb SD_E_SD_RES_OK", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_SysDiagStartResultCb SD_E_ERROR", 0U));
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+
+/*! FSM action function: Timeout occured
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_SysDiagTimeout(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ TR_FAILED_ASSERT(self_->base->ucs_user_ptr, "[SD]");
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_UNSPECIFIED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Sd_SysDiagStop(self_);
+}
+
+/*! FSM action function: Timeout occured
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_EnablePortTimeout(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ TR_FAILED_ASSERT(self_->base->ucs_user_ptr, "[SD]");
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_UNSPECIFIED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Sd_SysDiagStop(self_);
+}
+
+/*! FSM action function: Timeout occured
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_DisablePortTimeout(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ TR_FAILED_ASSERT(self_->base->ucs_user_ptr, "[SD]");
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_UNSPECIFIED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Sd_SysDiagStop(self_);
+}
+
+/*! Helper function. Stops the System Diagnosis
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_SysDiagStop(void *self)
+{
+ Ucs_Return_t ret_val;
+
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ ret_val = Inic_NwSysDiagEnd(self_->inic, &self_->sys_diag_stop);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_SysDiagStop", 0U));
+ if (ret_val == UCS_RET_SUCCESS)
+ {
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_COMMAND,
+ 0U);
+ }
+ else
+ {
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_STOP_SYSDIAG_FAILED;
+
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+ }
+}
+
+
+/*! \brief Callback function for the Inic_NwSysDiagEnd() command
+ *
+ * \param *self Reference to System Diagnosis object
+ * \param *result_ptr Result of the Inic_NwSysDiagEnd() command
+ */
+static void Sd_SysDiagStopResultCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", UCS_RES_SUCCESS == result_ptr_->result.code);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_SD_RES_OK);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_SysDiagStopResultCb SD_E_SD_RES_OK", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_SysDiagStopResultCb SD_E_ERROR", 0U));
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+
+
+/*! FSM action function: Send Hello.Get command
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_SendHello(void *self)
+{
+ Ucs_Return_t ret_val;
+
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ ret_val = Exc_Hello_Get(self_->exc,
+ UCS_ADDR_BROADCAST_BLOCKING,
+ SD_SIGNATURE_VERSION,
+ &self_->sys_hello);
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_HELLO,
+ 0U);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+/*! Callback function for the Enc.Hello.Status message
+ *
+ * \param *self Reference to System Diagnosis object
+ * \param *result_ptr Result of the Exc_Hello_Get() command
+ */
+static void Sd_HelloStatusCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ /* read signature and store it for the Welcome command */
+ self_->target.signature = (*(Exc_HelloStatus_t *)(result_ptr_->data_info)).signature;
+ self_->target.version = (*(Exc_HelloStatus_t *)(result_ptr_->data_info)).version;
+
+ if (self_->segment_nr != 0U)
+ {
+ self_->target.node_address = self_->segment_nr + 0x0400U;
+
+ }
+
+ Fsm_SetEvent(&self_->fsm, SD_E_HELLO_OK);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_HelloStatusCb SD_E_SD_RES_OK", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_HelloStatusCb SD_E_ERROR", 0U));
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+/*! \brief Timer callback used for supervising INIC command timeouts.
+ * \param self Reference to System Diagnosis object
+ */
+static void Sd_TimerCb(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ Fsm_SetEvent(&self_->fsm, SD_E_TIMEOUT);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_TimerCb SD_E_TIMEOUT", 0U));
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+
+/*! FSM action function: retry hello command or start CableLinkDiagnosis
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_HelloTimeout(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ if (self_->hello_retry > 0U)
+ {
+ --self_->hello_retry;
+ Fsm_SetEvent(&self_->fsm, SD_E_HELLO_RETRY);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_HelloTimeout SD_E_HELLO_RETRY", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_HELLO_ALL_DONE);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_HelloTimeout SD_E_HELLO_ALL_DONE", 0U));
+ }
+
+ /*Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);*/
+}
+
+
+/*! FSM action function: Send Welcome message
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_SendWelcome(void *self)
+{
+ Ucs_Return_t ret_val;
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ self_->admin_node_address = SD_DIAG_ADDR_BASE + self_->segment_nr;
+
+ ret_val = Exc_Welcome_Sr(self_->exc,
+ self_->target.node_address,
+ self_->admin_node_address,
+ SD_SIGNATURE_VERSION,
+ self_->target.signature,
+ &self_->sys_welcome);
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_COMMAND,
+ 0U);
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+
+/*! \brief Function is called on reception of the Welcome.Result messsage
+ * \param self Reference to System Diagnosis object
+ * \param result_ptr Pointer to the result of the Welcome message
+ */
+static void Sd_WelcomeResultCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ /* read signature and store it for the Welcome command */
+ self_->target.result = (*(Exc_WelcomeResult_t *)(result_ptr_->data_info)).res;
+
+ if (self_->target.result == SD_WELCOME_SUCCESS)
+ {
+ self_->target.available = true;
+
+ if (self_->segment_nr == 0U)
+ {
+ self_->num_ports = self_->target.signature.num_ports;
+ }
+ else
+ {
+ self_->last_result = SD_SEGMENT;
+ }
+ /* do not report result for own node */
+ if (self_->segment_nr != 0U)
+ {
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+
+ self_->report.code = UCS_SD_TARGET_FOUND;
+ self_->report.segment.branch = self_->curr_branch;
+ self_->report.segment.num = self_->segment_nr;
+ self_->report.segment.source = self_->source.signature;
+ self_->report.segment.target = self_->target.signature;
+ /*self_->report.cable_link_info = 0U;*/ /* element is not written deliberately */
+ /*self_->report.err_info = 0U;*/ /* element is not written deliberately */
+
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_WelcomeResultCb ReportSegment", 0U));
+ }
+
+ Fsm_SetEvent(&self_->fsm, SD_E_WELCOME);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_WelcomeResultCb SD_E_WELCOME", 0U));
+ }
+ else
+ {
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.segment.branch = self_->curr_branch;
+ self_->report.segment.num = self_->segment_nr;
+ self_->report.segment.source = self_->source.signature;
+ self_->report.segment.target = self_->target.signature;
+ /*self_->report.cable_link_info = 0U;*/ /* element is not written deliberately */
+ self_->report.err_info = UCS_SD_ERR_WELCOME_NO_SUCCESS;
+
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Fsm_SetEvent(&self_->fsm, SD_E_NO_SUCCESS);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_WelcomeResultCb reported NoSuccess", 0U));
+ }
+
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_WelcomeResultCb Error SD_E_ERROR 0x%x", 1U, result_ptr_->result.code));
+
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+
+/*! \brief FSM action function: Calculate the next port tobe examined
+ * \param self Reference to System Diagnosis object
+ */
+static void Sd_CalcPort(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ switch (self_->last_result)
+ {
+ case SD_INIT:
+ self_->curr_branch = 0U; /* Master device has at least one port */
+ self_->source = self_->target;
+ self_->master = self_->target;
+
+ MISC_MEM_SET(&(self_->target), 0, sizeof(self_->target));
+ self_->last_result = SD_SEGMENT;
+ Fsm_SetEvent(&self_->fsm, SD_E_PORT_FOUND);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CalcPort SD_E_PORT_FOUND", 0U));
+ break;
+
+ case SD_SEGMENT:
+ if (self_->target.signature.num_ports > 1U)
+ {
+ self_->source = self_->target;
+ MISC_MEM_SET(&(self_->target), 0, sizeof(self_->target));
+ Fsm_SetEvent(&self_->fsm, SD_E_PORT_FOUND);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CalcPort SD_E_PORT_FOUND", 0U));
+ }
+ else /* switch to next branch if possible*/
+ {
+ if (self_->num_ports == (self_->curr_branch + 1U)) /* last branch */
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ALL_DONE);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CalcPort SD_E_ALL_DONE", 0U));
+ }
+ else
+ {
+ self_->segment_nr = 1U; /* reset segment number */
+ self_->curr_branch++; /* switch to next port */
+ self_->source = self_->master;
+ MISC_MEM_SET(&(self_->target), 0, sizeof(self_->target));
+ Fsm_SetEvent(&self_->fsm, SD_E_BRANCH_FOUND);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CalcPort SD_E_BRANCH_FOUND", 0U));
+ }
+ }
+ break;
+
+ case SD_CABLE_LINK:
+ if (self_->num_ports == (self_->curr_branch + 1U)) /* last branch */
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ALL_DONE);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CalcPort SD_E_ALL_DONE", 0U));
+ }
+ else
+ {
+ self_->segment_nr = 1U; /* reset segment number */
+ self_->curr_branch++; /* switch to next port */
+ self_->source = self_->master;
+ MISC_MEM_SET(&(self_->target), 0, sizeof(self_->target));
+ Fsm_SetEvent(&self_->fsm, SD_E_BRANCH_FOUND);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CalcPort SD_E_BRANCH_FOUND", 0U));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /*Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);*/
+}
+
+
+/*! \brief FSM action function: Enable port
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_EnablePort(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ uint16_t target_address;
+ uint8_t port_number;
+ Ucs_Return_t ret_val;
+
+ if (self_->segment_nr == 0U)
+ {
+ port_number = self_->curr_branch;
+ target_address = 0x0001U;
+ }
+ else
+ {
+ port_number = 1U;
+ target_address = self_->source.node_address;
+ }
+
+ ret_val = Exc_EnablePort_Sr(self_->exc, target_address, port_number, true, &self_->sys_enable_port);
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_COMMAND,
+ 0U);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+
+/*! Function is called on reception of the EnablePort.Result messsage
+ *
+ * \param *self Reference to System Diagnosis object
+ * \param *result_ptr
+ */
+static void Sd_EnablePortResultCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ self_->segment_nr++;
+ Fsm_SetEvent(&self_->fsm, SD_E_PORT_ENABLED);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_EnablePortResultCb SD_E_PORT_ENABLED", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_EnablePortResultCb SD_E_ERROR", 0U));
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+
+/*! \brief FSM action function:
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_DisablePort(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ uint16_t target_address;
+ uint8_t port_number;
+ Ucs_Return_t ret_val;
+
+ target_address = self_->admin_node_address;
+ port_number = self_->curr_branch;
+
+ ret_val = Exc_EnablePort_Sr(self_->exc, target_address, port_number, false, &self_->sys_disable_port);
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_COMMAND,
+ 0U);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+
+
+static void Sd_DisablePortResultCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_PORT_DISABLED);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_DisablePortResultCb SD_E_PORT_DISABLED", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_DisablePortResultCb SD_E_ERROR", 0U));
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+}
+
+
+/*! \brief FSM action function: Start CableLinkDiagnosis
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_CableLinkDiagnosis(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+ uint16_t target_address;
+ uint8_t port_number;
+ Ucs_Return_t ret_val;
+
+
+ if (self_->segment_nr != 0U) /* do not start CableLinkDiagnosis when connecting to local INIC */
+ {
+ target_address = self_->source.node_address;
+
+ if (self_->segment_nr == 1U)
+ {
+ port_number = self_->curr_branch;
+ }
+ else
+ {
+ port_number = 1U; /* OS81119: always port 1 */
+ }
+
+ self_->last_result = SD_CABLE_LINK;
+
+ ret_val = Exc_CableLinkDiagnosis_Start(self_->exc, target_address, port_number, &self_->sys_cable_link_diagnosis);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CableLinkDiagnosis", 0U));
+
+ Tm_SetTimer(&self_->base->tm,
+ &self_->timer,
+ &Sd_TimerCb,
+ self_,
+ SD_TIMEOUT_CABLE_DIAGNOSIS,
+ 0U);
+
+ TR_ASSERT(self_->base->ucs_user_ptr, "[SD]", ret_val == UCS_RET_SUCCESS);
+ MISC_UNUSED(ret_val);
+}
+ else /* stop SystemDiagnosis when connecting to local INIC failed */
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+ }
+}
+
+
+static void Sd_CableLinkDiagnosisResultCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ Exc_StdResult_t *result_ptr_ = (Exc_StdResult_t *)result_ptr;
+
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ if (result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+
+ self_->report.code = UCS_SD_CABLE_LINK_RES;
+ self_->report.segment.branch = self_->curr_branch;
+ self_->report.segment.num = self_->segment_nr;
+ self_->report.segment.source = self_->source.signature;
+ /*self_->report.segment.target = self_->target.signature;*/ /* structure is not written deliberately */
+ self_->report.cable_link_info = (*(Exc_CableLinkDiagResult_t *)(result_ptr_->data_info)).result;
+ /*self_->report.err_info = 0U;*/ /* element is not written deliberately */
+
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+
+ Fsm_SetEvent(&self_->fsm, SD_E_CABLE_LINK_RES);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CableLinkDiagnosisResultCb SD_E_CABLE_LINK_RES", 0U));
+ }
+ else
+ {
+ Fsm_SetEvent(&self_->fsm, SD_E_ERROR);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_CableLinkDiagnosisResultCb SD_E_ERROR %02X %02X %02X", 3U, result_ptr_->result.info_ptr[0], result_ptr_->result.info_ptr[1], result_ptr_->result.info_ptr[2]));
+ }
+
+ Srv_SetEvent(&self_->sd_srv, SD_EVENT_SERVICE);
+
+}
+
+
+/*! \brief FSM action function: React on Timeout of CableLinkDiagnosis
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_CableLinkDiagnosisTimeout(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_UNSPECIFIED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ TR_FAILED_ASSERT(self_->base->ucs_user_ptr, "[SD]");
+ Sd_SysDiagStop(self_);
+}
+
+/*! \brief FSM action function: React on Timeout of Welcome
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_WelcomeTimeout(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_UNSPECIFIED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ TR_FAILED_ASSERT(self_->base->ucs_user_ptr, "[SD]");
+ Sd_SysDiagStop(self_);
+}
+
+
+
+
+/*! \brief FSM action function: All branches and segments explored, finish System Diagnosis
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_AllDone(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_AllDone", 0U));
+
+ Sd_SysDiagStop(self_);
+}
+
+
+/*! \brief FSM action function: INIC system Diagnosis mode ended
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_Finish(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+
+ self_->report.code = UCS_SD_FINISHED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, true);
+
+ self_->startup_locked = false;
+
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_Finish", 0U));
+}
+
+/*! \brief FSM action function: An unexpected error occurred.
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_Error(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_UNSPECIFIED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Sd_SysDiagStop(self_);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_Error", 0U));
+}
+
+/*! \brief FSM action function: Welcome reports NoSuccess.
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_ErrorWelcome(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ Sd_SysDiagStop(self_);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_ErrorWelcome", 0U));
+}
+
+/*! \brief FSM action function: stopping system diagnosis mode failed
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_StopDiagFailed(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_STOP_SYSDIAG_FAILED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ /* always finish the System Diagnosis with event UCS_SD_FINISHED */
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_FINISHED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, true); /* remove the observer function */
+
+ self_->startup_locked = false;
+
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_StopDiagFailed", 0U));
+}
+
+/*! \brief FSM action function: Application requested to abort the System Diagnosis.
+ *
+ * \param *self Reference to System Diagnosis object
+ */
+static void Sd_Abort(void *self)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ABORTED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ Sd_SysDiagStop(self_);
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_Abort", 0U));
+}
+
+
+
+/*! Function is called on severe internal errors
+ *
+ * \param *self Reference to System Diagnosis object
+ * \param *result_ptr Reference to data
+ */
+static void Sd_OnTerminateEventCb(void *self, void *result_ptr)
+{
+ CSysDiag *self_ = (CSysDiag *)self;
+
+ MISC_UNUSED(result_ptr);
+
+ if (self_->fsm.current_state != SD_S_IDLE)
+ {
+ Tm_ClearTimer(&self_->base->tm, &self_->timer);
+
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_ERROR;
+ self_->report.err_info = UCS_SD_ERR_TERMINATED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, false);
+
+ /* always finish the System Diagnosis with event UCS_SD_FINISHED */
+ MISC_MEM_SET(&self_->report, 0, sizeof(self_->report));
+ self_->report.code = UCS_SD_FINISHED;
+ Ssub_Notify(&self_->sysdiag, &self_->report, true); /* remove the observer function */
+
+ TR_INFO((self_->base->ucs_user_ptr, "[SD]", "Sd_OnTerminateEventCb", 0U));
+
+ /* reset FSM */
+ self_->startup_locked = false;
+ Sd_SysDiagInit(self_);
+ self_->fsm.current_state = SD_S_IDLE;
+ }
+}
+
+
+
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_telqueue.c b/ucs2-lib/src/ucs_telqueue.c
new file mode 100644
index 0000000..d969981
--- /dev/null
+++ b/ucs2-lib/src/ucs_telqueue.c
@@ -0,0 +1,117 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of class CTelQueue
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_MSG_QUEUE
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_telqueue.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal constants */
+/*------------------------------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of class CTelQueue
+ * \param self The instance
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Telq_Ctor(CTelQueue *self, void *ucs_user_ptr)
+{
+ self->ucs_user_ptr = ucs_user_ptr;
+ Dl_Ctor(&self->list, self->ucs_user_ptr);
+}
+
+/*! \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/ucs2-lib/src/ucs_timer.c b/ucs2-lib/src/ucs_timer.c
new file mode 100644
index 0000000..6563374
--- /dev/null
+++ b/ucs2-lib/src/ucs_timer.c
@@ -0,0 +1,456 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the timer management module.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_TIMER
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_timer.h"
+#include "ucs_misc.h"
+#include "ucs_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
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Tm_Ctor(CTimerManagement *self, CScheduler *scd, const Tm_InitData_t *init_ptr, void * ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->ucs_user_ptr = ucs_user_ptr;
+ /* Initialize subjects and add observers */
+ Ssub_Ctor(&self->get_tick_count_subject, self->ucs_user_ptr);
+ (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->ucs_user_ptr);
+ (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, &current_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 UCS 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
+ * Ucs_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, &current_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;
+ if (self->timer_list.head != NULL)
+ {
+ /* 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 UCS 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, &current_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(self->timer_list.head == NULL) /* 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(result_ptr != NULL) /* 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(timer_ptr->node.next != NULL) /* 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/ucs2-lib/src/ucs_transceiver.c b/ucs2-lib/src/ucs_transceiver.c
new file mode 100644
index 0000000..54fdf0e
--- /dev/null
+++ b/ucs2-lib/src/ucs_transceiver.c
@@ -0,0 +1,290 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of class CTransceiver
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_TRCV
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_transceiver.h"
+#include "ucs_misc.h"
+#include "ucs_pmp.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Trcv_OnTxStatusInternal(void *self, Msg_MostTel_t *tel_ptr, Ucs_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 ucs_user_ptr User reference that needs to be passed in every callback function
+ * \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, void *ucs_user_ptr, uint8_t trace_id)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->fifo_ptr = fifo_ptr;
+ self->tx_def_src = def_src_addr;
+ self->ucs_user_ptr = ucs_user_ptr;
+ self->own_id = trace_id;
+ Pool_Ctor(&self->tx_msg_pool, self->tx_msgs, TRCV_SIZE_TX_POOL, ucs_user_ptr);
+ TR_ASSERT(self->ucs_user_ptr, "[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->ucs_user_ptr, "[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->ucs_user_ptr, "[TRCV]", (tel_ptr != NULL));
+ msg_ptr = (CMessage*)(void*)tel_ptr;
+
+ TR_INFO((self->ucs_user_ptr, "[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->ucs_user_ptr, "[TRCV]", (tel_ptr != NULL));
+ msg_ptr = (CMessage*)(void*)tel_ptr;
+
+ if (callback_fptr == NULL)
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[TRCV]", (inst_ptr == NULL));
+ callback_fptr = &Trcv_OnTxStatusInternal;
+ inst_ptr = self;
+ }
+
+ TR_INFO((self->ucs_user_ptr, "[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->ucs_user_ptr, "[TRCV]", (tel_ptr != NULL));
+ msg_ptr = (CMessage*)(void*)tel_ptr;
+
+ if (callback_fptr == NULL)
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[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, Ucs_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_->ucs_user_ptr, "[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/ucs2-lib/src/ucs_xrm.c b/ucs2-lib/src/ucs_xrm.c
new file mode 100644
index 0000000..d8cbbf6
--- /dev/null
+++ b/ucs2-lib/src/ucs_xrm.c
@@ -0,0 +1,1174 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Extended Resource Manager. This file contains the implementation of
+ * the basic functions of the class CExtendedResourceManager.
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_UCS_XRM_INT
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_xrm.h"
+#include "ucs_xrm_pv.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service parameters */
+/*------------------------------------------------------------------------------------------------*/
+/*! Priority of the XRM service used by scheduler */
+const uint8_t XRM_SRV_PRIO = 250U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Event to trigger Extended Resource Manager service */
+const Srv_Event_t XRM_EVENT_PROCESS = 0x01U;
+/*! \brief Event to trigger error handling */
+const Srv_Event_t XRM_EVENT_ERROR = 0x02U;
+/*! \brief Event to trigger request list of invalid resource handles */
+const Srv_Event_t XRM_EVENT_REQ_INV_RES_LST = 0x04U;
+/*! \brief Event to trigger destruction of invalid resources */
+const Srv_Event_t XRM_EVENT_DESTROY_INV_RES = 0x08U;
+/*! \brief Event to resume the destruction of resources */
+const Srv_Event_t XRM_EVENT_RESUME_JOB_DESTRUCT = 0x10U;
+/*! \brief Event to reset INIC's Resource Monitor */
+const Srv_Event_t XRM_EVENT_RESET_RES_MONITOR = 0x20U;
+/*! \brief Event to trigger notification for automatically destroyed resources */
+const Srv_Event_t XRM_EVENT_NOTIFY_AUTO_DEST_RES = 0x40U;
+/*! \brief Event to trigger notification for destroyed resources */
+const Srv_Event_t XRM_EVENT_NOTIFY_DESTROYED_JOB = 0x80U;
+/*! \brief Event to trigger notification for automatically destroyed resources on remote devices */
+const Srv_Event_t XRM_EVENT_NOTIFY_AUTO_DEST_RESR = 0x100U;
+/*! \brief Event to trigger configuration of a stream port */
+const Srv_Event_t XRM_EVENT_STREAMPORT_CONFIG_SET = 0x200U;
+/*! \brief Event to read configuration of a stream port */
+const Srv_Event_t XRM_EVENT_STREAMPORT_CONFIG_GET = 0x400U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Constants */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Invalid resource handle */
+const uint16_t XRM_INVALID_RESOURCE_HANDLE = 0xFFFFU;
+/*! \brief Invalid MOST connection label */
+const uint16_t XRM_INVALID_CONNECTION_LABEL = 0xFFFFU; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */
+/*! \brief Default value used for INIC sender handles */
+const uint16_t XRM_DEFAULT_SENDER_HANDLE = 0x0001U;
+/*! \brief Invalid device node address */
+const uint16_t XRM_INVALID_NODE_ADDRESS = 0x0000U;
+/*! \brief Mask for network availability info */
+const uint16_t XRM_MASK_NETWORK_AVAILABILITY = 0x0002U;
+/*! \brief Mask for node address update info */
+const uint16_t XRM_MASK_NETWORK_NODE_ADDRESS = 0x0010U;
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CExtendedResourceManager */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the Extended Resource Manager class.
+ * \param self Instance pointer
+ * \param data_ptr Data pointer (receive reference to MNS instance)
+ */
+void Xrm_Ctor(CExtendedResourceManager *self, Xrm_InitData_t *data_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(CExtendedResourceManager));
+
+ /* Retrieve the initialization data */
+ self->net_ptr = data_ptr->net_ptr;
+ self->base_ptr = data_ptr->base_ptr;
+ self->rsm_ptr = data_ptr->rsm_ptr;
+ self->inic_ptr = data_ptr->inic_ptr;
+ self->xrmp_ptr = data_ptr->xrmp_ptr;
+ self->res_debugging_fptr = data_ptr->res_debugging_fptr;
+
+ /* Set the flag that indicates the run mode of the instance */
+ self->IsInRemoteControlMode = (UCS_ADDR_LOCAL_DEV != Inic_GetTargetAddress(self->inic_ptr)) ? true:false;
+
+ /* Initialize observers */
+ Obs_Ctor(&self->obs.tx_msg_obj_obs, self, &Xrm_MsgObjAvailCb);
+ Obs_Ctor(&self->obs.resource_monitor_obs, self, &Xrm_ResourceMonitorCb);
+ Sobs_Ctor(&self->obs.std_result_obs, self, &Xrm_StdResultCb);
+ Sobs_Ctor(&self->obs.resource_invalid_list_obs, self, &Xrm_RequestResourceListResultCb);
+ Sobs_Ctor(&self->obs.resource_destroy_obs, self, &Xrm_DestroyResourcesResultCb);
+ Sobs_Ctor(&self->obs.stream_port_config_obs, self, &Xrm_Stream_PortConfigResult);
+ Sobs_Ctor(&self->obs.most_port_enable_obs, self, &Xrm_Most_PortEnableResult);
+ Sobs_Ctor(&self->obs.most_port_en_full_str_obs, self, &Xrm_Most_PortEnFullStrResult);
+ Obs_Ctor(&self->obs.rsm_sync_lost_obs, self, &Xrm_RmtDevSyncLostCb);
+
+ /* Add observer to resource monitor subject */
+ Inic_AddObsrvResMonitor(self->inic_ptr, &self->obs.resource_monitor_obs);
+ /* Initialize callback pointer for unmute callback */
+ self->obs.check_unmute_fptr = data_ptr->check_unmute_fptr;
+
+ /* Add observer to the MNS termination event */
+ Mobs_Ctor(&self->obs.internal_error_obs, self, EH_M_TERMINATION_EVENTS, &Xrm_UninitializeService);
+ Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->obs.internal_error_obs);
+ /* Add observer to the MNS network event */
+ Mobs_Ctor(&self->obs.nw_status_obs, self, (XRM_MASK_NETWORK_AVAILABILITY | XRM_MASK_NETWORK_NODE_ADDRESS), &Xrm_MnsNwStatusInfosCb);
+ Net_AddObserverNetworkStatus(self->net_ptr, &self->obs.nw_status_obs);
+ /* Add observer to the MNS RSM event */
+ Rsm_AddObserver(self->rsm_ptr, &self->obs.rsm_sync_lost_obs);
+
+ /* Initialize the Jobs list queue */
+ Dl_Ctor(&self->job_list, self->base_ptr->ucs_user_ptr);
+
+ /* Initialize XRM service */
+ Srv_Ctor(&self->xrm_srv, XRM_SRV_PRIO, self, &Xrm_Service);
+ /* Add XRM service to scheduler */
+ (void)Scd_AddService(&self->base_ptr->scd, &self->xrm_srv);
+}
+
+/*! \brief Service function of the Extended Resource Manager.
+ * \param self Instance pointer
+ */
+void Xrm_Service(void *self)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Srv_Event_t event_mask;
+ Srv_GetEvent(&self_->xrm_srv, &event_mask);
+
+ /* Handle event to process a XRM job */
+ if((event_mask & XRM_EVENT_PROCESS) == XRM_EVENT_PROCESS)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_PROCESS);
+ Xrm_ProcessJob(self_);
+ }
+ /* Handle event to request the list of invalid resource handles */
+ if((event_mask & XRM_EVENT_REQ_INV_RES_LST) == XRM_EVENT_REQ_INV_RES_LST)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_REQ_INV_RES_LST);
+ Xrm_RequestResourceList(self_);
+ }
+ /* Handle event to destroy invalid INIC resources */
+ if((event_mask & XRM_EVENT_DESTROY_INV_RES) == XRM_EVENT_DESTROY_INV_RES)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_DESTROY_INV_RES);
+ Xrm_DestroyResources(self_, &Xrm_DestroyResourcesResultCb);
+ }
+ /* Handle event to resume the destruction of all INIC resources of a job */
+ if((event_mask & XRM_EVENT_RESUME_JOB_DESTRUCT) == XRM_EVENT_RESUME_JOB_DESTRUCT)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_RESUME_JOB_DESTRUCT);
+ Xrm_ResumeJobDestruction(self_);
+ }
+ /* Handle event to resume the destruction of all INIC resources of a job */
+ if((event_mask & XRM_EVENT_RESET_RES_MONITOR) == XRM_EVENT_RESET_RES_MONITOR)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_RESET_RES_MONITOR);
+ Xrm_ResetResourceMonitor(self_);
+ }
+ /* Handle error event */
+ if((event_mask & XRM_EVENT_ERROR) == XRM_EVENT_ERROR)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ Xrm_HandleError(self_);
+ }
+ /* Handle event to notify application of automatically destroyed resources */
+ if((event_mask & XRM_EVENT_NOTIFY_AUTO_DEST_RES) == XRM_EVENT_NOTIFY_AUTO_DEST_RES)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_NOTIFY_AUTO_DEST_RES);
+ Xrm_ReportAutoDestructionResult(self_);
+ }
+ /* Handle event to report result of resource destruction of a specific XRM job */
+ if((event_mask & XRM_EVENT_NOTIFY_DESTROYED_JOB) == XRM_EVENT_NOTIFY_DESTROYED_JOB)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_NOTIFY_DESTROYED_JOB);
+ Xrm_ReportJobDestructionResult(self_);
+ }
+ /* Handle event to notify application that resources on remote devices have been automatically destroyed */
+ if ((event_mask & XRM_EVENT_NOTIFY_AUTO_DEST_RESR) == XRM_EVENT_NOTIFY_AUTO_DEST_RESR)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_NOTIFY_AUTO_DEST_RESR);
+ Xrm_ReleaseResrcHandles(self_);
+ }
+ /* Handle event to set streaming port configuration */
+ if ((event_mask & XRM_EVENT_STREAMPORT_CONFIG_SET) == XRM_EVENT_STREAMPORT_CONFIG_SET)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_STREAMPORT_CONFIG_SET);
+ (void)Xrm_SetStreamPortConfiguration(self_);
+ }
+ /* Handle event to get streaming port configuration */
+ if ((event_mask & XRM_EVENT_STREAMPORT_CONFIG_GET) == XRM_EVENT_STREAMPORT_CONFIG_GET)
+ {
+ Srv_ClearEvent(&self_->xrm_srv, XRM_EVENT_STREAMPORT_CONFIG_GET);
+ (void)Xrm_GetStreamPortConfiguration(self_);
+ }
+}
+
+/*! \brief Checks if the API is locked and the MNS are initialized.
+ * \param self Instance pointer
+ * \return \c true if the API is not locked and the MNS are initialized, otherwise \c false.
+ */
+bool Xrm_IsApiFree(CExtendedResourceManager *self)
+{
+ return (self->lock_api == false);
+}
+
+/*! \brief Locks/Unlocks the XRM API.
+ * \param self Instance pointer
+ * \param status Locking status. \c true = Lock, \c false = Unlock
+ */
+void Xrm_ApiLocking(CExtendedResourceManager *self, bool status)
+{
+ self->lock_api = status;
+}
+
+/*! \brief Add observer to be notified if ICM TX message object is available. Store pending events.
+ * \param self Instance pointer
+ * \param event_mask Event to be queued
+ */
+void Xrm_WaitForTxMsgObj(CExtendedResourceManager *self, Srv_Event_t event_mask)
+{
+ Inic_AddObsrvOnTxMsgObjAvail(self->inic_ptr, &self->obs.tx_msg_obj_obs);
+ self->queued_event_mask |= event_mask;
+}
+
+/*! \brief Checks whether the given resource object list is part of the given Job
+ * \param job_ptr Reference to a job list
+ * \param ud_ptr Reference to the user data. Not used !
+ * \return \c true if it's part of my job list, otherwise \c false.
+ */
+bool Xrm_SetNtfForThisJob(void * job_ptr, void * ud_ptr)
+{
+ Xrm_Job_t * job_ptr_ = (Xrm_Job_t *)job_ptr;
+ MISC_UNUSED(ud_ptr);
+
+ if(job_ptr_->valid != false)
+ {
+ job_ptr_->notify = true;
+ }
+
+ return false;
+}
+
+/*! \brief Handle internal errors and un-initialize XRM service.
+ * \param self Instance pointer
+ * \param error_code_ptr Reference to internal error code
+ */
+void Xrm_UninitializeService(void *self, void *error_code_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ MISC_UNUSED(error_code_ptr);
+
+ Xrm_ApiLocking(self_, true);
+
+ MISC_MEM_SET(&self_->report_result, 0x00, sizeof(Ucs_Xrm_Result_t));
+ self_->report_result.code = UCS_XRM_RES_RC_AUTO_DESTROYED;
+
+ (void)Dl_Foreach(&self_->job_list, &Xrm_SetNtfForThisJob, NULL);
+
+ /* Notify destruction of current connections */
+ Xrm_NotifyInvalidJobs(self_);
+ /* Remove XRM service from schedulers list */
+ (void)Scd_RemoveService(&self_->base_ptr->scd, &self_->xrm_srv);
+ /* Remove error/event observers */
+ Eh_DelObsrvInternalEvent(&self_->base_ptr->eh, &self_->obs.internal_error_obs);
+ /* Remove rsm observers */
+ Rsm_DelObserver(self_->rsm_ptr, &self_->obs.rsm_sync_lost_obs);
+}
+
+
+/*! \brief Handle the network status information mask "Availability" and "NodeAddress".
+ * \param self Instance pointer
+ * \param result_ptr Reference to the results
+ */
+void Xrm_MnsNwStatusInfosCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Net_NetworkStatusParam_t *result_ptr_ = (Net_NetworkStatusParam_t *)result_ptr;
+
+ if ((XRM_MASK_NETWORK_AVAILABILITY & result_ptr_->change_mask) == XRM_MASK_NETWORK_AVAILABILITY)
+ {
+ if ((result_ptr_->availability == UCS_NW_NOT_AVAILABLE) &&
+ (self_->IsInRemoteControlMode))
+ {
+ /* Release all resources */
+ Xrm_ReleaseResrcHandles(self_);
+ }
+ }
+}
+
+/*! \brief Whenever this function is called, a message object (ICM or MCM) is available.
+ * \param self Instance pointer
+ * \param result_ptr Not used!
+ */
+void Xrm_MsgObjAvailCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ MISC_UNUSED(result_ptr);
+ Srv_SetEvent(&self_->xrm_srv, self_->queued_event_mask);
+ self_->queued_event_mask = 0U;
+ Inic_DelObsrvOnTxMsgObjAvail(self_->inic_ptr, &self_->obs.tx_msg_obj_obs);
+}
+
+/*! \brief Whenever this function is called, all remote devices have lost the synchronization.
+ * \param self instance pointer
+ * \param result_ptr Not Used !
+ */
+void Xrm_RmtDevSyncLostCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ MISC_UNUSED(result_ptr);
+
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_NOTIFY_AUTO_DEST_RESR);
+}
+
+/*! \brief Processes the XRM job that is specified by the given resource object list.
+ * \param self Instance pointer
+ * \param resource_object_list[] Reference to array of references to INIC resource objects
+ * \param most_network_connection_label MOST network connection label
+ * \param user_arg User argument
+ * \param report_fptr Report function pointer
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_NOT_AVAILABLE | Associated job not found
+ * UCS_RET_ERR_PARAM | Null pointer detected
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Xrm_Process(CExtendedResourceManager *self,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_list[],
+ uint16_t most_network_connection_label,
+ void * user_arg,
+ Ucs_Xrm_ReportCb_t report_fptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_SUCCESS;
+
+ if (self != NULL)
+ {
+ if(Xrm_IsApiFree(self) != false)
+ {
+ if((resource_object_list != NULL) && (report_fptr != NULL))
+ {
+ Xrm_ApiLocking(self, true);
+ self->current_job_ptr = Xrm_GetJob(self, resource_object_list);
+ if(self->current_job_ptr != NULL)
+ {
+ bool job_is_mine = Dl_IsNodeInList(&self->job_list, &self->current_job_ptr->node);
+ if (job_is_mine)
+ {
+ if(self->current_job_ptr->valid == false)
+ {
+ self->current_job_ptr->user_arg = user_arg;
+ self->current_job_ptr->valid = true;
+ self->current_job_ptr->notify = false;
+ self->current_job_ptr->report_fptr = report_fptr;
+ self->current_job_ptr->most_network_connection_label = most_network_connection_label;
+ self->current_job_ptr->resource_object_list_ptr = resource_object_list;
+ self->current_obj_pptr = &self->current_job_ptr->resource_object_list_ptr[0];
+ Xrm_ProcessJob(self);
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_ALREADY_SET;
+ Xrm_ApiLocking(self, false);
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ Xrm_ApiLocking(self, false);
+ }
+ }
+ else
+ {
+ Xrm_ApiLocking(self, false);
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_API_LOCKED;
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Destroys all resources that are specified by the given resource object list.
+ * \details This function triggers the destruction of all resources which are used by the given
+ * job. A resource will be destroyed only if it is not used by other valid resources.
+ * \param self Instance pointer
+ * \param resource_object_list[] Reference to array of references to INIC resource objects
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------- | ------------------------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_ALREADY_SET | Connection is already destroyed
+ * UCS_RET_ERR_NOT_AVAILABLE | Associated job not found
+ * UCS_RET_ERR_PARAM | Null pointer detected
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Xrm_Destroy(CExtendedResourceManager *self,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_list[])
+{
+ Ucs_Return_t ret_val = UCS_RET_SUCCESS;
+
+ if (self != NULL)
+ {
+ if(Xrm_IsApiFree(self) != false)
+ {
+ if(resource_object_list != NULL)
+ {
+ Xrm_ApiLocking(self, true);
+ self->current_job_ptr = Xrm_GetJob(self, resource_object_list);
+ if((self->current_job_ptr != NULL) &&
+ (self->current_job_ptr->resource_object_list_ptr != NULL))
+ {
+ Xrm_PreJobDestrResult_t result;
+
+ result = Xrm_PrepareJobDestruction(self);
+ if(result == XRM_PRE_JOB_DEST_TASKS_EXIST)
+ {
+ Xrm_DestroyResources(self, &Xrm_DestroyJobResourcesResultCb);
+ }
+ else if(result == XRM_PRE_JOB_DEST_DONE)
+ {
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_NOTIFY_DESTROYED_JOB);
+ }
+ else if (result == XRM_PRE_JOB_DEST_BUSY)
+ {
+ Xrm_ApiLocking(self, false);
+ ret_val = UCS_RET_ERR_API_LOCKED;
+ }
+ else
+ {
+ Xrm_ApiLocking(self, false);
+ ret_val = UCS_RET_ERR_ALREADY_SET;
+ }
+ }
+ else
+ {
+ Xrm_ApiLocking(self, false);
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_API_LOCKED;
+ }
+ }
+ else
+ {
+ /* This means that there is no instance associated to this job,
+ * what in turn means that the job is not available.
+ */
+ ret_val = UCS_RET_ERR_NOT_AVAILABLE;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Prepares the destruction of INIC resources of the current job.
+ * \param self Instance pointer
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------------- | ------------------------------------
+ * XRM_PRE_JOB_DEST_TASKS_EXIST | There are resources to destroy
+ * XRM_PRE_JOB_DEST_NO_TASKS_EXIST | All resources already destroyed
+ * XRM_PRE_JOB_DEST_DONE | Only shared resources affected. Invoke result callback immediately
+ * XRM_PRE_JOB_DEST_BUSY | Preparation of JobDestruction is currently not possible. Other resources are currently being destroyed
+ */
+Xrm_PreJobDestrResult_t Xrm_PrepareJobDestruction(CExtendedResourceManager *self)
+{
+ Xrm_PreJobDestrResult_t ret_val = XRM_PRE_JOB_DEST_BUSY;
+ if (self->inv_resource_handle_list_size == 0U)
+ {
+ ret_val = Xrm_UnsafePrepareJobDestruction(self);
+ }
+ return ret_val;
+}
+
+/*! \brief Prepares precariously the destruction of INIC resources of the current job (This was legacy code and is unsafe).
+ * \param self Instance pointer
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------------- | ------------------------------------
+ * XRM_PRE_JOB_DEST_TASKS_EXIST | There are resources to destroy
+ * XRM_PRE_JOB_DEST_NO_TASKS_EXIST | All resources already destroyed
+ * XRM_PRE_JOB_DEST_DONE | Only shared resources affected. Invoke result callback immediately
+ */
+Xrm_PreJobDestrResult_t Xrm_UnsafePrepareJobDestruction(CExtendedResourceManager *self)
+{
+ uint8_t i;
+ uint16_t resource_handle;
+ Xrm_PreJobDestrResult_t ret_val = XRM_PRE_JOB_DEST_NO_TASKS_EXIST;
+ self->inv_resource_handle_index = 0U;
+ self->inv_resource_handle_list_size = 0U;
+ for(i=Xrm_CountResourceObjects(self, self->current_job_ptr); (i>0U) && (self->inv_resource_handle_list_size < XRM_NUM_RES_HDL_PER_ICM); i--)
+ {
+ uint8_t count = Xrm_CountResourceHandleEntries(self, self->current_job_ptr->resource_object_list_ptr[i - 1U]);
+ if(count == 1U)
+ {
+ resource_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, self->current_job_ptr->resource_object_list_ptr[i - 1U], NULL);
+ if(resource_handle != XRM_INVALID_RESOURCE_HANDLE)
+ {
+ self->inv_resource_handle_list[self->inv_resource_handle_list_size] = resource_handle;
+ self->inv_resource_handle_list_size++;
+ ret_val = XRM_PRE_JOB_DEST_TASKS_EXIST;
+ }
+ }
+ else if(count > 0U)
+ {
+ Xrm_ReleaseResourceHandle(self, self->current_job_ptr, self->current_job_ptr->resource_object_list_ptr[i - 1U]);
+ ret_val = (ret_val == XRM_PRE_JOB_DEST_NO_TASKS_EXIST) ? XRM_PRE_JOB_DEST_DONE : ret_val;
+ }
+ }
+ return ret_val;
+}
+
+
+/*! \brief Resumes the destruction of all resources of the current job.
+ * \param self Instance pointer
+ */
+void Xrm_ResumeJobDestruction(CExtendedResourceManager *self)
+{
+ if(Xrm_UnsafePrepareJobDestruction(self) == XRM_PRE_JOB_DEST_TASKS_EXIST)
+ {
+ Xrm_DestroyResources(self, &Xrm_DestroyJobResourcesResultCb);
+ }
+ else
+ {
+ MISC_MEM_SET(&self->report_result, 0x00, sizeof(Ucs_Xrm_Result_t));
+ self->report_result.code = UCS_XRM_RES_SUCCESS_DESTROY;
+ Xrm_NotifyInvalidJobs(self);
+ Xrm_ApiLocking(self, false);
+ }
+}
+
+/*! \brief Returns the number of resource objects for the job that is identified by the given job
+ * reference.
+ * \param self Instance pointer
+ * \param job_ptr Reference to job
+ * \return Number of INIC resource objects of the desired job
+ */
+uint8_t Xrm_CountResourceObjects(CExtendedResourceManager *self, Xrm_Job_t *job_ptr)
+{
+ uint8_t num_resource_objects = 0U;
+ MISC_UNUSED(self);
+ while(job_ptr->resource_object_list_ptr[num_resource_objects] != NULL)
+ {
+ num_resource_objects++;
+ }
+
+ return num_resource_objects;
+}
+
+/*! \brief Returns the reference of the job that is identified by the given resource object list.
+ * \param self Instance pointer
+ * \param resource_object_list[] Reference to array of references to INIC resource objects
+ * \return Reference to the desired job if the job was found, otherwise NULL.
+ */
+Xrm_Job_t * Xrm_GetJob(CExtendedResourceManager *self,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_list[])
+{
+ Xrm_Job_t *ret_ptr = NULL;
+
+ ret_ptr = Xrmp_GetJob(self->xrmp_ptr, resource_object_list);
+ if (ret_ptr != NULL)
+ {
+ if ((!Dl_IsNodeInList(&self->job_list, &ret_ptr->node)) &&
+ (!Dln_IsNodePartOfAList(&ret_ptr->node)))
+ {
+ Dln_SetData(&ret_ptr->node, ret_ptr);
+ Dl_InsertTail(&self->job_list, &ret_ptr->node);
+ }
+ }
+
+ return ret_ptr;
+}
+
+/*! \brief Checks whether the given resource object list is part of the given Job
+ * \param job_ptr Reference to a job list
+ * \param resrc_obj_ptr Reference to array of references to INIC resource objects
+ * \return \c true if it's part of my job list, otherwise \c false.
+ */
+bool Xrm_IsPartOfJobList (void * job_ptr, void * resrc_obj_ptr)
+{
+ Xrm_Job_t *job_ptr_ = (Xrm_Job_t *)job_ptr;
+ bool ret_val = false;
+
+ if(job_ptr_->resource_object_list_ptr == (UCS_XRM_CONST Ucs_Xrm_ResObject_t **)resrc_obj_ptr)
+ {
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Checks whether the given resource object list is part of my Job list
+ * \param self Instance pointer
+ * \param resource_object_list[] Reference to array of references to INIC resource objects
+ * \return \c true if it's part of my job list, otherwise \c false.
+ */
+bool Xrm_IsInMyJobList(CExtendedResourceManager *self, UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_list[])
+{
+ return (NULL != Dl_Foreach(&self->job_list, &Xrm_IsPartOfJobList, (void *)resource_object_list));
+}
+
+/*! \brief Returns the table index of the given resource object.
+ * \param self Instance pointer
+ * \param job_ptr Reference to job
+ * \param obj_pptr Reference to array of references to INIC resource objects
+ * \return Table index of the given resource object. If entry is not found 0xFF is returned.
+ */
+uint8_t Xrm_GetResourceObjectIndex(CExtendedResourceManager *self,
+ Xrm_Job_t *job_ptr,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t **obj_pptr)
+{
+ return Xrmp_GetResourceHandleIdx(self->xrmp_ptr, job_ptr, obj_pptr);
+}
+
+/*! \brief Check if the current device is already attached respectively sync'ed.
+ * \param self Instance pointer
+ * \return \c true if no error occurred, otherwise \c false.
+ */
+bool Xrm_IsCurrDeviceAlreadyAttached(CExtendedResourceManager *self)
+{
+ bool ret_val = true;
+
+ if (Rsm_GetDevState(self->rsm_ptr) == RSM_DEV_UNSYNCED)
+ {
+ ret_val = false;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Check if the current device is already attached respectively sync'ed.
+ * \param self XRM Instance pointer
+ * \param job_ptr Reference to the XRM job to be looked for
+ * \return \c true if the given job is part of my jobs_list, otherwise \c false.
+ */
+bool Xrm_IsInMyJobsList (void * self, void * job_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Xrm_Job_t *job_ptr_ = (Xrm_Job_t *)job_ptr;
+ bool ret_val = false;
+
+ if ((self_ != NULL) && (job_ptr_ != NULL) &&
+ (Dl_IsNodeInList(&self_->job_list, &job_ptr_->node)))
+ {
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Search for the next resource object to process.
+ * \param self Instance pointer
+ * \return \c true if no error occurred, otherwise \c false.
+ */
+bool Xrm_SearchNextResourceObject(CExtendedResourceManager *self)
+{
+ uint16_t tmp_resource_handle;
+ bool ret_val = true;
+
+ while(*self->current_obj_pptr != NULL)
+ {
+ if(Xrm_IsDefaultCreatedPort(self, *self->current_obj_pptr) != false)
+ {
+ self->current_obj_pptr++;
+ }
+ else
+ {
+ tmp_resource_handle = Xrm_GetResourceHandle(self, NULL, *self->current_obj_pptr, &Xrm_IsInMyJobsList);
+ if(tmp_resource_handle == XRM_INVALID_RESOURCE_HANDLE)
+ {
+ break;
+ }
+ else
+ {
+ if(Xrm_GetResourceHandle(self, self->current_job_ptr, *self->current_obj_pptr, NULL) == XRM_INVALID_RESOURCE_HANDLE)
+ {
+ if(Xrm_StoreResourceHandle(self, tmp_resource_handle, self->current_job_ptr, *self->current_obj_pptr) == false)
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_CONFIG;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Misconfiguration. Resource handle list is too small.", 0U));
+ ret_val = false;
+ }
+ }
+ self->current_obj_pptr++;
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Process the next INIC resource object in the resource object list of the current job.
+ * \param self Instance pointer
+ */
+void Xrm_ProcessJob(CExtendedResourceManager *self)
+{
+ if(Xrm_SearchNextResourceObject(self) != false)
+ {
+ if(*self->current_obj_pptr != NULL)
+ {
+ if (Xrm_IsCurrDeviceAlreadyAttached(self) == false)
+ {
+ (void)Xrm_RemoteDeviceAttach(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ switch(*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr))
+ {
+ case UCS_XRM_RC_TYPE_MOST_SOCKET:
+ Xrm_CreateMostSocket(self);
+ break;
+ case UCS_XRM_RC_TYPE_MLB_PORT:
+ Xrm_CreateMlbPort(self);
+ break;
+ case UCS_XRM_RC_TYPE_MLB_SOCKET:
+ Xrm_CreateMlbSocket(self);
+ break;
+ case UCS_XRM_RC_TYPE_USB_PORT:
+ Xrm_CreateUsbPort(self);
+ break;
+ case UCS_XRM_RC_TYPE_USB_SOCKET:
+ Xrm_CreateUsbSocket(self);
+ break;
+ case UCS_XRM_RC_TYPE_RMCK_PORT:
+ Xrm_CreateRmckPort(self);
+ break;
+ case UCS_XRM_RC_TYPE_STRM_PORT:
+ Xrm_CreateStreamPort(self);
+ break;
+ case UCS_XRM_RC_TYPE_STRM_SOCKET:
+ Xrm_CreateStreamSocket(self);
+ break;
+ case UCS_XRM_RC_TYPE_SYNC_CON:
+ Xrm_CreateSyncCon(self);
+ break;
+ case UCS_XRM_RC_TYPE_DFIPHASE_CON:
+ Xrm_CreateDfiPhaseCon(self);
+ break;
+ case UCS_XRM_RC_TYPE_COMBINER:
+ Xrm_CreateCombiner(self);
+ break;
+ case UCS_XRM_RC_TYPE_SPLITTER:
+ Xrm_CreateSplitter(self);
+ break;
+ case UCS_XRM_RC_TYPE_AVP_CON:
+ Xrm_CreateAvpCon(self);
+ break;
+ case UCS_XRM_RC_TYPE_QOS_CON:
+ Xrm_CreateQoSCon(self);
+ break;
+ default:
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Unexpected Resource Type: 0x%02X", 1U, *(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr)));
+ self->report_result.code = UCS_XRM_RES_ERR_CONFIG;
+ Xrm_HandleError(self);
+ break;
+ }
+ }
+ }
+ else
+ {
+ Xrm_FinishJob(self);
+ }
+ }
+}
+
+/*! \brief Checks if the given resource object is from type "Default Created Port".
+ * \param self Instance pointer
+ * \param resource_object_ptr Reference to the resource object
+ * \return Returns \c true if resource object is from type "Default Created Port", otherwise \c false.
+ */
+bool Xrm_IsDefaultCreatedPort(CExtendedResourceManager *self, UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_ptr)
+{
+ MISC_UNUSED(self);
+ return (*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(resource_object_ptr) == UCS_XRM_RC_TYPE_DC_PORT);
+}
+
+/*! \brief Stores the given resource handle in the resource handle list.
+ * \param self Instance pointer
+ * \param resource_handle Resource handle to save
+ * \param job_ptr Reference to job
+ * \param resource_object_ptr Reference to resource object
+ * \return \c true if free slot in handle list was found, otherwise \c false
+ */
+bool Xrm_StoreResourceHandle(CExtendedResourceManager *self,
+ uint16_t resource_handle,
+ Xrm_Job_t *job_ptr,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_ptr)
+{
+ return Xrmp_StoreResourceHandle(self->xrmp_ptr, resource_handle, job_ptr, resource_object_ptr);
+}
+
+/*! \brief Retrieves the resource handle identified by the given job reference and the given
+ * resource object reference.
+ * \param self Instance pointer
+ * \param job_ptr Reference to the job. Use NULL as wildcard.
+ * \param resource_object_ptr Reference to the resource object
+ * \param func_ptr Reference to a function that checks if found jobs by XRMP belongs to our own job list
+ * \return Resource handle if handle was found, otherwise XRM_INVALID_RESOURCE_HANDLE.
+ */
+uint16_t Xrm_GetResourceHandle(CExtendedResourceManager *self,
+ Xrm_Job_t *job_ptr,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_ptr, Xrmp_CheckJobListFunc_t func_ptr)
+{
+ return Xrmp_GetResourceHandle(self->xrmp_ptr, job_ptr, resource_object_ptr, func_ptr, self);
+}
+
+/*! \brief Checks for the resource handle in the given resource handle list and counts It if found.
+ * \param resrc_ptr Reference to the resource handle list to be looked for.
+ * \param job_ptr Reference to the job list to be looked for.
+ * \param param_ptr Reference to the user parameter.
+ * \param user_arg Reference to the user argument.
+ * \return \c false to continue the for-each-loop of the resources list queue.
+ */
+bool Xrm_IncrResHandleEntryCnt (void *resrc_ptr, void *job_ptr, void *param_ptr, void * user_arg)
+{
+ Xrm_ResourceHandleListItem_t * resrc_ptr_ = (Xrm_ResourceHandleListItem_t *)resrc_ptr;
+ Xrm_Job_t * job_ptr_ = (Xrm_Job_t *)job_ptr;
+ Xrm_CntEntriesResHandle_t * param_ptr_ = (Xrm_CntEntriesResHandle_t *)param_ptr;
+ MISC_UNUSED(user_arg);
+
+ if((resrc_ptr_->resource_handle != XRM_INVALID_RESOURCE_HANDLE) &&
+ (resrc_ptr_->job_ptr == job_ptr_) &&
+ (resrc_ptr_->resource_object_ptr == param_ptr_->resource_object_ptr))
+ {
+ (*param_ptr_->cnt_res)++;
+ }
+
+ return false;
+}
+
+/*! \brief Finds the resource handle to be counted in my job list and pass it to the record callback function .
+ * \param job_ptr Reference to the job to be looked for.
+ * \param param_ptr Reference to the user parameter.
+ * \return \c false to continue the for-each-loop of the job_list queue
+ */
+bool Xrm_CntResHandleEntries(void * job_ptr, void * param_ptr)
+{
+ Xrm_CntEntriesResHandle_t * param_ptr_ = (Xrm_CntEntriesResHandle_t *)param_ptr;
+
+ Xrmp_Foreach(param_ptr_->xrm_inst->xrmp_ptr, &Xrm_IncrResHandleEntryCnt, job_ptr, param_ptr_, NULL);
+
+ return false;
+}
+
+/*! \brief Retrieves the number of list entries that uses the given resource handle.
+ * \param self Instance pointer
+ * \param resource_object_ptr Reference to the current resource object
+ * \return Number of list entries
+ */
+uint8_t Xrm_CountResourceHandleEntries(CExtendedResourceManager *self,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_ptr)
+{
+ uint8_t ret_val = 0U;
+ Xrm_CntEntriesResHandle_t cnt_entry_param;
+ cnt_entry_param.xrm_inst = self;
+ cnt_entry_param.cnt_res = &ret_val;
+ cnt_entry_param.resource_object_ptr = resource_object_ptr;
+
+ (void)Dl_Foreach(&self->job_list, &Xrm_CntResHandleEntries, &cnt_entry_param);
+
+ return ret_val;
+}
+
+/*! \brief Releases the given resource handle.
+ * \param resrc_ptr Reference to the resource handle list to be looked for.
+ * \param job_ptr Reference to the job list to be looked for.
+ * \param resrc_obj_pptr Reference to the resource object to be looked for.
+ * \param user_arg Reference to the user argument
+ * \return \c true to stop the foreach loop when the resource handle has been found, otherwise \c false
+ */
+bool Xrm_ReleaseResrcHandle(void *resrc_ptr, void *job_ptr, void *resrc_obj_pptr, void * user_arg)
+{
+ bool ret_val = false;
+ Xrm_ResourceHandleListItem_t * resrc_ptr_ = (Xrm_ResourceHandleListItem_t *)resrc_ptr;
+ Xrm_Job_t * job_ptr_ = (Xrm_Job_t *)job_ptr;
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resrc_obj_ptr_ = *(UCS_XRM_CONST Ucs_Xrm_ResObject_t **)resrc_obj_pptr;
+ MISC_UNUSED(user_arg);
+
+ if((resrc_ptr_->job_ptr == job_ptr_) &&
+ (resrc_ptr_->resource_object_ptr == resrc_obj_ptr_))
+ {
+ resrc_ptr_->resource_handle = XRM_INVALID_RESOURCE_HANDLE;
+ resrc_ptr_->job_ptr = NULL;
+ resrc_ptr_->resource_object_ptr = NULL;
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Releases the given resource handle. Frees the corresponding table row.
+ * \param self Instance pointer
+ * \param job_ptr Reference to the job
+ * \param resource_object_ptr Reference to the resource object
+ */
+void Xrm_ReleaseResourceHandle(CExtendedResourceManager *self,
+ Xrm_Job_t *job_ptr,
+ UCS_XRM_CONST Ucs_Xrm_ResObject_t *resource_object_ptr)
+{
+ void * resource_object_pptr = (void *)&resource_object_ptr;
+ Xrmp_Foreach(self->xrmp_ptr, &Xrm_ReleaseResrcHandle, job_ptr, resource_object_pptr, NULL);
+}
+
+/*! \brief Releases the given resource and sets the notification to \c true.
+ * \param resrc_ptr Reference to the resource handle list to be looked for.
+ * \param resrc_handle Reference to the resource handle to be found.
+ * \param job_ptr Reference to the job to be looked for.
+ * \param user_arg Reference to a user argument.
+ * \return \c false to continue the for-each-loop of the resources list table
+ */
+bool Xrm_FreeResrcHandleAndNtf(void *resrc_ptr, void *resrc_handle, void *job_ptr, void * user_arg)
+{
+ Xrm_ResourceHandleListItem_t * resrc_ptr_ = (Xrm_ResourceHandleListItem_t *)resrc_ptr;
+ uint16_t * resrc_handle_ = (uint16_t *)resrc_handle;
+ Xrm_Job_t * job_ptr_ = (Xrm_Job_t *)job_ptr;
+ CExtendedResourceManager *self = (CExtendedResourceManager *) user_arg;
+
+ if((resrc_ptr_->resource_handle == *resrc_handle_) &&
+ (*resrc_handle_ != XRM_INVALID_RESOURCE_HANDLE) &&
+ ((resrc_ptr_->job_ptr == job_ptr_) ||
+ (Dl_IsNodeInList(&self->job_list, &resrc_ptr_->job_ptr->node))))
+ {
+ resrc_ptr_->job_ptr->notify = true;
+ resrc_ptr_->job_ptr->valid = false;
+ resrc_ptr_->resource_handle = XRM_INVALID_RESOURCE_HANDLE;
+ resrc_ptr_->job_ptr = NULL;
+
+ if (self->res_debugging_fptr != NULL)
+ {
+ self->res_debugging_fptr(*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(resrc_ptr_->resource_object_ptr),
+ resrc_ptr_->resource_object_ptr, UCS_XRM_INFOS_DESTROYED, self->current_job_ptr->user_arg, self->base_ptr->ucs_user_ptr);
+ }
+
+ resrc_ptr_->resource_object_ptr = NULL;
+ }
+
+ return false;
+}
+
+/*! \brief Releases all given resource handles. Frees the corresponding table rows. Marks the
+ * corresponding job(s) as invalid and sets the notification flag.
+ * \param self Instance pointer
+ * \param job_ptr Reference to the job. Use NULL as wildcard.
+ * \param resource_handle_list Resource handle list
+ * \param resource_handle_list_size Size of list resource_handle_list[]
+ * \param failed_resource_handle This parameter can be used to specify where the release
+ * process has to be stopped. All resource handles prior to
+ * the failed handle are released. If this feature is not
+ * used \c failed_resource_handle must be set to
+ * \ref XRM_INVALID_RESOURCE_HANDLE.
+ * \return the index of the resource where the release process has stopped.
+ */
+uint8_t Xrm_ReleaseResourceHandles(CExtendedResourceManager *self,
+ Xrm_Job_t *job_ptr,
+ uint16_t resource_handle_list[],
+ uint8_t resource_handle_list_size,
+ uint16_t failed_resource_handle)
+{
+ uint8_t i;
+
+ for(i=0U; i<resource_handle_list_size; i++)
+ {
+ if((failed_resource_handle != XRM_INVALID_RESOURCE_HANDLE) &&
+ (resource_handle_list[i] == failed_resource_handle))
+ {
+ break;
+ }
+
+ Xrmp_Foreach(self->xrmp_ptr, &Xrm_FreeResrcHandleAndNtf, &resource_handle_list[i], job_ptr, self);
+ }
+
+ return i;
+}
+
+/*! \brief Releases all resource handles created on remote devices. Frees the corresponding table rows. Marks the
+ * corresponding job(s) as invalid and sets the notification flag.
+ * \param self Instance pointer
+ */
+void Xrm_ReleaseResrcHandles(CExtendedResourceManager *self)
+{
+ if(Xrm_IsApiFree(self) != false)
+ {
+ Xrm_ApiLocking(self, true);
+
+ Xrm_MarkResrcAndJobsAsInvalid(self);
+ Xrm_NotifyInvalidJobs(self);
+ Xrm_ApiLocking(self, false);
+ }
+ else
+ {
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_NOTIFY_AUTO_DEST_RESR);
+ }
+}
+
+/*! \brief Handles and reports Extended Resource Manager errors.
+ * \param self Instance pointer
+ */
+void Xrm_HandleError(CExtendedResourceManager *self)
+{
+ self->current_job_ptr->valid = false;
+ self->current_job_ptr->notify = false;
+ self->current_job_ptr->report_fptr(Inic_GetTargetAddress(self->inic_ptr), XRM_INVALID_CONNECTION_LABEL, self->report_result, self->current_job_ptr->user_arg);
+ Xrm_ApiLocking(self, false);
+}
+
+/*! \brief Reports result of automatically destroyed resources
+ * \param self Instance pointer
+ */
+void Xrm_ReportAutoDestructionResult(CExtendedResourceManager *self)
+{
+ MISC_MEM_SET(&self->report_result, 0x00, sizeof(Ucs_Xrm_Result_t));
+ self->report_result.code = UCS_XRM_RES_RC_AUTO_DESTROYED;
+ Xrm_NotifyInvalidJobs(self);
+ Xrm_ApiLocking(self, false);
+}
+
+/*! \brief Reports result of resource destruction for a specific XRM job
+ * \param self Instance pointer
+ */
+void Xrm_ReportJobDestructionResult(CExtendedResourceManager *self)
+{
+ MISC_MEM_SET(&self->report_result, 0x00, sizeof(Ucs_Xrm_Result_t));
+ self->report_result.code = UCS_XRM_RES_SUCCESS_DESTROY;
+ self->current_job_ptr->notify = true;
+ Xrm_NotifyInvalidJobs(self);
+ Xrm_ApiLocking(self, false);
+}
+
+/*! \brief Reports the conclusion of Extended Resource Manager jobs.
+ * \param self Instance pointer
+ */
+void Xrm_FinishJob(CExtendedResourceManager *self)
+{
+ MISC_MEM_SET(&self->report_result, 0x00, sizeof(Ucs_Xrm_Result_t));
+ self->report_result.code = UCS_XRM_RES_SUCCESS_BUILD;
+ self->current_job_ptr->report_fptr(Inic_GetTargetAddress(self->inic_ptr), self->current_job_ptr->connection_label, self->report_result, self->current_job_ptr->user_arg);
+ Xrm_ApiLocking(self, false);
+}
+
+/*! \brief Marks the given resource as invalid and sets the notification.
+ * \param resrc_ptr Reference to the resource handle list to be looked for.
+ * \param xrm_inst Reference to the XRM instance to be looked for.
+ * \param ud_ptr2 Optional reference to the user data 2. Not used !
+ * \param ud_ptr3 Optional reference to the user data 3. Not used !
+ * \return \c false to continue the for-each-loop of the job_list queue
+ */
+bool Xrm_MarkThisResrcAsInvalid (void *resrc_ptr, void * xrm_inst, void *ud_ptr2, void *ud_ptr3)
+{
+ Xrm_ResourceHandleListItem_t * resrc_ptr_ = (Xrm_ResourceHandleListItem_t *)resrc_ptr;
+ CExtendedResourceManager * xrm_inst_ = (CExtendedResourceManager *)xrm_inst;
+ MISC_UNUSED(ud_ptr2);
+ MISC_UNUSED(ud_ptr3);
+
+ if (Dl_IsNodeInList(&xrm_inst_->job_list, &resrc_ptr_->job_ptr->node))
+ {
+ if (resrc_ptr_->job_ptr->valid == true)
+ {
+ resrc_ptr_->job_ptr->valid = false;
+ resrc_ptr_->job_ptr->notify = true;
+ }
+
+ /* Inform monitor callback function */
+ if (xrm_inst_->res_debugging_fptr != NULL)
+ {
+ xrm_inst_->res_debugging_fptr(*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(resrc_ptr_->resource_object_ptr),
+ resrc_ptr_->resource_object_ptr, UCS_XRM_INFOS_DESTROYED, xrm_inst_->current_job_ptr->user_arg, xrm_inst_->base_ptr->ucs_user_ptr);
+ }
+
+ resrc_ptr_->resource_handle = XRM_INVALID_RESOURCE_HANDLE;
+ resrc_ptr_->job_ptr = NULL;
+ resrc_ptr_->resource_object_ptr = NULL;
+ }
+
+ return false;
+}
+
+/*! \brief Marks all jobs on remote devices as "invalid".
+ * \param self Instance pointer
+ */
+void Xrm_MarkResrcAndJobsAsInvalid (CExtendedResourceManager *self)
+{
+ Xrmp_Foreach(self->xrmp_ptr, &Xrm_MarkThisResrcAsInvalid, self, NULL, NULL);
+
+ self->report_result.code = UCS_XRM_RES_RC_AUTO_DESTROYED;
+}
+
+/*! \brief Calls the result callbacks of jobs that were marked as invalid.
+ * \param job_ptr Reference to the job to be looked for.
+ * \param xrm_inst XRM Instance pointer.
+ * \return \c false to continue the for-each-loop of the job_list queue
+ */
+bool Xrm_SetJobAsInvalid(void * job_ptr, void * xrm_inst)
+{
+ Xrm_Job_t *job_ptr_ = (Xrm_Job_t *)job_ptr;
+ CExtendedResourceManager * xrm_inst_ = (CExtendedResourceManager *)xrm_inst;
+
+ if(job_ptr_->notify != false)
+ {
+ job_ptr_->report_fptr(Inic_GetTargetAddress(xrm_inst_->inic_ptr), job_ptr_->connection_label, xrm_inst_->report_result, job_ptr_->user_arg);
+ job_ptr_->notify = false;
+ }
+
+ return false;
+}
+
+/*! \brief Calls the result callbacks of jobs that were marked as invalid.
+ * \param self Instance pointer
+ */
+void Xrm_NotifyInvalidJobs(CExtendedResourceManager *self)
+{
+ (void)Dl_Foreach(&self->job_list, &Xrm_SetJobAsInvalid, self);
+}
+
+/*! \brief Sets the monitoring callback for XRM resources.
+ * \param self Reference to the XRM Instance to be looked for.
+ * \param dbg_cb_fn Debug callback function to set.
+ */
+void Xrm_SetResourceDebugCbFn(CExtendedResourceManager *self, Ucs_Xrm_ResourceDebugCb_t dbg_cb_fn)
+{
+ if ((self != NULL) && (dbg_cb_fn != NULL))
+ {
+ self->res_debugging_fptr = dbg_cb_fn;
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_xrm_res.c b/ucs2-lib/src/ucs_xrm_res.c
new file mode 100644
index 0000000..900671d
--- /dev/null
+++ b/ucs2-lib/src/ucs_xrm_res.c
@@ -0,0 +1,1443 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Extended Resource Manager. This file contains the implementation of
+ * the INIC Resource Management functions and result/error handlers.
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_UCS_XRM_INT
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_xrm.h"
+#include "ucs_xrm_pv.h"
+#include "ucs_xrm_cfg.h"
+#include "ucs_misc.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static uint16_t Xrm_CreatePortHandle(CExtendedResourceManager *self,
+ Ucs_Xrm_PortType_t port_type,
+ uint8_t index);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CExtendedResourceManager (Handling of resource objects) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Creates the corresponding INIC port handle depending on the given port type and the
+ * given port instance id.
+ * \param self Instance pointer
+ * \param port_type Type of the port
+ * \param index Port instance id
+ * \return Returns the created INIC port handle.
+ */
+static uint16_t Xrm_CreatePortHandle(CExtendedResourceManager *self,
+ Ucs_Xrm_PortType_t port_type,
+ uint8_t index)
+{
+ MISC_UNUSED(self);
+ return ((uint16_t)((uint16_t)port_type << 8) | (uint16_t)index);
+}
+
+/*! \brief Activates remote synchronization on the current device
+ * \param self Instance pointer
+ * \param next_set_event Next event to set once the remote synchronization succeeded
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * ------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_BUFFER_OVERFLOW | no message buffer available
+ */
+extern Ucs_Return_t Xrm_RemoteDeviceAttach (CExtendedResourceManager *self, Srv_Event_t next_set_event)
+{
+ Ucs_Return_t result;
+
+ result = Rsm_SyncDev(self->rsm_ptr, self, &Xrm_RmtDevAttachResultCb);
+
+ if(result == UCS_RET_SUCCESS)
+ {
+ self->queued_event_mask |= next_set_event;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start Synchronization of remote device", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, next_set_event);
+ }
+
+ return result;
+}
+
+/*! \brief Triggers the creation of a MOST socket.
+ * \param self Instance pointer
+ */
+void Xrm_CreateMostSocket(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_MostSocket_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_MostSocket_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t con_label = (cfg_ptr->direction == UCS_SOCKET_DIR_INPUT) ? self->current_job_ptr->most_network_connection_label : 0xFFFFU;
+ Ucs_Return_t result = Inic_MostSocketCreate(self->inic_ptr,
+ cfg_ptr->most_port_handle,
+ cfg_ptr->direction,
+ cfg_ptr->data_type,
+ cfg_ptr->bandwidth,
+ con_label,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating MOST socket", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_MOST_SOCKET;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating MOST socket failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of the MediaLB port.
+ * \param self Instance pointer
+ */
+void Xrm_CreateMlbPort(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_MlbPort_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_MlbPort_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ Ucs_Return_t result = Inic_MlbPortCreate(self->inic_ptr,
+ cfg_ptr->index,
+ cfg_ptr->clock_config,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating MediaLB port", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_MLB_PORT;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating MediaLB port failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a MediaLB socket.
+ * \param self Instance pointer
+ */
+void Xrm_CreateMlbSocket(CExtendedResourceManager *self)
+{
+ Ucs_Return_t result;
+ uint16_t mlb_port_handle;
+ UCS_XRM_CONST Ucs_Xrm_MlbSocket_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_MlbSocket_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ if(Xrm_IsDefaultCreatedPort(self, cfg_ptr->mlb_port_obj_ptr) != false)
+ {
+ mlb_port_handle = Xrm_CreatePortHandle(self,
+ UCS_XRM_PORT_TYPE_MLB,
+ ((UCS_XRM_CONST Ucs_Xrm_DefaultCreatedPort_t *)(UCS_XRM_CONST void*)(cfg_ptr->mlb_port_obj_ptr))->index);
+ }
+ else
+ {
+ mlb_port_handle= Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->mlb_port_obj_ptr, NULL);
+ }
+ result = Inic_MlbSocketCreate(self->inic_ptr,
+ mlb_port_handle,
+ cfg_ptr->direction,
+ cfg_ptr->data_type,
+ cfg_ptr->bandwidth,
+ cfg_ptr->channel_address,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating MediaLB socket", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_MLB_SOCKET;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating MediaLB socket failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of the USB port.
+ * \param self Instance pointer
+ */
+void Xrm_CreateUsbPort(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_UsbPort_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_UsbPort_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ Ucs_Return_t result = Inic_UsbPortCreate(self->inic_ptr,
+ cfg_ptr->index,
+ cfg_ptr->physical_layer,
+ cfg_ptr->devices_interfaces,
+ cfg_ptr->streaming_if_ep_out_count,
+ cfg_ptr->streaming_if_ep_in_count,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating USB port", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_USB_PORT;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating USB port failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a USB socket.
+ * \param self Instance pointer
+ */
+void Xrm_CreateUsbSocket(CExtendedResourceManager *self)
+{
+ Ucs_Return_t result;
+ uint16_t usb_port_handle;
+ UCS_XRM_CONST Ucs_Xrm_UsbSocket_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_UsbSocket_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ if(Xrm_IsDefaultCreatedPort(self, cfg_ptr->usb_port_obj_ptr) != false)
+ {
+ usb_port_handle = Xrm_CreatePortHandle(self,
+ UCS_XRM_PORT_TYPE_USB,
+ ((UCS_XRM_CONST Ucs_Xrm_DefaultCreatedPort_t *)(UCS_XRM_CONST void*)(cfg_ptr->usb_port_obj_ptr))->index);
+ }
+ else
+ {
+ usb_port_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->usb_port_obj_ptr, NULL);
+ }
+ result = Inic_UsbSocketCreate(self->inic_ptr,
+ usb_port_handle,
+ cfg_ptr->direction,
+ cfg_ptr->data_type,
+ cfg_ptr->end_point_addr,
+ cfg_ptr->frames_per_transfer,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating USB socket", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_USB_SOCKET;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating USB socket failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of the RMCK port.
+ * \param self Instance pointer
+ */
+void Xrm_CreateRmckPort(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_RmckPort_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_RmckPort_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ Ucs_Return_t result = Inic_RmckPortCreate(self->inic_ptr,
+ cfg_ptr->index,
+ cfg_ptr->clock_source,
+ cfg_ptr->divisor,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating RMCK port", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_RMCK_PORT;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating RMCK port failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a streaming port.
+ * \param self Instance pointer
+ */
+void Xrm_CreateStreamPort(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_StrmPort_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_StrmPort_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ Ucs_Return_t result = Inic_StreamPortCreate(self->inic_ptr,
+ cfg_ptr->index,
+ cfg_ptr->clock_config,
+ cfg_ptr->data_alignment,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating streaming port", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_STRM_PORT;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating streaming port failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a streaming data socket.
+ * \param self Instance pointer
+ */
+void Xrm_CreateStreamSocket(CExtendedResourceManager *self)
+{
+ Ucs_Return_t result;
+ uint16_t stream_port_handle;
+ UCS_XRM_CONST Ucs_Xrm_StrmSocket_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_StrmSocket_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ if(Xrm_IsDefaultCreatedPort(self, cfg_ptr->stream_port_obj_ptr) != false)
+ {
+ stream_port_handle = Xrm_CreatePortHandle(self,
+ UCS_XRM_PORT_TYPE_STRM,
+ ((UCS_XRM_CONST Ucs_Xrm_DefaultCreatedPort_t *)(UCS_XRM_CONST void*)(cfg_ptr->stream_port_obj_ptr))->index);
+ }
+ else
+ {
+ stream_port_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->stream_port_obj_ptr, NULL);
+ }
+ result = Inic_StreamSocketCreate(self->inic_ptr,
+ stream_port_handle,
+ cfg_ptr->direction,
+ cfg_ptr->data_type,
+ cfg_ptr->bandwidth,
+ cfg_ptr->stream_pin_id,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating streaming data socket", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_STRM_SOCKET;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating streaming data socket failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a synchronous data connection.
+ * \param self Instance pointer
+ */
+void Xrm_CreateSyncCon(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_SyncCon_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_SyncCon_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t in_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_in_obj_ptr, NULL);
+ uint16_t out_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_out_obj_ptr, NULL);
+ Ucs_Return_t result = Inic_SyncCreate(self->inic_ptr,
+ in_socket_handle,
+ out_socket_handle,
+ false,
+ cfg_ptr->mute_mode,
+ cfg_ptr->offset,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating synchronous data connection", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_SYNC_CON;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating synchronous data connection failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a DiscreteFrame Isochronous streaming phase connection.
+ * \param self Instance pointer
+ */
+void Xrm_CreateDfiPhaseCon(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_DfiPhaseCon_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_DfiPhaseCon_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t in_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_in_obj_ptr, NULL);
+ uint16_t out_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_out_obj_ptr, NULL);
+ Ucs_Return_t result = Inic_DfiPhaseCreate(self->inic_ptr,
+ in_socket_handle,
+ out_socket_handle,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating DFIPhase connection", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_DFIPHASE_CON;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating DFIPhase connection failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a combiner resource.
+ * \param self Instance pointer
+ */
+void Xrm_CreateCombiner(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_Combiner_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_Combiner_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t port_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->port_socket_obj_ptr, NULL);
+ Ucs_Return_t result = Inic_CombinerCreate(self->inic_ptr,
+ port_socket_handle,
+ cfg_ptr->most_port_handle,
+ cfg_ptr->bytes_per_frame,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating combiner resource", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_COMBINER;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating combiner resource failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a splitter resource.
+ * \param self Instance pointer
+ */
+void Xrm_CreateSplitter(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_Splitter_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_Splitter_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t socket_handle_in = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_in_obj_ptr, NULL);
+ Ucs_Return_t result = Inic_SplitterCreate(self->inic_ptr,
+ socket_handle_in,
+ cfg_ptr->most_port_handle,
+ cfg_ptr->bytes_per_frame,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating splitter resource", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_SPLITTER;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating splitter resource failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a A/V packetized isochronous streaming data connection.
+ * \param self Instance pointer
+ */
+void Xrm_CreateAvpCon(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_AvpCon_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_AvpCon_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t in_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_in_obj_ptr, NULL);
+ uint16_t out_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_out_obj_ptr, NULL);
+ Ucs_Return_t result = Inic_AvpCreate(self->inic_ptr,
+ in_socket_handle,
+ out_socket_handle,
+ cfg_ptr->isoc_packet_size,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating A/V packetized isochronous streaming data connection", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_AVP_CON;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating A/V packetized isochronous streaming data connection failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Triggers the creation of a Quality of Service IP streaming data connection.
+ * \param self Instance pointer
+ */
+void Xrm_CreateQoSCon(CExtendedResourceManager *self)
+{
+ UCS_XRM_CONST Ucs_Xrm_QoSCon_t *cfg_ptr = (UCS_XRM_CONST Ucs_Xrm_QoSCon_t *)(UCS_XRM_CONST void*)(*self->current_obj_pptr);
+ uint16_t in_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_in_obj_ptr, NULL);
+ uint16_t out_socket_handle = Xrm_GetResourceHandle(self, self->current_job_ptr, cfg_ptr->socket_out_obj_ptr, NULL);
+ Ucs_Return_t result = Inic_QoSCreate(self->inic_ptr,
+ in_socket_handle,
+ out_socket_handle,
+ &self->obs.std_result_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating QoS IP streaming data connection", 0U));
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_PROCESS);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self->report_result.details.resource_type = UCS_XRM_RC_TYPE_QOS_CON;
+ self->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self,
+ self->current_job_ptr,
+ self->current_obj_pptr);
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Xrm_HandleError(self);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start creating QoS IP streaming data connection failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Process the result of the INIC resource monitor.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result data. Result must be casted into data type
+ * Inic_StdResult_t.
+ */
+void Xrm_ResourceMonitorCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if((result_ptr_->result.code == UCS_RES_SUCCESS) && (result_ptr_->data_info != NULL))
+ {
+ Ucs_Resource_MonitorState_t state = *((Ucs_Resource_MonitorState_t *)result_ptr_->data_info);
+ if(state == UCS_INIC_RES_MON_STATE_ACT_REQ)
+ {
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_REQ_INV_RES_LST);
+ }
+ else if((state == UCS_INIC_RES_MON_STATE_OK) && (self_->obs.check_unmute_fptr != NULL))
+ {
+ self_->obs.check_unmute_fptr(Inic_GetTargetAddress(self_->inic_ptr), self_->base_ptr->ucs_user_ptr);
+ }
+ }
+}
+
+/*! \brief Retrieves the list of invalid resources.
+ * \param self Instance pointer
+ */
+void Xrm_RequestResourceList(CExtendedResourceManager *self)
+{
+ if(Xrm_IsApiFree(self) != false)
+ {
+ Ucs_Return_t result;
+ result = Inic_ResourceInvalidList_Get(self->inic_ptr,
+ &self->obs.resource_invalid_list_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ Xrm_ApiLocking(self, true);
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_REQ_INV_RES_LST);
+ }
+ else
+ {
+ self->report_result.code = UCS_XRM_RES_ERR_INV_LIST;
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start requesting invalid resources failed. Return value: 0x%02X", 1U, result));
+ }
+ }
+ else
+ {
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_REQ_INV_RES_LST);
+ }
+}
+
+/*! \brief Process the received list of invalid resources.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result data. Result must be casted into data type
+ * Inic_StdResult_t.
+ */
+void Xrm_RequestResourceListResultCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if((result_ptr_->result.code == UCS_RES_SUCCESS) && (result_ptr_->data_info != NULL))
+ {
+ Inic_ResHandleList_t resource_handle_list = *((Inic_ResHandleList_t *)result_ptr_->data_info);
+ if((resource_handle_list.res_handles != NULL) && (resource_handle_list.num_handles > 0U))
+ {
+ MISC_MEM_CPY(&self_->inv_resource_handle_list[0],
+ &resource_handle_list.res_handles[0],
+ (resource_handle_list.num_handles * sizeof(resource_handle_list.res_handles[0])));
+ }
+ self_->inv_resource_handle_list_size = resource_handle_list.num_handles;
+ self_->inv_resource_handle_index = 0U;
+
+ Xrmp_Foreach(self_->xrmp_ptr, &Xrm_SetCurrJobPtr, &resource_handle_list.res_handles[0], NULL, self);
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_DESTROY_INV_RES);
+ }
+ else
+ {
+ self_->report_result.code = UCS_XRM_RES_ERR_INV_LIST;
+ if (result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ self_->report_result.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TX;
+ }
+ else
+ {
+ self_->report_result.details.tx_result = UCS_MSG_STAT_OK;
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TGT;
+ }
+
+ self_->report_result.details.inic_result = result_ptr_->result;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Request of invalid resources failed. Return value: 0x%02X", 1U, result_ptr_->result.code));
+ TR_ERROR_INIC_RESULT(self_->base_ptr->ucs_user_ptr, "[XRM]", result_ptr_->result.info_ptr, result_ptr_->result.info_size);
+ }
+
+ Xrm_ApiLocking(self_, false);
+}
+
+/*! \brief Triggers the destruction of INIC resources.
+ * \param self Instance pointer
+ * \param result_fptr Result callback function pointer
+ */
+void Xrm_DestroyResources(CExtendedResourceManager *self, Sobs_UpdateCb_t result_fptr)
+{
+ Ucs_Return_t result;
+ Inic_ResHandleList_t list;
+ list.res_handles = &self->inv_resource_handle_list[self->inv_resource_handle_index];
+ if (self->inv_resource_handle_list_size > MAX_INVALID_HANDLES_LIST)
+ {
+ list.num_handles = MAX_INVALID_HANDLES_LIST;
+ self->curr_dest_resource_handle_size = list.num_handles;
+ }
+ else
+ {
+ list.num_handles = self->inv_resource_handle_list_size;
+ self->curr_dest_resource_handle_size = list.num_handles;
+ if(self->inv_resource_handle_list[(self->inv_resource_handle_index + self->inv_resource_handle_list_size) - 1U] == XRM_INVALID_RESOURCE_HANDLE)
+ {
+ list.num_handles--;
+ }
+ }
+ Sobs_Ctor(&self->obs.resource_destroy_obs, self, result_fptr);
+ result = Inic_ResourceDestroy(self->inic_ptr,
+ list,
+ &self->obs.resource_destroy_obs);
+ if(result == UCS_RET_SUCCESS)
+ {
+ /* No error */
+#ifdef UCS_TR_INFO
+ uint8_t i;
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "Destruction of invalid resource handles been successfully started:", 0U));
+ for(i=0U; i<list.num_handles; i++)
+ {
+ TR_INFO((self->base_ptr->ucs_user_ptr, "[XRM]", "--> Handle: 0x%04X", 1U, list.res_handles[i]));
+ }
+#endif
+ }
+ else if (result == UCS_RET_ERR_PARAM)
+ {
+ /* empty list */
+ if ((list.num_handles == 0U) && (list.res_handles[0] == XRM_INVALID_RESOURCE_HANDLE))
+ {
+ self->inv_resource_handle_list_size = 0U;
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_RESET_RES_MONITOR);
+ }
+ }
+ else if(result == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_DESTROY_INV_RES);
+ }
+ else
+ {
+ self->inv_resource_handle_list_size = 0U;
+ self->report_result.code = UCS_XRM_RES_ERR_DESTROY;
+ self->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self->report_result.details.int_result = result;
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self->base_ptr->ucs_user_ptr, "[XRM]", "Start destroying invalid resources failed. Return value: 0x%02X", 1U, result));
+ }
+}
+
+/*! \brief Resets the INIC's Resource Monitor.
+ * \param self Instance pointer
+ */
+void Xrm_ResetResourceMonitor(CExtendedResourceManager *self)
+{
+ Ucs_Return_t result = Inic_ResourceMonitor_Set(self->inic_ptr, UCS_INIC_RES_MON_CTRL_RESET);
+ if(result == UCS_RET_SUCCESS)
+ {
+ Srv_SetEvent(&self->xrm_srv, XRM_EVENT_NOTIFY_AUTO_DEST_RES);
+ }
+ else
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_RESET_RES_MONITOR);
+ }
+}
+
+
+/*! \brief Handles the result of resource destructions.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result data. Result must be casted into data type
+ * Inic_StdResult_t.
+ */
+void Xrm_DestroyResourcesResultCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ (void)Xrm_ReleaseResourceHandles(self_,
+ NULL,
+ &self_->inv_resource_handle_list[self_->inv_resource_handle_index],
+ self_->curr_dest_resource_handle_size,
+ XRM_INVALID_RESOURCE_HANDLE);
+
+ if (self_->inv_resource_handle_list_size >= self_->curr_dest_resource_handle_size)
+ {
+ self_->inv_resource_handle_list_size -= self_->curr_dest_resource_handle_size;
+ if (self_->inv_resource_handle_list_size > 0U)
+ {
+ self_->inv_resource_handle_index += self_->curr_dest_resource_handle_size;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_DESTROY_INV_RES);
+ }
+ else
+ {
+ if(self_->inv_resource_handle_list[(self_->inv_resource_handle_index + self_->curr_dest_resource_handle_size) - 1U] != XRM_INVALID_RESOURCE_HANDLE)
+ {
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_REQ_INV_RES_LST);
+ }
+ else
+ {
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_RESET_RES_MONITOR);
+ }
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[XRM]", "INIC resources been successfully destroyed.", 0U));
+ }
+ }
+ else
+ {
+ self_->inv_resource_handle_list_size = 0U;
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self_->report_result.details.tx_result = UCS_MSG_STAT_OK;
+ self_->report_result.code = UCS_XRM_RES_ERR_DESTROY;
+ self_->report_result.details.inic_result = result_ptr_->result;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Destruction of invalid resources failed. Internal resources handles List is corrupted", 0U));
+ }
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_BUSY)
+ {
+ uint8_t stop_index;
+ uint16_t failed_resource_handle;
+ MISC_DECODE_WORD(&(failed_resource_handle), &(result_ptr_->result.info_ptr[3]));
+ stop_index = Xrm_ReleaseResourceHandles(self_,
+ NULL,
+ &self_->inv_resource_handle_list[self_->inv_resource_handle_index],
+ self_->curr_dest_resource_handle_size,
+ failed_resource_handle);
+
+ if (stop_index > 0U)
+ {
+ self_->inv_resource_handle_index = stop_index;
+ self_->inv_resource_handle_list_size -= stop_index;
+ }
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_DESTROY_INV_RES);
+ }
+ else
+ {
+ self_->inv_resource_handle_list_size = 0U;
+ if (result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ self_->report_result.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TX;
+ }
+ else
+ {
+ self_->report_result.details.tx_result = UCS_MSG_STAT_OK;
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TGT;
+ }
+ self_->report_result.code = UCS_XRM_RES_ERR_DESTROY;
+ self_->report_result.details.inic_result = result_ptr_->result;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Destruction of invalid resources failed. Return value: 0x%02X", 1U, result_ptr_->result.code));
+ TR_ERROR_INIC_RESULT(self_->base_ptr->ucs_user_ptr, "[XRM]", result_ptr_->result.info_ptr, result_ptr_->result.info_size);
+ }
+}
+
+/*! \brief Handles the result of resource destructions for all resources of a job.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result data. Result must be casted into data type
+ * Inic_StdResult_t.
+ */
+void Xrm_DestroyJobResourcesResultCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ if(result_ptr_->result.code == UCS_RES_SUCCESS)
+ {
+ (void)Xrm_ReleaseResourceHandles(self_,
+ self_->current_job_ptr,
+ &self_->inv_resource_handle_list[self_->inv_resource_handle_index],
+ self_->curr_dest_resource_handle_size,
+ XRM_INVALID_RESOURCE_HANDLE);
+
+ if (self_->inv_resource_handle_list_size >= self_->curr_dest_resource_handle_size)
+ {
+ self_->inv_resource_handle_list_size -= self_->curr_dest_resource_handle_size;
+ self_->inv_resource_handle_index = 0U;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_RESUME_JOB_DESTRUCT);
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[XRM]", "INIC resources been successfully destroyed.", 0U));
+ }
+ else
+ {
+ self_->inv_resource_handle_list_size = 0U;
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_INT;
+ self_->report_result.details.tx_result = UCS_MSG_STAT_OK;
+ self_->report_result.code = UCS_XRM_RES_ERR_DESTROY;
+ self_->report_result.details.inic_result = result_ptr_->result;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Destruction of invalid resources failed. Internal resources handles List is corrupted", 0U));
+ }
+ }
+ else if(result_ptr_->result.code == UCS_RES_ERR_BUSY)
+ {
+ uint16_t failed_handle;
+ MISC_DECODE_WORD(&(failed_handle), &(result_ptr_->result.info_ptr[3]));
+ (void)Xrm_ReleaseResourceHandles(self_,
+ NULL,
+ &self_->inv_resource_handle_list[self_->inv_resource_handle_index],
+ self_->curr_dest_resource_handle_size,
+ failed_handle);
+
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_RESUME_JOB_DESTRUCT);
+ }
+ else
+ {
+ self_->inv_resource_handle_list_size = 0U;
+ if (result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ self_->report_result.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TX;
+ }
+ else
+ {
+ self_->report_result.details.tx_result = UCS_MSG_STAT_OK;
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TGT;
+ }
+
+ self_->report_result.code = UCS_XRM_RES_ERR_DESTROY;
+ self_->report_result.details.inic_result = result_ptr_->result;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Destruction of invalid resources failed. Return value: 0x%02X", 1U, result_ptr_->result.code));
+ TR_ERROR_INIC_RESULT(self_->base_ptr->ucs_user_ptr, "[XRM]", result_ptr_->result.info_ptr, result_ptr_->result.info_size);
+ }
+}
+
+/*! \brief Handles the result of "create port", "create socket" and "create connection" operations.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result data. Result must be casted into data type
+ * Inic_StdResult_t.
+ */
+void Xrm_StdResultCb(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+
+ if((result_ptr_->result.code == UCS_RES_SUCCESS) && (result_ptr_->data_info != NULL))
+ {
+ uint16_t resource_handle = 0U;
+ if(*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self_->current_obj_pptr) == UCS_XRM_RC_TYPE_MOST_SOCKET)
+ {
+ Inic_MostSocketCreate_Result_t res_ack = {0U, 0U};
+ res_ack = *((Inic_MostSocketCreate_Result_t *)result_ptr_->data_info);
+ resource_handle = res_ack.most_socket_handle;
+ self_->current_job_ptr->connection_label = res_ack.conn_label;
+ }
+ else
+ {
+ resource_handle = *((uint16_t *)result_ptr_->data_info);
+ }
+
+ if(Xrm_StoreResourceHandle(self_, resource_handle, self_->current_job_ptr, *self_->current_obj_pptr) != false)
+ {
+ if (self_->res_debugging_fptr != NULL)
+ {
+ self_->res_debugging_fptr(*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self_->current_obj_pptr), *self_->current_obj_pptr, UCS_XRM_INFOS_BUILT,
+ self_->current_job_ptr->user_arg, self_->base_ptr->ucs_user_ptr);
+ }
+
+ self_->current_obj_pptr++;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_PROCESS);
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[XRM]", "Resource has been successfully created. Handle: 0x%04X", 1U, resource_handle));
+ }
+ else
+ {
+ self_->report_result.code = UCS_XRM_RES_ERR_CONFIG;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Misconfiguration. Resource handle list is too small.", 0U));
+ }
+ }
+ else
+ {
+ self_->current_job_ptr->valid = false;
+
+ if (result_ptr_->result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ self_->report_result.details.tx_result = *(Ucs_MsgTxStatus_t *)(result_ptr_->data_info);
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TX;
+ }
+ else
+ {
+ self_->report_result.details.tx_result = UCS_MSG_STAT_OK;
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TGT;
+ }
+
+ self_->report_result.code = UCS_XRM_RES_ERR_BUILD;
+ self_->report_result.details.resource_type = *(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self_->current_obj_pptr);
+ self_->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self_,
+ self_->current_job_ptr,
+ self_->current_obj_pptr);
+ self_->report_result.details.inic_result = result_ptr_->result;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+
+ if (self_->res_debugging_fptr != NULL)
+ {
+ self_->res_debugging_fptr(*(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self_->current_obj_pptr),
+ *self_->current_obj_pptr, UCS_XRM_INFOS_ERR_BUILT, self_->current_job_ptr->user_arg, self_->base_ptr->ucs_user_ptr);
+ }
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Creation of resource failed. Result code: 0x%02X", 1U, result_ptr_->result.code));
+ if (result_ptr_->result.info_ptr != NULL)
+ {
+ TR_ERROR_INIC_RESULT(self_->base_ptr->ucs_user_ptr, "[XRM]", result_ptr_->result.info_ptr, result_ptr_->result.info_size);
+ }
+ }
+}
+
+/*! \brief Handles the result of "device.sync" operations.
+ * \param self Instance pointer
+ * \param result RSM result
+ */
+void Xrm_RmtDevAttachResultCb(void *self, Rsm_Result_t result)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ if (result.code == RSM_RES_SUCCESS)
+ {
+ Srv_SetEvent(&self_->xrm_srv, self_->queued_event_mask);
+ self_->queued_event_mask = 0U;
+ TR_INFO((self_->base_ptr->ucs_user_ptr, "[XRM]", "Remote device has been successfully synchronized.", 0U));
+ }
+ else
+ {
+ /* In case of StreamingConfig, simulate an error configuration since there
+ * is currently no possibility to signal SyncLost
+ */
+ if ((self_->queued_event_mask == XRM_EVENT_STREAMPORT_CONFIG_SET) ||
+ (self_->queued_event_mask == XRM_EVENT_STREAMPORT_CONFIG_GET))
+ {
+ Inic_StdResult_t sim_inic_res;
+ sim_inic_res.result.code = result.details.inic_result.code;
+ sim_inic_res.result.info_ptr = NULL;
+ sim_inic_res.result.info_size = 0U;
+ sim_inic_res.data_info = NULL;
+
+ self_->queued_event_mask = 0U;
+ /* Force a Notification of the Streaming observer */
+ self_->obs.stream_port_config_fptr = self_->current_streamport_config.result_fptr;
+ self_->obs.stream_port_config_obs.update_fptr(self, &sim_inic_res);
+ }
+ else
+ {
+ if (result.details.inic_result.code == UCS_RES_ERR_TRANSMISSION)
+ {
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TX;
+ }
+ else
+ {
+ self_->report_result.details.result_type = UCS_XRM_RESULT_TYPE_TGT;
+ }
+ self_->report_result.code = UCS_XRM_RES_ERR_SYNC;
+ self_->report_result.details.inic_result.code = result.details.inic_result.code;
+ self_->report_result.details.inic_result.info_ptr = result.details.inic_result.info_ptr;
+ self_->report_result.details.inic_result.info_size = result.details.inic_result.info_size;
+ self_->report_result.details.resource_type = *(UCS_XRM_CONST Ucs_Xrm_ResourceType_t *)(UCS_XRM_CONST void*)(*self_->current_obj_pptr);
+ self_->report_result.details.resource_index = Xrm_GetResourceObjectIndex(self_,
+ self_->current_job_ptr,
+ self_->current_obj_pptr);
+ self_->report_result.details.tx_result = (Ucs_MsgTxStatus_t)result.details.tx_result;
+
+ self_->queued_event_mask = 0U;
+ Srv_SetEvent(&self_->xrm_srv, XRM_EVENT_ERROR);
+ TR_ERROR((self_->base_ptr->ucs_user_ptr, "[XRM]", "Synchronization to the remote device failed. Result code: 0x%02X", 1U, result));
+ }
+ }
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CExtendedResourceManager (INIC Resource Management API) */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief This function is used to configure a Streaming Port.
+ * \param self Instance pointer
+ * \param index Streaming Port instance.
+ * \param op_mode Operation mode of the Streaming Port.
+ * \param port_option Direction of the physical pins of the indexed Streaming Port.
+ * \param clock_mode Configuration of the FSY/SCK signals.
+ * \param clock_data_delay Configuration of the FSY/SCK signals for Generic Streaming.
+ * \param result_fptr Required result callback
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | Invalid callback pointer
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Xrm_Stream_SetPortConfig(CExtendedResourceManager *self,
+ uint8_t index,
+ Ucs_Stream_PortOpMode_t op_mode,
+ Ucs_Stream_PortOption_t port_option,
+ Ucs_Stream_PortClockMode_t clock_mode,
+ Ucs_Stream_PortClockDataDelay_t clock_data_delay,
+ Ucs_Xrm_Stream_PortCfgResCb_t result_fptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_API_LOCKED;
+
+ if ((self != NULL) && (result_fptr != NULL) )
+ {
+ if(Xrm_IsApiFree(self) != false)
+ {
+ Xrm_ApiLocking(self, true);
+
+ self->current_streamport_config.index = index;
+ self->current_streamport_config.op_mode = op_mode;
+ self->current_streamport_config.port_option = port_option;
+ self->current_streamport_config.clock_mode = clock_mode;
+ self->current_streamport_config.clock_data_delay = clock_data_delay;
+ self->current_streamport_config.result_fptr = result_fptr;
+
+ ret_val = Xrm_SetStreamPortConfiguration(self);
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+
+ return ret_val;
+}
+
+/*! \brief This function is used to configure a Streaming Port.
+ * \param self Instance pointer
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | Invalid callback pointer
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ */
+Ucs_Return_t Xrm_SetStreamPortConfiguration (CExtendedResourceManager *self)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (Xrm_IsCurrDeviceAlreadyAttached(self) == false)
+ {
+ ret_val = Xrm_RemoteDeviceAttach(self, XRM_EVENT_STREAMPORT_CONFIG_SET);
+ }
+ else
+ {
+ ret_val = Inic_StreamPortConfig_SetGet(self->inic_ptr,
+ self->current_streamport_config.index,
+ self->current_streamport_config.op_mode,
+ self->current_streamport_config.port_option,
+ self->current_streamport_config.clock_mode,
+ self->current_streamport_config.clock_data_delay,
+ &self->obs.stream_port_config_obs);
+ if(ret_val == UCS_RET_SUCCESS)
+ {
+ self->obs.stream_port_config_fptr = self->current_streamport_config.result_fptr;
+ }
+ else if(ret_val == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_STREAMPORT_CONFIG_SET);
+ }
+ else if (ret_val == UCS_RET_ERR_API_LOCKED)
+ {
+ Xrm_ApiLocking(self, false);
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief This function requests the configurations of a Streaming Port.
+ * \param self Instance pointer
+ * \param index Streaming Port instance.
+ * \param result_fptr Required result callback
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ----------------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | At least one parameter is wrong
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ * UCS_RET_ERR_NOT_AVAILABLE | Associated device not found
+ */
+Ucs_Return_t Xrm_Stream_GetPortConfig(CExtendedResourceManager *self,
+ uint8_t index,
+ Ucs_Xrm_Stream_PortCfgResCb_t result_fptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_API_LOCKED;
+
+ if((self != NULL) && (result_fptr != NULL) )
+ {
+ if(Xrm_IsApiFree(self) != false)
+ {
+ Xrm_ApiLocking(self, true);
+
+ self->current_streamport_config.index = index;
+ self->current_streamport_config.result_fptr = result_fptr;
+
+ ret_val = Xrm_GetStreamPortConfiguration(self);
+ if (ret_val == UCS_RET_ERR_API_LOCKED)
+ {
+ /* from another process locked */
+ Xrm_ApiLocking(self, false);
+ }
+ }
+ }
+ else
+ {
+ ret_val = UCS_RET_ERR_PARAM;
+ }
+
+ return ret_val;
+}
+
+/*! \brief This function is used to configure a Streaming Port.
+ * \param self Instance pointer
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_PARAM | Invalid callback pointer
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ */
+Ucs_Return_t Xrm_GetStreamPortConfiguration (CExtendedResourceManager *self)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if (Xrm_IsCurrDeviceAlreadyAttached(self) == false)
+ {
+ ret_val = Xrm_RemoteDeviceAttach(self, XRM_EVENT_STREAMPORT_CONFIG_GET);
+ }
+ else
+ {
+ ret_val = Inic_StreamPortConfig_Get(self->inic_ptr,
+ self->current_streamport_config.index,
+ &self->obs.stream_port_config_obs);
+ if(ret_val == UCS_RET_SUCCESS)
+ {
+ self->obs.stream_port_config_fptr = self->current_streamport_config.result_fptr;
+ }
+ else if(ret_val == UCS_RET_ERR_BUFFER_OVERFLOW)
+ {
+ Xrm_WaitForTxMsgObj(self, XRM_EVENT_STREAMPORT_CONFIG_GET);
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Observer callback for Inic_StreamPortConfig_Get(). Casts the result and invokes
+ * the application result callback.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result
+ */
+void Xrm_Stream_PortConfigResult(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ Inic_StreamPortConfigStatus_t status;
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ Ucs_StdResult_t res = result_ptr_->result;
+
+ if(self_->obs.stream_port_config_fptr != NULL)
+ {
+ if((result_ptr_->result.code == UCS_RES_SUCCESS) && (result_ptr_->data_info != NULL))
+ {
+ status = *((Inic_StreamPortConfigStatus_t *)result_ptr_->data_info);
+ }
+ else
+ {
+ /* Fill param callback function with default (dummy) values */
+ status.index = 0x00U;
+ status.op_mode = UCS_STREAM_PORT_OP_MODE_GENERIC;
+ status.port_option = UCS_STREAM_PORT_OPT_IN_OUT;
+ status.clock_mode = UCS_STREAM_PORT_CLK_MODE_OUTPUT;
+ status.clock_data_delay = UCS_STREAM_PORT_CLK_DLY_NONE;
+ }
+ self_->obs.stream_port_config_fptr(Inic_GetTargetAddress(self_->inic_ptr),
+ status.index,
+ status.op_mode,
+ status.port_option,
+ status.clock_mode,
+ status.clock_data_delay,
+ res,
+ self_->base_ptr->ucs_user_ptr);
+ }
+
+ Xrm_ApiLocking(self_, false);
+}
+
+/*! \brief Enables or disables a specific MOST Network Port.
+ * \param self Instance pointer
+ * \param most_port_handle Port resource handle.
+ * \param enabled State of the MOST Port.
+ * \param result_fptr Optional result callback.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ * UCS_RET_ERR_NOT_INITIALIZED | UNICENS is not initialized
+ */
+Ucs_Return_t Xrm_Most_EnablePort(CExtendedResourceManager *self,
+ uint16_t most_port_handle,
+ bool enabled,
+ Ucs_StdResultCb_t result_fptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if(Xrm_IsApiFree(self) != false)
+ {
+ ret_val = Inic_MostPortEnable(self->inic_ptr,
+ most_port_handle,
+ enabled,
+ &self->obs.most_port_enable_obs);
+ if(ret_val == UCS_RET_SUCCESS)
+ {
+ self->obs.most_port_enable_fptr = result_fptr;
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Enables full streaming for a specific MOST Network Port.
+ * \param self Instance pointer
+ * \param most_port_handle Port resource handle.
+ * \param enabled State of the MOST Port related to full streaming.
+ * \param result_fptr Optional result callback.
+ * \return Possible return values are shown in the table below.
+ * Value | Description
+ * --------------------------- | ------------------------------------
+ * UCS_RET_SUCCESS | No error
+ * UCS_RET_ERR_BUFFER_OVERFLOW | No message buffer available
+ * UCS_RET_ERR_API_LOCKED | API is currently locked
+ * UCS_RET_ERR_NOT_INITIALIZED | UNICENS is not initialized
+ */
+Ucs_Return_t Xrm_Most_PortEnFullStr(CExtendedResourceManager *self,
+ uint16_t most_port_handle,
+ bool enabled,
+ Ucs_StdResultCb_t result_fptr)
+{
+ Ucs_Return_t ret_val = UCS_RET_ERR_NOT_INITIALIZED;
+
+ if(Xrm_IsApiFree(self) != false)
+ {
+ ret_val = Inic_MostPortEnFullStr(self->inic_ptr,
+ most_port_handle,
+ enabled,
+ &self->obs.most_port_en_full_str_obs);
+ if(ret_val == UCS_RET_SUCCESS)
+ {
+ self->obs.most_port_en_full_str_fptr = result_fptr;
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Observer callback for Inic_MostPortEnable(). Casts the result and invokes
+ * the application result callback.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result
+ */
+void Xrm_Most_PortEnableResult(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ if(self_->obs.most_port_enable_fptr != NULL)
+ {
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ self_->obs.most_port_enable_fptr(result_ptr_->result, self_->base_ptr->ucs_user_ptr);
+ }
+}
+
+/*! \brief Observer callback for Inic_MostPortEnFullStr(). Casts the result and invokes
+ * the application result callback.
+ * \param self Instance pointer
+ * \param result_ptr Reference to result
+ */
+void Xrm_Most_PortEnFullStrResult(void *self, void *result_ptr)
+{
+ CExtendedResourceManager *self_ = (CExtendedResourceManager *)self;
+ if(self_->obs.most_port_en_full_str_fptr != NULL)
+ {
+ Inic_StdResult_t *result_ptr_ = (Inic_StdResult_t *)result_ptr;
+ self_->obs.most_port_en_full_str_fptr(result_ptr_->result, self_->base_ptr->ucs_user_ptr);
+ }
+}
+
+/*! \brief Sets the current job pointer of the CExtendedResourceManager instance.
+ * \param resrc_ptr Reference to the resource handle list to be looked for.
+ * \param resrc_handle Reference to the resource handle to be found.
+ * \param job_ptr Reference to the job to be looked for.
+ * \param user_arg Reference to a user argument.
+ * \return \c false to continue the for-each-loop of the resources list table, otherwise \c true
+ */
+bool Xrm_SetCurrJobPtr(void *resrc_ptr, void *resrc_handle, void *job_ptr, void * user_arg)
+{
+ bool ret_val = false;
+ Xrm_ResourceHandleListItem_t * resrc_ptr_ = (Xrm_ResourceHandleListItem_t *)resrc_ptr;
+ uint16_t * resrc_handle_ = (uint16_t *)resrc_handle;
+ CExtendedResourceManager *self = (CExtendedResourceManager *)user_arg;
+
+ MISC_UNUSED(job_ptr);
+
+ if ((resrc_ptr_->resource_handle == *resrc_handle_) &&
+ (*resrc_handle_ != XRM_INVALID_RESOURCE_HANDLE) &&
+ (Dl_IsNodeInList(&self->job_list, &resrc_ptr_->job_ptr->node)))
+ {
+ self->current_job_ptr = resrc_ptr_->job_ptr;
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+
diff --git a/ucs2-lib/src/ucs_xrmpool.c b/ucs2-lib/src/ucs_xrmpool.c
new file mode 100644
index 0000000..116253b
--- /dev/null
+++ b/ucs2-lib/src/ucs_xrmpool.c
@@ -0,0 +1,210 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the Connection Storage Pool.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_UCS_XRM_INT
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_xrmpool.h"
+#include "ucs_xrm_pv.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class XrmPool */
+/*------------------------------------------------------------------------------------------------*/
+/*------------------------------------------------------------------------------------------------*/
+/* Initialization Methods */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the XrmPool class.
+ * \param self Instance pointer
+ */
+void Xrmp_Ctor(CXrmPool * self)
+{
+ uint8_t i;
+ MISC_MEM_SET(self, 0, sizeof(CXrmPool));
+
+ /* Initialize resource handle list */
+ for(i=0U; i<XRM_NUM_RESOURCE_HANDLES; i++)
+ {
+ self->resource_handle_list[i].resource_handle = XRM_INVALID_RESOURCE_HANDLE;
+ self->resource_handle_list[i].job_ptr = NULL;
+ self->resource_handle_list[i].resource_object_ptr = NULL;
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Service */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Stores the given resource handle in the resource handle list.
+ * \param self_ptr XrmPool Instance pointer
+ * \param resource_handle Resource handle to save
+ * \param job_ptr Reference to job
+ * \param resource_object_ptr Reference to resource object
+ * \return \c true if free slot in handle list was found, otherwise \c false
+ */
+bool Xrmp_StoreResourceHandle(CXrmPool * self_ptr, uint16_t resource_handle, Xrm_Job_t * job_ptr, UCS_XRM_CONST Ucs_Xrm_ResObject_t * resource_object_ptr)
+{
+ bool ret_val = false;
+ uint8_t i;
+
+ for(i=0U; i<XRM_NUM_RESOURCE_HANDLES; i++)
+ {
+ if(self_ptr->resource_handle_list[i].job_ptr == NULL)
+ {
+ self_ptr->resource_handle_list[i].job_ptr = job_ptr;
+ self_ptr->resource_handle_list[i].resource_object_ptr = resource_object_ptr;
+ self_ptr->resource_handle_list[i].resource_handle = resource_handle;
+ ret_val = true;
+ break;
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Retrieves the resource handle identified by the given job reference and the given
+ * resource object reference.
+ * \param self Instance pointer
+ * \param job_ptr Reference to the job. Use NULL as wildcard.
+ * \param resource_object_ptr Reference to the resource object
+ * \param func_ptr Optional function pointer in order to check whether the found job belongs to the provided XRM instance.
+ * \param usr_ptr User pointer used to store the XRM instance to be looked for
+ * \return Resource handle if handle was found, otherwise XRM_INVALID_RESOURCE_HANDLE.
+ */
+uint16_t Xrmp_GetResourceHandle(CXrmPool * self, Xrm_Job_t * job_ptr, UCS_XRM_CONST Ucs_Xrm_ResObject_t * resource_object_ptr, Xrmp_CheckJobListFunc_t func_ptr, void * usr_ptr)
+{
+ uint16_t ret_val = XRM_INVALID_RESOURCE_HANDLE;
+ uint8_t i;
+ bool job_found = true;
+
+ for(i=0U; i<XRM_NUM_RESOURCE_HANDLES; i++)
+ {
+ if(((self->resource_handle_list[i].job_ptr == job_ptr) || (job_ptr == NULL)) &&
+ (self->resource_handle_list[i].resource_object_ptr == resource_object_ptr))
+ {
+ if ((func_ptr != NULL) && (usr_ptr != NULL))
+ {
+ job_found = func_ptr(usr_ptr, self->resource_handle_list[i].job_ptr);
+ }
+
+ if (job_found)
+ {
+ ret_val = self->resource_handle_list[i].resource_handle;
+ break;
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+/*! \brief Returns the table index of the given resource object.
+ * \param self Instance pointer
+ * \param job_ptr Reference to job
+ * \param obj_pptr Reference to array of references to INIC resource objects
+ * \return Table index of the given resource object. If entry is not found 0xFF is returned.
+ */
+uint8_t Xrmp_GetResourceHandleIdx(CXrmPool *self, Xrm_Job_t *job_ptr, UCS_XRM_CONST Ucs_Xrm_ResObject_t **obj_pptr)
+{
+ uint8_t i = 0U;
+ uint8_t ret_val = 0xFFU;
+
+ MISC_UNUSED(self);
+
+ while(job_ptr->resource_object_list_ptr[i] != NULL)
+ {
+ if(job_ptr->resource_object_list_ptr[i] == *obj_pptr)
+ {
+ ret_val = i;
+ break;
+ }
+ i++;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Returns the reference of the job that is identified by the given resource object list.
+ * \param self Instance pointer
+ * \param resource_object_list[] Reference to array of references to INIC resource objects
+ * \return Reference to the desired job if the job was found, otherwise NULL.
+ */
+Xrm_Job_t * Xrmp_GetJob(CXrmPool * self, UCS_XRM_CONST Ucs_Xrm_ResObject_t * resource_object_list[])
+{
+ uint8_t i;
+ Xrm_Job_t *ret_ptr = NULL;
+
+ for(i=0U; i<(uint8_t)XRM_NUM_JOBS; i++)
+ {
+ if(self->job_list[i].resource_object_list_ptr == resource_object_list)
+ {
+ ret_ptr = &self->job_list[i];
+ break;
+ }
+ else if((self->job_list[i].resource_object_list_ptr == NULL) && (ret_ptr == NULL))
+ {
+ ret_ptr = &self->job_list[i];
+ }
+ }
+
+ return ret_ptr;
+}
+
+/*! \brief Calls the given function for each node in the resource list. If the func_ptr
+ * returns true the loop is stopped.
+ * \param self Instance pointer
+ * \param func_ptr Reference of the callback function which is called for each node
+ * \param user_data_ptr1 Reference of optional user data 1 pass to func_ptr
+ * \param user_data_ptr2 Reference of optional user data 2 pass to func_ptr
+ * \param user_data_ptr3 Reference of optional user data 3 pass to func_ptr
+ */
+void Xrmp_Foreach(CXrmPool *self, Xrmp_ForeachFunc_t func_ptr, void *user_data_ptr1, void *user_data_ptr2, void *user_data_ptr3)
+{
+ uint8_t j;
+
+ for(j=0U; j<XRM_NUM_RESOURCE_HANDLES; j++)
+ {
+ if (self->resource_handle_list[j].job_ptr != NULL)
+ {
+ if (func_ptr(&self->resource_handle_list[j], user_data_ptr1, user_data_ptr2, user_data_ptr3) != false)
+ {
+ break;
+ }
+ }
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+