diff options
author | Fulup Ar Foll <fulup@iot.bzh> | 2017-05-26 18:45:56 +0200 |
---|---|---|
committer | Fulup Ar Foll <fulup@iot.bzh> | 2017-05-26 18:45:56 +0200 |
commit | d2e42029ec04c3f224580f8007cdfbbfe0fc47a6 (patch) | |
tree | ad2ccf167cf7997c84191d41e6ba55cb2efd6bed /ucs2-lib/src | |
parent | 18e393e1443fd4c38b34979888fb55d30448cf31 (diff) |
Initial Commit
Diffstat (limited to 'ucs2-lib/src')
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 = ¬if_res; + res_data.result.code = UCS_RES_SUCCESS; + res_data.result.info_ptr = NULL; + + MISC_DECODE_WORD(¬if_res.func_id, &(msg_ptr->tel.tel_data_ptr[0])); + if (msg_ptr->tel.tel_len == 4U) + { + MISC_DECODE_WORD(¬if_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, ¤t_obs_ptr_->node); + } + return false; +} + +/*! \brief Returns the number of registered observers of a subject. + * \param self Instance pointer + * \return The number of registered observers + */ +uint8_t Sub_GetNumObservers(CSubject *self) +{ + return self->num_observers; +} + +/*! \brief Switches all observers of the source-subject to the target-subject. + * \param sub_target Target subject + * \param sub_source Source subject + * \return \c SUB_OK: No error + * \return \c SUB_INVALID_OPERATION: Target and source must be different objects + */ +Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source) +{ + Sub_Ret_t ret_val; + + if(sub_target == sub_source) + { + ret_val = SUB_INVALID_OPERATION; + } + else + { + Dl_AppendList(&sub_target->list, &sub_source->list); + sub_target->num_observers += sub_source->num_observers; + sub_source->num_observers = 0U; + ret_val = SUB_OK; + } + return ret_val; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the observer class. Initializes an observer which is notified + * by a corresponding subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param update_fptr Callback function to update the observer + */ +void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr) +{ + 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, ¤t_tick_count, false); + + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + bool continue_loop = true; + /* Calculate time difference between the current and the last TM service run */ + uint16_t tick_count_diff = (uint16_t)(current_tick_count - self->last_tick_count); + /* Save current tick count for next service run */ + self->last_tick_count = current_tick_count; + + /* Loop while timer list is not empty */ + while((self->timer_list.head != NULL) && (continue_loop!= false)) + { + /* Is not first timer in list elapsed yet? */ + if(tick_count_diff <= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Update delta of first timer in list */ + ((CTimer *)self->timer_list.head->data_ptr)->delta -= tick_count_diff; + tick_count_diff = 0U; + } + else /* At least first timer in list elapsed */ + { + /* Update tick count difference for next timer in list */ + tick_count_diff -= ((CTimer *)self->timer_list.head->data_ptr)->delta; + /* First timer elapsed */ + ((CTimer *)self->timer_list.head->data_ptr)->delta = 0U; + } + + /* First timer in list elapsed? */ + if(0U == ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Handle elapsed timer */ + continue_loop = Tm_HandleElapsedTimer(self); + } + else /* No elapsed timer in list. */ + { + /* First timer in list updated! Set trigger to inform application (see + Tm_CheckForNextService()) and stop TM service. */ + self->set_service_timer = true; + continue_loop = false; + } + } + } +} + +/*! \brief This function is called if the first timer in list is elapsed. The timer handler + * callback function is invoked. If the timer is a periodic timer it is wound up again. + * \param self Instance pointer + * \return \c true if the next timer must be check. + * \return \c false if the wound up timer (periodic timer) is new head of timer list + */ +static bool Tm_HandleElapsedTimer(CTimerManagement *self) +{ + bool ret_val = true; + + CDlNode *node = self->timer_list.head; + /* Reset flag to be able to check if timer object has changed within handler + callback function */ + ((CTimer *)node->data_ptr)->changed = false; + /* Call timer handler callback function */ + ((CTimer *)node->data_ptr)->handler_fptr(((CTimer *)node->data_ptr)->args_ptr); + + /* Timer object hasn't changed within handler callback function? */ + if(false == ((CTimer *)node->data_ptr)->changed) + { + /* Remove current timer from list */ + (void)Dl_Remove(&self->timer_list, node); + /* Mark timer as unused */ + ((CTimer *)node->data_ptr)->in_use = false; + /* Is current timer a periodic timer? */ + if(((CTimer *)node->data_ptr)->period > 0U) + { + /* Reload current timer */ + Tm_SetTimerInternal(self, + ((CTimer *)node->data_ptr), + ((CTimer *)node->data_ptr)->handler_fptr, + ((CTimer *)node->data_ptr)->args_ptr, + ((CTimer *)node->data_ptr)->period, + ((CTimer *)node->data_ptr)->period); + + if(node == self->timer_list.head) /* Is current timer new head of list? */ + { + /* Set trigger to inform application (see Tm_CheckForNextService()) and + stop TM service. */ + self->set_service_timer = true; + ret_val = false; + } + } + } + + return ret_val; +} + +/*! \brief Calls an application callback function to inform the application that the 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, ¤t_tick_count, false); + /* Has head of timer list changed? */ + if(self->set_service_timer != false) + { + uint16_t new_time; + uint16_t diff = current_tick_count - self->last_tick_count; + self->set_service_timer = false; + 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, ¤t_tick_count, false); + + /* Save timer specific values */ + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + timer_ptr->in_use = true; + timer_ptr->handler_fptr = handler_fptr; + timer_ptr->args_ptr = args_ptr; + timer_ptr->elapse = elapse; + timer_ptr->period = period; + timer_ptr->delta = elapse; + + /* Create back link to be able to point from node to timer object */ + timer_ptr->node.data_ptr = (void *)timer_ptr; + + if(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 */ +/*------------------------------------------------------------------------------------------------*/ + |