/*
* MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch
*
* Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 .
*
* You may also obtain this software under a propriety license from Microchip.
* Please contact Microchip for further information.
*
*/
/* parasoft suppress item * reason "not part of MOST NetServices delivery" */
/*!
* \file
* \brief Implementation of MOST NetServices Light
*/
/*------------------------------------------------------------------------------------------------*/
/* Includes */
/*------------------------------------------------------------------------------------------------*/
#include "mns_types_cfg.h"
#include "mnsl.h"
#include "mns_base.h"
#include "mns_misc.h"
#include "mns_pmchannel.h"
#include "mns_ams.h"
#include "mns_transceiver.h"
#include "mns_pmfifos.h"
#include //TKU
/*------------------------------------------------------------------------------------------------*/
/* Internal prototypes */
/*------------------------------------------------------------------------------------------------*/
static void Mnsl_OnPmsEvent(void *self, void *event_code);
static void Mnsl_OnServiceRequest(void *self, void *data_ptr);
static void Mnsl_OnGetTickCount(void *self, void *tick_count_value_ptr);
static void Mnsl_OnSetApplicationTimer(void *self, void *new_time_value_ptr);
/*------------------------------------------------------------------------------------------------*/
/* Implementation of class CMnsl */
/*------------------------------------------------------------------------------------------------*/
/*! \brief Assigns default values to a provided MNSL init structure
* \param init_ptr Reference to MNSL init structure.
* \param enable_watchdog Set to \c true for normal use. Set to \c false to disable the watchdog
* supervision for debugging purpose only.
*/
extern void Mnsl_SetDefaultConfig(Mnsl_InitData_t *init_ptr, bool enable_watchdog)
{
MISC_MEM_SET(init_ptr, 0, sizeof(Mnsl_InitData_t));
init_ptr->pms.active_fifos = MNSL_FIFOS_MCM_ICM;
init_ptr->pms.compressed = false;
init_ptr->pms.icm_config.fifo_id = PMP_FIFO_ID_ICM;
init_ptr->pms.icm_config.tx_wd_timeout = 0U;
init_ptr->pms.icm_config.tx_wd_timer_value = 0U;
init_ptr->pms.icm_config.rx_ack_timeout = 10U;
init_ptr->pms.icm_config.rx_busy_allowed = 0xFU;
init_ptr->pms.icm_config.rx_credits = PMCH_FIFO_CREDITS;
init_ptr->pms.icm_config.rx_threshold = PMCH_FIFO_THRESHOLD;
init_ptr->pms.rcm_config.fifo_id = PMP_FIFO_ID_RCM;
init_ptr->pms.rcm_config.tx_wd_timeout = 10U; /* watchdog timeout: 1s */
init_ptr->pms.rcm_config.tx_wd_timer_value = 600U; /* watchdog trigger every 600 ms */
init_ptr->pms.rcm_config.rx_ack_timeout = 10U;
init_ptr->pms.rcm_config.rx_busy_allowed = 0xFU;
init_ptr->pms.rcm_config.rx_credits = PMCH_FIFO_CREDITS;
init_ptr->pms.rcm_config.rx_threshold = PMCH_FIFO_THRESHOLD;
init_ptr->pms.mcm_config.fifo_id = PMP_FIFO_ID_MCM;
init_ptr->pms.mcm_config.tx_wd_timeout = 10U; /* watchdog timeout: 1s */
init_ptr->pms.mcm_config.tx_wd_timer_value = 600U; /* watchdog trigger every 600 ms */
init_ptr->pms.mcm_config.rx_ack_timeout = 10U;
init_ptr->pms.mcm_config.rx_busy_allowed = 0xFU;
init_ptr->pms.mcm_config.rx_credits = PMCH_MCM_CREDITS;
init_ptr->pms.mcm_config.rx_threshold = PMCH_MCM_THRESHOLD;
if (enable_watchdog == false)
{
init_ptr->pms.icm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */
init_ptr->pms.rcm_config.tx_wd_timeout = 0U; /* watchdog timeout: 0 -> infinite */
init_ptr->pms.rcm_config.tx_wd_timer_value = 0U;/* watchdog timer: 0 -> no timer */
init_ptr->pms.rcm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */
init_ptr->pms.mcm_config.tx_wd_timeout = 0U; /* watchdog timeout: 0 -> infinite */
init_ptr->pms.mcm_config.tx_wd_timer_value = 0U;/* watchdog timer: 0 -> no timer */
init_ptr->pms.mcm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */
}
}
/*! \brief Initialize MNS Light class
* \param init_ptr Reference to the MNSL init structure.
* The memory of the init structure can be freed after the function returns.
*/
void Mnsl_Init(CMnsl *mnsl, Mnsl_InitData_t *init_ptr, void *inst_ptr)
{
assert(NULL != mnsl);
CSingleObserver *srv_request_obs_ptr = NULL;
CSingleObserver *app_timer_obs_ptr = NULL;
Base_InitData_t base_init_data;
Pmch_InitData_t pmch_init_data;
CPmFifo *icm_ptr = NULL;
CPmFifo *rcm_ptr = NULL;
CPmFifo *mcm_ptr = NULL;
CTransceiver *trcv_mcm_ptr = NULL;
CTransceiver *trcv_rcm_ptr = NULL;
MISC_MEM_SET(mnsl, 0, sizeof(mnsl));
MISC_MEM_SET(&base_init_data, 0, sizeof(base_init_data));
mnsl->inst_ptr = inst_ptr; //TKU
mnsl->inst_id = 1U;
mnsl->get_tick_count_fptr = init_ptr->general.get_tickcount_fptr;
mnsl->srv_request_fptr = init_ptr->general.request_service_fptr;
mnsl->set_app_timer_fptr = init_ptr->general.set_app_timer_fptr;
if(mnsl->srv_request_fptr != NULL)
{
Sobs_Ctor(&mnsl->srv_request_obs, mnsl, &Mnsl_OnServiceRequest);
srv_request_obs_ptr = &mnsl->srv_request_obs;
}
if (mnsl->set_app_timer_fptr != NULL)
{
Sobs_Ctor(&mnsl->set_app_timer_obs, mnsl, &Mnsl_OnSetApplicationTimer);
app_timer_obs_ptr = &mnsl->set_app_timer_obs;
}
base_init_data.mns_inst_id = 1U;
base_init_data.tm.mns_inst_id = 1U;
base_init_data.scd.mns_inst_id = 1U;
Sobs_Ctor(&mnsl->get_tick_count_obs, mnsl, &Mnsl_OnGetTickCount);
base_init_data.tm.get_tick_count_obs_ptr = &mnsl->get_tick_count_obs;
base_init_data.scd.service_request_obs_ptr = srv_request_obs_ptr;
base_init_data.tm.set_application_timer_obs_ptr = app_timer_obs_ptr;
Base_Ctor(&mnsl->base, &base_init_data);
/* Initialize port message service */
pmch_init_data.mns_inst_id = 1U;
pmch_init_data.tx_release_fptr = &Fifo_TxOnRelease;
pmch_init_data.lld_iface = init_ptr->lld;
Pmch_Ctor(&mnsl->pm_channel, &pmch_init_data, inst_ptr);
if((init_ptr->pms.active_fifos & MNSL_FIFOS_ICM) == MNSL_FIFOS_ICM)
{
Fifo_InitData_t icm_init;
icm_init.base_ptr = &mnsl->base;
icm_init.channel_ptr = &mnsl->pm_channel;
icm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete;
icm_init.rx_cb_inst = &mnsl->icm_transceiver;
if (init_ptr->pms.compressed)
{
icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80);
icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80);
}
else
{
icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
}
Fifo_Ctor(&mnsl->icm_fifo, &icm_init, &init_ptr->pms.icm_config);
Trcv_Ctor(&mnsl->icm_transceiver, &mnsl->icm_fifo, MSG_ADDR_EHC_CFG, base_init_data.mns_inst_id, PMP_FIFO_ID_ICM);/* initialize ICM transceiver and set link to PMS instance */
icm_ptr = &mnsl->icm_fifo;
}
if((init_ptr->pms.active_fifos & MNSL_FIFOS_RCM) == MNSL_FIFOS_RCM)
{
Fifo_InitData_t rcm_init;
rcm_init.base_ptr = &mnsl->base;
rcm_init.channel_ptr = &mnsl->pm_channel;
rcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete;
rcm_init.rx_cb_inst = &mnsl->rcm_transceiver;
rcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
rcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
/* Note: RCM compressed data format is not supported */
Fifo_Ctor(&mnsl->rcm_fifo, &rcm_init, &init_ptr->pms.rcm_config);
Trcv_Ctor(&mnsl->rcm_transceiver, &mnsl->rcm_fifo, MSG_ADDR_EHC_CFG, base_init_data.mns_inst_id, PMP_FIFO_ID_RCM);/* initialize ICM transceiver and set link to PMS instance */
rcm_ptr = &mnsl->rcm_fifo;
trcv_rcm_ptr = &mnsl->rcm_transceiver;
}
if((init_ptr->pms.active_fifos & MNSL_FIFOS_MCM) == MNSL_FIFOS_MCM)
{
Fifo_InitData_t mcm_init;
mcm_init.base_ptr = &mnsl->base;
mcm_init.channel_ptr = &mnsl->pm_channel;
mcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete;
mcm_init.rx_cb_inst = &mnsl->mcm_transceiver;
if (init_ptr->pms.compressed)
{
mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_81);
mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80);
}
else
{
mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00);
}
Fifo_Ctor(&mnsl->mcm_fifo, &mcm_init, &init_ptr->pms.mcm_config);
Trcv_Ctor(&mnsl->mcm_transceiver, &mnsl->mcm_fifo, MSG_ADDR_EHC_APP, base_init_data.mns_inst_id, PMP_FIFO_ID_MCM);/* initialize ICM transceiver and set link to PMS instance */
mcm_ptr = &mnsl->mcm_fifo;
trcv_mcm_ptr = &mnsl->mcm_transceiver;
}
if ((trcv_mcm_ptr != NULL) || (trcv_rcm_ptr != NULL)) /* initialize AMS if MCM or RCM is active */
{
mnsl->ams_allocator.alloc_fptr = init_ptr->ams.rx_alloc_mem_fptr;
mnsl->ams_allocator.free_fptr = init_ptr->ams.rx_free_mem_fptr;
Amsp_Ctor(&mnsl->ams_pool, &mnsl->ams_allocator, mnsl->base.mns_inst_id);
Ams_Ctor(&mnsl->ams, &mnsl->base, trcv_mcm_ptr, trcv_rcm_ptr, &mnsl->ams_pool, AMS_RX_DEF_SIZE_PAYLOAD);
}
/* initialize FIFO handler */
Fifos_Ctor(&mnsl->fifos, &mnsl->base, &mnsl->pm_channel, icm_ptr, mcm_ptr, rcm_ptr);
/* register event callback */
mnsl->event_fptr = init_ptr->general.event_fptr;
Obs_Ctor(&mnsl->pms_observer, mnsl, &Mnsl_OnPmsEvent);
Fifos_AddEventObserver(&mnsl->fifos, &mnsl->pms_observer);
}
/*! \brief Synchronizes the PMS
* \details Accordingly MNSL_EVENT_SYNC_COMPLETE or MNSL_EVENT_SYNC_FAILED will be notified.
*/
void Mnsl_Synchronize(CMnsl *mnsl)
{
assert(NULL != mnsl);
/* initializes the port message service & LLD interface */
Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT);
Fifos_Synchronize(&mnsl->fifos, true, true);
}
/*! \brief Un-synchronizes the PMS
* \details Accordingly MNSL_EVENT_UNSYNC_COMPLETE or MNSL_EVENT_UNSYNC_FAILED will be notified.
* Calling this function if MNSL is already un-synced will report MNSL_EVENT_UNSYNC_FAILED,
* since the low-level driver interface is not active.
* \param initial MNSL is able to trigger un-synchronization in advance of the initial
* synchronization. In this case, timeouts and retries behave like a synchronization
* and on MNSL_EVENT_UNSYNC_COMPLETE the LLD interface will stay running.
* I.e. if set to \c true the un-synchronization behaves like MNS initial un-synchronization.
* If set to \c false, the un-synchronization behaves like MNS final un-synchronization.
*/
void Mnsl_Unsynchronize(CMnsl *mnsl, bool initial)
{
assert(NULL != mnsl);
if (initial == false)
{
Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT); /* final sync */
}
else
{
Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); /* initial sync */
}
Fifos_Unsynchronize(&mnsl->fifos, true, initial);
}
/*! \brief Handles PMS event and notifies MNSL registered event callback
* \param self Reference to instance
* \param event_code Event notified by the PMS
*/
static void Mnsl_OnPmsEvent(void *self, void *event_code)
{
assert(NULL != self);
Fifos_Event_t *code = (Fifos_Event_t*)event_code;
CMnsl *mnsl = (CMnsl *)self;
if (mnsl->event_fptr != NULL)
{
switch (*code)
{
case FIFOS_EV_SYNC_LOST:
Eh_ReportEvent(&mnsl->base.eh, EH_E_SYNC_LOST); /* event is necessary for AMS cleanup */
mnsl->event_fptr(MNSL_EVENT_SYNC_LOST, mnsl->inst_ptr);
break;
case FIFOS_EV_SYNC_ESTABLISHED:
mnsl->event_fptr(MNSL_EVENT_SYNC_COMPLETE, mnsl->inst_ptr);
break;
case FIFOS_EV_SYNC_FAILED:
mnsl->event_fptr(MNSL_EVENT_SYNC_FAILED, mnsl->inst_ptr);
break;
case FIFOS_EV_UNSYNC_COMPLETE:
mnsl->event_fptr(MNSL_EVENT_UNSYNC_COMPLETE, mnsl->inst_ptr);
Eh_ReportEvent(&mnsl->base.eh, EH_E_UNSYNC_COMPLETE); /* event is necessary for AMS cleanup */
break;
case FIFOS_EV_UNSYNC_FAILED:
mnsl->event_fptr(MNSL_EVENT_UNSYNC_FAILED, mnsl->inst_ptr);
Eh_ReportEvent(&mnsl->base.eh, EH_E_UNSYNC_FAILED); /* event is necessary for AMS cleanup */
break;
default:
break;
}
}
}
/*! \brief Service function of MNS Light
*/
void Mnsl_Service(CMnsl *mnsl)
{
assert(NULL != mnsl);
bool pending_events;
TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service()", 0U));
Scd_Service(&mnsl->base.scd); /* Run the scheduler */
pending_events = Scd_AreEventsPending(&mnsl->base.scd); /* Check if events are still pending? */
if (pending_events != false) /* At least one event is pending? */
{
TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service(): events still pending", 0U));
Mnsl_OnServiceRequest(mnsl, NULL);
}
else /* No event is pending */
{
TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service(): calling Tm_CheckForNextService()", 0U));
mnsl->base.scd.scd_srv_is_running = true; /* prevent continuous service requests if no app timer is provided */
Tm_CheckForNextService(&mnsl->base.tm); /* If MNS timers are running: What is the next time that the timer management must be */
mnsl->base.scd.scd_srv_is_running = false; /* serviced again? */
}
}
/*! \brief Reports that the application provided timer has expired.
*/
void Mnsl_ReportTimeout(CMnsl *mnsl)
{
assert(NULL != mnsl);
TR_INFO((mnsl->inst_id, "[API]", "Mns_ReportTimeout()", 0U));
Tm_TriggerService(&mnsl->base.tm); /* Trigger TM service call */
}
/*! \brief Callback function which is invoked by the scheduler
* \param self Parameter not used with single instance API
* \param data_ptr Currently unused
*/
static void Mnsl_OnServiceRequest(void *self, void *data_ptr)
{
assert(NULL != self);
CMnsl *mnsl = (CMnsl *)self;
MISC_UNUSED(data_ptr);
if (mnsl->srv_request_fptr != NULL)
{
TR_INFO((mnsl->inst_id, "[API]", "Mnsl_OnServiceRequest(): calling srv_request_fptr()", 0U));
mnsl->srv_request_fptr(mnsl->inst_ptr);
}
}
/*! \brief This function is used in combination with the observer \c mns.get_tick_count_obs
* which is used to request the current tick count value form the application.
* \param self Parameter not used with single instance API
* \param tick_count_value_ptr Reference to the requested tick count value. The pointer must
* be casted into data type uint16_t.
*/
static void Mnsl_OnGetTickCount(void *self, void *tick_count_value_ptr)
{
assert(NULL != self);
CMnsl *mnsl = (CMnsl *)self;
*((uint16_t *)tick_count_value_ptr) = mnsl->get_tick_count_fptr();
}
/*! \brief Callback function which is invoked to start the application timer when the MNSL service
* is implemented event driven
* \param self The instance
* \param new_time_value_ptr Reference to the new timer value. The pointer must be casted into
* data type uint16_t.
*/
static void Mnsl_OnSetApplicationTimer(void *self, void *new_time_value_ptr)
{
CMnsl *self_ = (CMnsl*)self;
TR_INFO((mnsl->inst_id, "[API]", "Mnsl_OnSetApplicationTimer(): set_app_timer_fptr(%d)", 1U, *((uint16_t *)new_time_value_ptr)));
self_->set_app_timer_fptr(*((uint16_t *)new_time_value_ptr), self_->inst_ptr);
}
/*! \brief Returns the ICM transceiver object
* \return Reference to the ICM transceiver
*/
CTransceiver * Mnsl_GetIcmTransceiver(CMnsl *mnsl)
{
return &mnsl->icm_transceiver;
}
/*! \brief Returns the RCM transceiver object
* \return Reference to the RCM transceiver
*/
CTransceiver * Mnsl_GetRcmTransceiver(CMnsl *mnsl)
{
return &mnsl->rcm_transceiver;
}
/*! \brief Returns the MCM transceiver object
* \return Reference to the MCM transceiver
*/
CTransceiver * Mnsl_GetMcmTransceiver(CMnsl *mnsl)
{
return &mnsl->mcm_transceiver;
}
/*! \brief Returns the AMS transceiver object
* \details It is important not make usage of competing transceivers
* AMS, MCM and RCM. Either use AMS exclusively or MCM and RCM.
* Important: Do not use AMS in mode \c MNSL_FIFOS_ICM.
* \return Reference to the AMS
*/
CAms * Mnsl_GetAmsTransceiver(CMnsl *mnsl)
{
return &mnsl->ams;
}
/*------------------------------------------------------------------------------------------------*/
/* End of file */
/*------------------------------------------------------------------------------------------------*/