Updated GDB ini file to load binding symbols directly from SDK
[apps/agl-service-unicens.git] / ucs2-lib / src / ucs_pmfifo.c
1 /*------------------------------------------------------------------------------------------------*/
2 /* UNICENS V2.1.0-3491                                                                            */
3 /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG.                              */
4 /*                                                                                                */
5 /* This program is free software: you can redistribute it and/or modify                           */
6 /* it under the terms of the GNU General Public License as published by                           */
7 /* the Free Software Foundation, either version 2 of the License, or                              */
8 /* (at your option) any later version.                                                            */
9 /*                                                                                                */
10 /* This program is distributed in the hope that it will be useful,                                */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of                                 */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                  */
13 /* GNU General Public License for more details.                                                   */
14 /*                                                                                                */
15 /* You should have received a copy of the GNU General Public License                              */
16 /* along with this program.  If not, see <http://www.gnu.org/licenses/>.                          */
17 /*                                                                                                */
18 /* You may also obtain this software under a propriety license from Microchip.                    */
19 /* Please contact Microchip for further information.                                              */
20 /*------------------------------------------------------------------------------------------------*/
21
22 /*!
23  * \file
24  * \brief Implementation of Port Message FIFO
25  *
26  * \cond UCS_INTERNAL_DOC
27  * \addtogroup  G_PMF
28  * @{
29  */
30
31 /*------------------------------------------------------------------------------------------------*/
32 /* Includes                                                                                       */
33 /*------------------------------------------------------------------------------------------------*/
34 #include "ucs_pmfifo.h"
35 #include "ucs_pmp.h"
36 #include "ucs_pmcmd.h"
37 #include "ucs_misc.h"
38
39 /*------------------------------------------------------------------------------------------------*/
40 /* Internal macros                                                                                */
41 /*------------------------------------------------------------------------------------------------*/
42 /*------------------------------------------------------------------------------------------------*/
43 /* Internal Constants                                                                             */
44 /*------------------------------------------------------------------------------------------------*/
45 static const uint8_t     FIFO_SRV_PRIO              = 252U; /* parasoft-suppress  MISRA2004-8_7 "configuration property" */
46 static const Srv_Event_t FIFO_SE_RX_SERVICE         = 1U;   /*!< \brief Event which triggers the Rx service */
47 static const Srv_Event_t FIFO_SE_TX_SERVICE         = 2U;   /*!< \brief Event which triggers the Rx service */
48 static const Srv_Event_t FIFO_SE_TX_APPLY_STATUS    = 4U;   /*!< \brief Event which triggers to apply the current INIC status */
49 static const Srv_Event_t FIFO_SE_ALL                = 7U;   /* parasoft-suppress  MISRA2004-8_7 "configuration property" */
50
51 /*------------------------------------------------------------------------------------------------*/
52 /* Internal prototypes                                                                            */
53 /*------------------------------------------------------------------------------------------------*/
54 static void Fifo_InitCounters(CPmFifo *self, uint8_t tx_sid_complete, uint8_t tx_credits);
55 static void Fifo_Service(void *self);
56
57 static void Fifo_RxService(CPmFifo *self);
58 static void Fifo_RxCheckStatusTrigger(CPmFifo *self);
59 static void Fifo_RxGetCredit(CPmFifo *self);
60 static void Fifo_RxReleaseCredit(CPmFifo *self);
61 static bool Fifo_RxProcessData(CPmFifo *self, CMessage *msg_ptr);
62 static void Fifo_RxProcessStatus(CPmFifo *self, CMessage *msg_ptr);
63 static void Fifo_RxProcessCommand(CPmFifo *self, CMessage *msg_ptr);
64 static void Fifo_RxProcessSyncStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code, uint8_t *header_ptr);
65 static uint8_t Fifo_RxCheckFailureCode(CPmFifo *self, uint8_t failure_code);
66 static void Fifo_OnRx(void *self, CMessage *msg_ptr);
67
68 static void Fifo_TxService(CPmFifo *self);
69 static void Fifo_TxProcessData(CPmFifo *self);
70 static void Fifo_TxProcessStatus(CPmFifo *self);
71 static void Fifo_TxProcessCommand(CPmFifo *self);
72
73 static void Fifo_TxEnqueueBypassMsg(CPmFifo *self, CDlList *q_ptr, CMessage *msg_ptr);
74 static bool Fifo_FindFirstRegularMsg(void *d_ptr, void *ud_ptr);
75
76 static void Fifo_TxExecuteCancel(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code);
77 static void Fifo_TxExecuteCancelAll(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code);
78 static void Fifo_TxFinishedCancelAll(CPmFifo *self);
79 static uint8_t Fifo_TxPendingGetFollowerId(CPmFifo *self);
80 static void Fifo_TxCancelFollowers(CPmFifo *self, uint8_t follower_id, Ucs_MsgTxStatus_t status);
81
82 static bool Fifo_TxHasAccessPending(CPmFifo *self);
83 static void Fifo_TxRestorePending(CPmFifo *self);
84
85 static void Fifo_TxOnWatchdogTimer(void *self);
86 static void Fifo_TxStartWatchdog(CPmFifo *self);
87
88 static uint8_t Fifo_TxGetValidAcknowledges(CPmFifo *self, uint8_t sid);
89 static bool Fifo_TxNotifyStatus(CPmFifo *self, uint8_t sid, Ucs_MsgTxStatus_t status);
90 static void Fifo_TxApplyCurrentStatus(CPmFifo *self);
91 static void Fifo_TxUpdateCurrentStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code);
92 static bool Fifo_TxIsIncomingSidValid(CPmFifo *self, uint8_t sid);
93
94 /*------------------------------------------------------------------------------------------------*/
95 /* Implementation                                                                                 */
96 /*------------------------------------------------------------------------------------------------*/
97 /*! \brief  Constructor of message FIFO
98  *  \param  self        The instance
99  *  \param  init_ptr    Reference to initialization data
100  *  \param  config_ptr  Reference to configuration
101  */
102 void Fifo_Ctor(CPmFifo *self, const Fifo_InitData_t *init_ptr, const Fifo_Config_t *config_ptr)
103 {
104     MISC_MEM_SET(self, 0, sizeof(*self));
105
106     self->init          = *init_ptr;
107     self->config        = *config_ptr;
108
109     self->sync_state    = FIFO_S_UNSYNCED_INIT;                         /* initialize members */
110     Sub_Ctor(&self->sync_state_subject, self->init.base_ptr->ucs_user_ptr);
111
112     TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_Ctor(): state: %u", 1U, self->sync_state));
113
114     Srv_Ctor(&self->service, FIFO_SRV_PRIO, self, &Fifo_Service);       /* registration of service */
115     (void)Scd_AddService(&self->init.base_ptr->scd, &self->service);
116
117     T_Ctor(&self->wd.timer);                                            /* setup watchdog */
118     self->wd.timer_value = self->config.tx_wd_timer_value;
119     Pmcmd_Ctor(&self->wd.wd_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD);
120     Pmcmd_SetContent(&self->wd.wd_cmd, 0U, PMP_CMD_TYPE_REQ_STATUS, PMP_CMD_CODE_REQ_STATUS, NULL, 0U);
121
122     /* init Rx part */
123     Dl_Ctor(&self->rx.queue, self->init.base_ptr->ucs_user_ptr);
124     self->rx.encoder_ptr = self->init.rx_encoder_ptr;
125     self->rx.on_complete_fptr = self->init.rx_cb_fptr;
126     self->rx.on_complete_inst = self->init.rx_cb_inst;
127
128     self->rx.ack_threshold = self->config.rx_threshold;
129
130     if (self->config.rx_threshold > self->config.rx_credits)/* configuration error - use single acknowledge */
131     {
132         self->rx.ack_threshold = 1U;
133         TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");
134     }
135
136     self->rx.wait_processing = false;
137     Pmcmd_Ctor(&self->rx.status, self->config.fifo_id, PMP_MSG_TYPE_STATUS);
138     Pmcmd_SetContent(&self->rx.status, 0U, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_SUCCESS, NULL, 0U);
139
140     /* init Tx part */
141     Dl_Ctor(&self->tx.waiting_queue, self->init.base_ptr->ucs_user_ptr);
142     Dl_Ctor(&self->tx.pending_q, self->init.base_ptr->ucs_user_ptr);
143
144     Pmcmd_Ctor(&self->tx.cancel_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD);
145     Pmcmd_SetContent(&self->tx.cancel_cmd, 0U, PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL, NULL, 0U);
146
147     Fifo_InitCounters(self, 0U, 0U);                        /* values are incremented on each sync attempt */
148     self->tx.encoder_ptr = init_ptr->tx_encoder_ptr;
149
150     /* FIFO synchronization command */
151     self->sync_cnt = 0xFFU;
152     self->sync_params[0] = config_ptr->rx_credits;
153     self->sync_params[1] = config_ptr->rx_busy_allowed; 
154     self->sync_params[2] = config_ptr->rx_ack_timeout;
155     self->sync_params[3] = config_ptr->tx_wd_timeout;
156     Pmcmd_Ctor(&self->tx.sync_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD);
157     Pmcmd_SetContent(&self->tx.sync_cmd, 0U, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_SYNC, self->sync_params, 4U);
158
159     /* default PM header for Tx */
160     self->tx.pm_header.pml = 6U;
161     self->tx.pm_header.pmhl  = self->tx.encoder_ptr->pm_hdr_sz - 3U;
162     Pmh_SetFph(&self->tx.pm_header, self->config.fifo_id, PMP_MSG_TYPE_DATA);
163     self->tx.pm_header.sid = 0U;
164     self->tx.pm_header.ext_type = (uint8_t)self->tx.encoder_ptr->content_type;
165
166     Lldp_Ctor(&self->tx.lld_pool, self, self->init.base_ptr->ucs_user_ptr);
167
168     Pmch_RegisterReceiver(self->init.channel_ptr, self->config.fifo_id, &Fifo_OnRx, self);
169 }
170
171 /*! \brief Initializes flow control and related counters
172  *  \param  self            The instance
173  *  \param  tx_sid_complete Reference to initialization data
174  *  \param  tx_credits      Number of credits for Tx
175  */
176 static void Fifo_InitCounters(CPmFifo *self, uint8_t tx_sid_complete, uint8_t tx_credits)
177 {
178     self->rx.busy_num = 0U;
179     self->rx.expected_sid = tx_sid_complete + 1U;
180     self->rx.ack_last_ok_sid = tx_sid_complete;
181
182     self->tx.credits = tx_credits;
183     self->tx.sid_next_to_use = tx_sid_complete +1U;
184     self->tx.sid_last_completed = tx_sid_complete;
185
186     self->tx.failure_status = 0U;
187     self->tx.failure_sid = 0U;
188
189     self->tx.current_sid = tx_sid_complete;
190     self->tx.current_type = PMP_STATUS_TYPE_FLOW;
191     self->tx.current_code = (uint8_t)PMP_STATUS_CODE_SUCCESS;
192 }
193
194 /*! \brief          Adds an observer of synchronization state changes
195  *  \param self     The instance
196  *  \param obs_ptr  The observer. The notification result type is \ref Pmp_FifoId_t.
197  */
198 void Fifo_AddStateObserver(CPmFifo *self, CObserver *obs_ptr)
199 {
200     (void)Sub_AddObserver(&self->sync_state_subject, obs_ptr);
201 }
202
203 /*! \brief          Removes an observer of synchronization state changes
204  *  \param self     The instance
205  *  \param obs_ptr  The observer.
206  */
207 void Fifo_RemoveStateObserver(CPmFifo *self, CObserver *obs_ptr)
208 {
209     (void)Sub_RemoveObserver(&self->sync_state_subject, obs_ptr);
210 }
211
212 /*! \brief  Stops execution of a FIFO and notifies sync lost if necessary
213  *  \param  self        The instance
214  *  \param  new_state   The new synchronization state
215  *  \param  allow_notification Set to \c false in order to avoid recursion
216  */
217 void Fifo_Stop(CPmFifo *self, Fifo_SyncState_t new_state, bool allow_notification)
218 {
219     bool notify = false; 
220
221     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));
222
223     if (self->sync_state != new_state)
224     {
225         notify = true;
226     }
227
228     self->sync_state = new_state;
229     self->tx.credits = 0U;
230
231     if (self->wd.timer_value != 0U)
232     {
233         Tm_ClearTimer(&self->init.base_ptr->tm, &self->wd.timer);
234     }
235
236     if ((notify != false) && (allow_notification != false))
237     {
238         Sub_Notify(&self->sync_state_subject, &self->config.fifo_id);
239     }
240 }
241
242 /*! \brief   Releases all external references
243  *  \details It is important to call Fifo_Stop() prior to this functions. The low-level driver
244  *           must be stopped as well to avoid concurrent access to message objects.
245  *  \param   self   The instance
246  */
247 void Fifo_Cleanup(CPmFifo *self)
248 {
249     CMessage *msg_ptr = NULL;
250     CDlNode *node_ptr = NULL;
251
252     TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (self->sync_state == FIFO_S_UNSYNCED_INIT));
253     TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_Cleanup(): FIFO: %u", 1U, self->config.fifo_id));
254
255     /* cleanup pending queue */
256     for (node_ptr = Dl_PopHead(&self->tx.pending_q); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.pending_q))
257     {
258         msg_ptr = (CMessage*)Dln_GetData(node_ptr);
259
260         Msg_NotifyTxStatus(msg_ptr, UCS_MSG_STAT_ERROR_SYNC);
261         Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(msg_ptr));
262         Msg_SetLldHandle(msg_ptr, NULL);                    /* remove link to LLD message object */
263     }
264
265     /* cleanup waiting queue */
266     for (node_ptr = Dl_PopHead(&self->tx.waiting_queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.waiting_queue))
267     {
268         msg_ptr = (CMessage*)Dln_GetData(node_ptr);
269
270         Msg_NotifyTxStatus(msg_ptr, UCS_MSG_STAT_ERROR_SYNC);
271     }
272
273     /* cleanup Rx queue */
274     for (node_ptr = Dl_PopHead(&self->rx.queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->rx.queue))
275     {
276         msg_ptr = (CMessage*)Dln_GetData(node_ptr);
277
278         Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr);
279     }
280
281     Srv_ClearEvent(&self->service, FIFO_SE_ALL);
282 }
283
284
285 /*! \brief   Service function of FIFO
286  *  \details The processing order of Rx followed by Tx is important for Fifo_RxProcessCommand()
287  *  \param   self   The instance
288  */
289 static void Fifo_Service(void *self)
290 {
291     CPmFifo *self_ = (CPmFifo*)self;
292     Srv_Event_t event_mask;
293
294     Srv_GetEvent(&self_->service, &event_mask);
295
296     if(FIFO_SE_RX_SERVICE == (event_mask & FIFO_SE_RX_SERVICE))     /* Is event pending? */
297     {
298         Srv_ClearEvent(&self_->service, FIFO_SE_RX_SERVICE);
299         Fifo_RxService(self_);
300     }
301
302     if((event_mask & FIFO_SE_TX_APPLY_STATUS) == FIFO_SE_TX_APPLY_STATUS)
303     {
304         Srv_ClearEvent(&self_->service, FIFO_SE_TX_APPLY_STATUS);
305         Fifo_TxApplyCurrentStatus(self_);
306     }
307
308     if(FIFO_SE_TX_SERVICE == (event_mask & FIFO_SE_TX_SERVICE))     /* Is event pending? */
309     {
310         Srv_ClearEvent(&self_->service, FIFO_SE_TX_SERVICE);
311         Fifo_TxService(self_);
312     }
313 }
314
315 /*------------------------------------------------------------------------------------------------*/
316 /* Tx Implementation                                                                              */
317 /*------------------------------------------------------------------------------------------------*/
318 /*! \brief   Enqueues a message for transmission
319  *  \param   self    The instance
320  *  \param   msg_ptr The Tx message object
321  *  \param   bypass  Use \c true if the message shall bypass all other messages
322  *                   in the FIFO. Otherwise \c false.
323  */
324 void Fifo_Tx(CPmFifo *self, CMessage *msg_ptr, bool bypass)
325 {
326     uint8_t *msg_hdr_ptr = NULL;
327
328     TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (msg_ptr != NULL));
329     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));
330
331     Msg_PullHeader(msg_ptr, self->tx.encoder_ptr->msg_hdr_sz);
332     msg_hdr_ptr = Msg_GetHeader(msg_ptr);
333     self->tx.encoder_ptr->encode_fptr(Msg_GetMostTel(msg_ptr), msg_hdr_ptr);
334
335     if (bypass == false)
336     {
337         Dl_InsertTail(&self->tx.waiting_queue, Msg_GetNode(msg_ptr));       /* enqueue message for asynchronous transmission */
338     }
339     else
340     {
341         Fifo_TxEnqueueBypassMsg(self, &self->tx.waiting_queue, msg_ptr);    /* queue before first non-bypass message */
342     }
343
344     Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE);
345 }
346
347 /*! \brief   Enqueues a bypass message between the last bypass and the first regular message in a queue
348  *  \param   self    The instance
349  *  \param   q_ptr   The message queue
350  *  \param   msg_ptr The Tx message object
351  */
352 static void Fifo_TxEnqueueBypassMsg(CPmFifo *self, CDlList *q_ptr, CMessage *msg_ptr)
353 {
354     CDlNode *node_ptr = Dl_Foreach(q_ptr, &Fifo_FindFirstRegularMsg, NULL); /* find first "non-bypass" message */
355     Msg_SetTxBypass(msg_ptr, true);                                         /* mark new message as bypass message */
356
357     if (node_ptr == NULL)                                                   /* no message or only bypass messages found */
358     {                               
359         Dl_InsertTail(&self->tx.waiting_queue, Msg_GetNode(msg_ptr));       /* enqueue message to tail */   
360     }
361     else                                                                    /* first "non-bypass" message is found */
362     {                                                                       /* insert the bypass message before the first regular message found */
363         Dl_InsertBefore(&self->tx.waiting_queue, node_ptr, Msg_GetNode(msg_ptr));
364     }
365 }
366
367 /*! \brief   Required as "for-each" function to find the first "regular message"
368  *  \param   d_ptr   Points to a message object in the queue
369  *  \param   ud_ptr  Unused data reference, always \c NULL
370  *  \return  Returns \c true if a regular (non-bypass) message is found.
371  */
372 static bool Fifo_FindFirstRegularMsg(void *d_ptr, void *ud_ptr)
373 {
374     bool ret = true;
375     MISC_UNUSED(ud_ptr);
376
377     if (Msg_IsTxBypass((CMessage*)d_ptr))
378     {
379         ret = false;
380     }
381
382     return ret;
383 }
384
385 /*! \brief   Processing of data, status and command messages
386  *  \param   self    The instance
387  */
388 static void Fifo_TxService(CPmFifo *self)
389 {
390     Fifo_TxProcessCommand(self);
391     Fifo_TxProcessStatus(self);
392     Fifo_TxProcessData(self);
393 }
394
395 /*! \brief   Processing of status messages
396  *  \param   self    The instance
397  */
398 static void Fifo_TxProcessStatus(CPmFifo *self)
399 {
400     if (Pmcmd_IsTriggered(&self->rx.status) != false)
401     {
402         if (Pmcmd_Reserve(&self->rx.status) != false)
403         {
404             Pmcmd_SetTrigger(&self->rx.status, false);
405             self->rx.ack_last_ok_sid = (self->rx.expected_sid - self->rx.busy_num) - 1U;
406             self->rx.wait_processing = false;
407
408             if (self->rx.busy_num == 0U)                /* currently no processing of data messages active */
409             {                                           /* notify the latest with SUCCESS */
410                 Pmcmd_UpdateContent(&self->rx.status, self->rx.expected_sid - 1U, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_SUCCESS);
411             }
412             else                                        /* message processing is active */
413             {                                           /* notify code busy according to remaining credits */
414                 Pmcmd_UpdateContent(&self->rx.status, self->rx.expected_sid - self->rx.busy_num, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_BUSY);
415             }
416
417             Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->rx.status));
418         }
419     }
420 }
421
422 /*! \brief   Processing of queued data messages
423  *  \param   self    The instance
424  */
425 static void Fifo_TxProcessData(CPmFifo *self)
426 {
427     /* process all queued messages as long as credits are available,
428      * process all queued messages if FIFO is not synced 
429      */
430     while ((self->tx.cancel_all_running == false) && (self->tx.credits > 0U))
431     {
432         CMessage *msg_ptr = NULL;
433         CDlNode *node_ptr = NULL;
434         uint8_t *msg_hdr_ptr = NULL;
435         Lld_IntTxMsg_t *lld_tx_ptr = NULL;
436
437         node_ptr = Dl_PopHead(&self->tx.waiting_queue);             /* get message node */
438         if (node_ptr == NULL)
439         {
440             msg_ptr = NULL;                                         /* stop processing - no further messages in queue */
441             break;
442         }
443
444         msg_ptr = (CMessage*)Dln_GetData(node_ptr);                 /* get message object */
445
446         if (self->sync_state != FIFO_S_SYNCED)
447         {
448             Msg_NotifyTxStatus(msg_ptr, UCS_MSG_STAT_ERROR_SYNC);     /* notify sync error while not synced */
449         }
450         else
451         {
452             lld_tx_ptr = Lldp_GetTxFromPool(&self->tx.lld_pool);
453             TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (msg_ptr != NULL));
454             TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (lld_tx_ptr != NULL));
455             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));
456
457             Msg_SetLldHandle(msg_ptr, lld_tx_ptr);                     /* link message objects */
458             lld_tx_ptr->msg_ptr = msg_ptr;
459
460             Msg_PullHeader(msg_ptr, self->tx.encoder_ptr->pm_hdr_sz);  /* get PM header pointer */
461             msg_hdr_ptr = Msg_GetHeader(msg_ptr);
462
463             {
464                 uint8_t tel_length = Msg_GetMostTel(msg_ptr)->tel.tel_len;
465                 self->tx.pm_header.pml = (Msg_GetHeaderSize(msg_ptr) + tel_length) - 2U;
466             }
467
468             self->tx.pm_header.sid = self->tx.sid_next_to_use;        /* assign SeqID */
469             self->tx.sid_next_to_use++;
470
471             Pmh_BuildHeader(&self->tx.pm_header, msg_hdr_ptr);        /* build PM header */
472             lld_tx_ptr->lld_msg.memory_ptr = Msg_GetMemTx(msg_ptr);
473
474             Msg_SetTxActive(msg_ptr, true);
475             Dl_InsertTail(&self->tx.pending_q, Msg_GetNode(msg_ptr));
476
477             Pmch_Transmit(self->init.channel_ptr, (Ucs_Lld_TxMsg_t*)(void*)lld_tx_ptr);
478
479             self->tx.credits--;
480         }
481     }
482 }
483
484 /*! \brief   Processing of status messages
485  *  \param   self    The instance
486  */
487 static void Fifo_TxProcessCommand(CPmFifo *self)
488 {
489     if (Pmcmd_IsTriggered(&self->tx.sync_cmd) != false)
490     {
491         if (Pmcmd_Reserve(&self->tx.sync_cmd) != false)
492         {
493             Pmcmd_SetTrigger(&self->tx.sync_cmd, false);
494
495             if (self->sync_state == FIFO_S_SYNCING)
496             {
497                 self->sync_cnt++;
498                 Pmcmd_SetContent(&self->tx.sync_cmd, self->sync_cnt, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_SYNC, self->sync_params, 4U);
499                 Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.sync_cmd));
500             }
501             else if (self->sync_state == FIFO_S_UNSYNCING)
502             {
503                 Pmcmd_SetContent(&self->tx.sync_cmd, 0U, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_UNSYNC, NULL, 0U);
504                 Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.sync_cmd));
505             }
506             else
507             {
508                 Pmcmd_Release(&self->tx.sync_cmd);
509                 TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");
510             }
511         }
512         else
513         {
514             TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");
515         }
516     }
517 }
518
519 /*! \brief      Releases a LLD Tx message object
520  *  \param      self        The instance
521  *  \param      handle_ptr  The unused LLD Tx message object 
522  *  \details    If Fifo_TxApplyStatus() is waiting for a message object 
523  *              being released 
524  */
525 void Fifo_TxOnRelease(void *self, Ucs_Lld_TxMsg_t *handle_ptr)
526 {
527     CPmFifo *self_ = (CPmFifo*)self;
528     Lld_IntTxMsg_t *tx_ptr = (Lld_IntTxMsg_t*)(void*)handle_ptr;
529
530     if (tx_ptr->msg_ptr != NULL)
531     {
532         Msg_SetTxActive(tx_ptr->msg_ptr, false);
533     }
534     else
535     {
536         TR_FAILED_ASSERT(self_->init.base_ptr->ucs_user_ptr, "[FIFO]");
537     }
538
539     if (self_->tx.status_waiting_release != false)
540     {
541         self_->tx.status_waiting_release = false;
542         Srv_SetEvent(&self_->service, (FIFO_SE_TX_APPLY_STATUS | FIFO_SE_TX_SERVICE));
543     }
544 }
545
546 /*! \brief   Triggers a command CANCEL_ALL and stops further Tx processing
547  *  \details CANCEL_ALL shall be called only, if the front-most pending message 
548  *           has followers (is segmented, i.e. \c cancel_id > 0). Use command CANCEL
549  *           if the front-most message has no followers (\c cancel_id == NULL).
550  *  \param   self           The instance
551  *  \param   failure_sid    The failure sid
552  *  \param   failure_code   The failure code reported by the INIC
553  */
554 static void Fifo_TxExecuteCancelAll(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code)
555 {
556     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));
557
558     if (Pmcmd_Reserve(&self->tx.cancel_cmd) != false)                   /* prepare cancel command */
559     {
560         Pmcmd_UpdateContent(&self->tx.cancel_cmd, self->tx.current_sid, 
561                             PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL_ALL);
562         Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.cancel_cmd));
563     }
564     else
565     {
566         TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");   /* Unable to reserve cancel command */
567     }
568
569     self->tx.cancel_all_running = true;
570     self->tx.failure_sid = failure_sid;
571     self->tx.failure_status = failure_code;
572 }
573
574 /*! \brief   Shall be called if the command CANCEL_ALL was processed completely
575  *  \param   self           The instance
576  *  \details Since the CANCEL_ALL is used to cancel the front-most message and
577  *           all of its followers (same cancel_id)
578
579  for mid-level retries, the canceled messages
580  *           are moved from the processing_q to the waiting_q again. The MLR timer is
581  *           started. As soon as the timer elapses, Tx processing is continued again.
582  *           If the front-most message has a follower id, all pending messages are 
583  *           moved to the waiting queue and all messages with the same follower id 
584  *           are notified as failed.
585  */
586 static void Fifo_TxFinishedCancelAll(CPmFifo *self)
587 {
588     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));
589
590     if (self->tx.failure_status != 0U)                          /* avoid multiple execution of the same CANCELED status */
591     {                                                           /* and all of its followers */
592         uint8_t follower_id = Fifo_TxPendingGetFollowerId(self);
593         Fifo_TxRestorePending(self);                            /* move remaining messages to waiting_q */
594         Fifo_TxCancelFollowers(self, follower_id, (Ucs_MsgTxStatus_t)self->tx.failure_status);
595                                                                 /* notify front-most and message and all of its followers */
596         self->tx.cancel_all_running = false;                    /* continue with Tx processing */
597         self->tx.failure_sid = 0U;
598         self->tx.failure_status = 0U;
599         Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE);
600     }
601 }
602
603 /*! \brief   Triggers a command CANCEL while Tx processing continues
604  *  \param   self           The instance
605  *  \param   failure_sid    The failure sid
606  *  \param   failure_code   The failure code reported by the INIC
607  */
608 static void Fifo_TxExecuteCancel(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code)
609 {
610     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));
611
612     if (Pmcmd_Reserve(&self->tx.cancel_cmd) != false)
613     {
614         Pmcmd_UpdateContent(&self->tx.cancel_cmd, self->tx.current_sid, 
615                             PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL);
616         Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.cancel_cmd));
617     }
618     else
619     {
620         TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");   /* Unable to reserve cancel command */
621     }
622
623     self->tx.cancel_all_running = false;
624     self->tx.failure_sid = failure_sid;
625     self->tx.failure_status = failure_code;
626 }
627
628 /*! \brief   Checks if the LLD has released all messages in the pending_q
629  *  \param   self           The instance
630  *  \return  Returns \c true if all messages are released by the LLD, otherwise \c false.
631  */
632 static bool Fifo_TxHasAccessPending(CPmFifo *self)
633 {
634     bool ret = true;
635     CDlNode *node_ptr = Dl_PeekTail(&self->tx.pending_q);           /* if the tail is not active, then all */
636                                                                     /* pending message are not active */
637     if (node_ptr != NULL)
638     {
639         CMessage *msg_ptr = (CMessage*)Dln_GetData(node_ptr);
640
641         if (Msg_IsTxActive(msg_ptr) != false)
642         {
643             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));
644             self->tx.status_waiting_release = true;
645             ret = false;
646         }
647     }
648
649     return ret;
650 }
651
652 /*! \brief   Moves all pending messages to the waiting_q
653  *  \details All messages from pending_q will be moved to the waiting_g and 
654  *           all consumed credits are restored. The message objects are restored
655  *           to the queue in the same order as they have been forwarded to the LLD.
656  *           This method is typically called to restore the waiting_q in the correct
657  *           order before notifying a 
658  *  \param   self           The instance
659  */
660 static void Fifo_TxRestorePending(CPmFifo *self)
661 {
662     /* take tail from pending_q to the head of waiting_q */
663     CMessage *msg_ptr = NULL;
664     CDlNode *node_ptr = NULL;
665
666     /* cleanup pending queue */
667     for (node_ptr = Dl_PopTail(&self->tx.pending_q); node_ptr != NULL; node_ptr = Dl_PopTail(&self->tx.pending_q))
668     {
669         msg_ptr = (CMessage*)Dln_GetData(node_ptr);
670
671         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));
672         TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (Msg_IsTxActive(msg_ptr) == false));
673
674         self->tx.sid_last_completed++;
675         self->tx.credits++;
676         Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(msg_ptr));
677         Msg_SetLldHandle(msg_ptr, NULL);                            /* remove link to LLD message object */
678         Msg_PushHeader(msg_ptr, self->tx.encoder_ptr->pm_hdr_sz);   /* set index to position of message header */
679         Dl_InsertHead(&self->tx.waiting_queue, node_ptr);           /* enqueue message to waiting_q */
680     }
681 }
682
683 /*! \brief   Retrieves the follower id of the front-most pending message
684  *  \param   self           The instance
685  *  \return  Returns the follower id of the front-most pending message.
686  */
687 static uint8_t Fifo_TxPendingGetFollowerId(CPmFifo *self)
688 {
689     CDlNode *node_ptr;
690     CMessage *tx_ptr;
691     uint8_t ret = 0U;
692
693     node_ptr = Dl_PeekHead(&self->tx.pending_q);
694     TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (node_ptr != NULL));
695
696     if (node_ptr != NULL)
697     {
698         tx_ptr = (CMessage*)Dln_GetData(node_ptr);
699         ret = tx_ptr->pb_msg.opts.cancel_id;
700     }
701
702     return ret;
703 }
704
705 /*! \brief  Aborts the transmission of all messages in the waiting_q with a given follower id
706  *  \param  self          The instance
707  *  \param  follower_id   The follower id a message needs to have to be canceled
708  *  \param  status        The transmission status that shall be notified 
709  */
710 static void Fifo_TxCancelFollowers(CPmFifo *self, uint8_t follower_id, Ucs_MsgTxStatus_t status)
711 {
712     CDlNode *node_ptr;
713     CDlList temp_queue;
714
715     Dl_Ctor(&temp_queue, self->init.base_ptr->ucs_user_ptr);
716     TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_TxCancelFollowers(): FIFO: %u: FollowerId: %u", 2U, self->config.fifo_id, follower_id));
717
718     for (node_ptr = Dl_PopHead(&self->tx.waiting_queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.waiting_queue))
719     {
720         CMessage *tx_ptr = (CMessage*)Dln_GetData(node_ptr);
721
722         TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (Msg_GetLldHandle(tx_ptr) == NULL));
723
724         if (tx_ptr->pb_msg.opts.cancel_id == follower_id)
725         {
726             Msg_NotifyTxStatus(tx_ptr, status);             /* notify failed transmission of message and all followers */
727         }
728         else
729         {
730             Dl_InsertTail(&temp_queue, node_ptr);           /* add to temporary queue and keep order of messages */
731         }
732     }
733
734     if (Dl_GetSize(&temp_queue) > 0U)                       /* restore temp_queue to waiting_q */
735     {
736         Dl_AppendList(&self->tx.waiting_queue, &temp_queue);/* temp_queue will be empty now */
737     }
738 }
739
740 /*------------------------------------------------------------------------------------------------*/
741 /* Tx Message Processing                                                                          */
742 /*------------------------------------------------------------------------------------------------*/
743 /*! \brief  Retrieves the number of (implicit) acknowledges that are related to one SID
744  *  \param  self    The instance
745  *  \param  sid     The sequence ID
746  *  \return The number of implicit acknowledges that are related to the SID
747  */
748 static uint8_t Fifo_TxGetValidAcknowledges(CPmFifo *self, uint8_t sid)
749 {
750     uint8_t diff_s = (uint8_t)(sid - self->tx.sid_last_completed);                          /* number of implicit acknowledged data */
751     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 */
752
753     if (diff_b <= diff_s)                                                                   /* check valid acknowledges */
754     {
755         diff_s = 0U;
756     }
757
758     return diff_s;
759 }
760
761
762 /*! \brief  Checks id an incoming SID of a status message is valid.
763  *  \param  self The instance
764  *  \param  sid  The sequence ID
765  *  \return Returns \c true if the SID is valid, otherwise \c false.
766  */
767 static bool Fifo_TxIsIncomingSidValid(CPmFifo *self, uint8_t sid)
768 {
769     bool ret = false;
770     uint8_t diff_s = (uint8_t)(sid - self->tx.sid_last_completed);                          /* number of implicit acknowledged data */
771     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 */
772     uint8_t diff_p = (uint8_t)(self->tx.current_sid - self->tx.sid_last_completed);         /* pending/known acknowledges */
773
774     if (diff_b > diff_s)                                                                    /* check if SID fits in valid range */
775     {
776         if (diff_s >= diff_p)                                                               /* avoid overwriting with smaller values */
777         {
778             ret = true;
779         }
780     }
781
782     return ret;
783 }
784
785 /*! \brief  Implicitly notifies transmission status to calling classes
786  *  \param  self    The instance
787  *  \param  sid     The sequence ID until the status shall be notified
788  *  \param  status  The status which is notified
789  *  \return Returns \c true if all desired messages had been notified, 
790  *          otherwise \c false.
791  */
792 static bool Fifo_TxNotifyStatus(CPmFifo *self, uint8_t sid, Ucs_MsgTxStatus_t status)
793 {
794     bool ret = true;
795     uint8_t acks = Fifo_TxGetValidAcknowledges(self, sid);
796
797     TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, calculated_acks: %u", 2U, self->config.fifo_id, acks));
798
799     while (acks > 0U)
800     {
801         CDlNode *node_ptr = Dl_PopHead(&self->tx.pending_q);
802
803         if (node_ptr != NULL)
804         {
805             CMessage *tx_ptr = (CMessage*)node_ptr->data_ptr;
806
807             if (!Msg_IsTxActive(tx_ptr))
808             {
809                 TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (tx_ptr != NULL));
810                 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));
811                 Msg_NotifyTxStatus(tx_ptr, status);
812                 Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(tx_ptr));
813                 Msg_SetLldHandle(tx_ptr, NULL);                             /* remove link to LLD message object */
814
815                 self->tx.credits++;                                         /* increment credits */
816                 self->tx.sid_last_completed++;                              /* update last acknowledge SID */
817             }
818             else
819             {
820                 TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, LLD objects still occupied", 1U, self->config.fifo_id));
821                 Dl_InsertHead(&self->tx.pending_q, node_ptr);
822                 self->tx.status_waiting_release = true;
823                 ret = false;
824                 break;
825             }
826         }
827         else
828         {
829             TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");   /* not yet handled */
830                                                                             /* trigger sync again */
831         }
832
833         acks--;
834     }
835
836     return ret;
837 }
838
839 /*! \brief  Updates the current Tx status with the content of a received FIFO status
840  *  \param  self    The instance
841  *  \param  sid     The sequence id of the FIFO status
842  *  \param  type    The type of the FIFO status. Valid types are only:
843  *                  - PMP_STATUS_TYPE_FLOW
844  *                  - PMP_STATUS_TYPE_FAILURE
845  *  \param  code    The code of the FIFO status
846  */
847 static void Fifo_TxUpdateCurrentStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code)
848 {
849     TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (type == (uint8_t)PMP_STATUS_TYPE_FAILURE) || (type == (uint8_t)PMP_STATUS_TYPE_FLOW));
850     if (Fifo_TxIsIncomingSidValid(self, sid))               /* is new or updating status */
851     {
852         self->tx.current_sid = sid;                         /* update current status */
853         self->tx.current_type = (Pmp_StatusType_t)type;
854         self->tx.current_code = code;
855     }
856     else
857     {
858         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));
859     }
860 }
861
862 /*! \brief  Analyses the current Tx status, tries to notify statuses to the transmitter and triggers
863  *          retry/cancel actions.
864  *  \param  self    The instance
865  */
866 static void Fifo_TxApplyCurrentStatus(CPmFifo *self)
867 {
868     if ((self->tx.cancel_all_running == false) && (self->tx.failure_status != 0U))      /* Command(CANCEL) is pending */
869     {
870         if (Fifo_TxGetValidAcknowledges(self, self->tx.current_sid) > 1U)               /* ?>=1? "single cancel" is valid and implicit */
871         {
872             if (Fifo_TxNotifyStatus(self, self->tx.failure_sid, (Ucs_MsgTxStatus_t)self->tx.failure_status))
873             {
874                 self->tx.failure_status = 0U;                                           /* implicit canceled stops retries */
875                 self->tx.failure_sid = 0U;
876             }
877         }
878     }
879
880     if ((self->tx.current_type == PMP_STATUS_TYPE_FAILURE) && (self->tx.status_waiting_release == false))
881     {
882         if (self->tx.cancel_all_running == false)
883         {
884             if (Fifo_TxNotifyStatus(self, self->tx.current_sid - 1U, UCS_MSG_STAT_OK) != false)
885             {
886                 /* important: failed message now is front-most message in the tx.pending_q, */
887                 /*            any implicit acknowledge was done before */
888                 if (self->tx.failure_status == 0U)                  /* failure not yet handled - avoid multiple calls */
889                 {
890                     if (Fifo_TxPendingGetFollowerId(self) == 0U)
891                     {
892                         Fifo_TxExecuteCancel(self, self->tx.current_sid, self->tx.current_code);    /* execute simple cancel */
893                     }
894                     else
895                     {
896                         Fifo_TxExecuteCancelAll(self, self->tx.current_sid, self->tx.current_code); /* execute cancel all */
897                         /* self->tx.cancel_all_running now is 'true' and Tx is stopped */
898                     }
899                 }
900             }
901         }
902     }
903
904     if ((self->tx.current_type == PMP_STATUS_TYPE_FLOW) && (self->tx.status_waiting_release == false))
905     {
906         if ((uint8_t)PMP_STATUS_CODE_SUCCESS == self->tx.current_code)                           /* acknowledge pending messages */
907         {
908                                                                    /* no further retries possible */
909             (void)Fifo_TxNotifyStatus(self, self->tx.current_sid, UCS_MSG_STAT_OK);
910         }
911         else if ((uint8_t)PMP_STATUS_CODE_CANCELED == self->tx.current_code)
912         {
913             if (self->tx.cancel_all_running != false)
914             {
915                 /* wait until the last SID is notified */
916                 if (self->tx.current_sid == (uint8_t)(self->tx.sid_next_to_use - (uint8_t)1U))
917                 {
918                     /* cancel done if none of pending messages is active */
919                     if (Fifo_TxHasAccessPending(self) != false)
920                     {
921                         Fifo_TxFinishedCancelAll(self);
922                     }
923                 }
924             }
925             else if (Fifo_TxNotifyStatus(self, self->tx.current_sid, (Ucs_MsgTxStatus_t)self->tx.failure_status))
926             {
927                 self->tx.failure_status = 0U;
928                 self->tx.failure_sid = 0U;
929             }
930         }
931         else 
932         {
933             if (Fifo_TxNotifyStatus(self, self->tx.current_sid - 1U, UCS_MSG_STAT_OK)) /* just implicitly acknowledge preceding message */
934             {
935                 if ((uint8_t)PMP_STATUS_CODE_NACK == self->tx.current_code)
936                 {
937                     Fifo_Stop(self, FIFO_S_UNSYNCED_INIT, true);
938                     TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");
939                 }
940             }
941         }
942     }
943 }
944
945 /*------------------------------------------------------------------------------------------------*/
946 /* Rx Implementation                                                                              */
947 /*------------------------------------------------------------------------------------------------*/
948 /*! \brief  Receives a message on the respective FIFO 
949  *  \param  self    The instance
950  *  \param  msg_ptr Reference to the Rx message
951  */
952 static void Fifo_OnRx(void *self, CMessage *msg_ptr)
953 {
954     CPmFifo *self_ = (CPmFifo*)self;
955     Dl_InsertTail(&self_->rx.queue, Msg_GetNode(msg_ptr));      /* enqueue in rx_queue */
956     Srv_SetEvent(&self_->service, (FIFO_SE_RX_SERVICE | FIFO_SE_TX_APPLY_STATUS | FIFO_SE_TX_SERVICE));
957 }
958
959 /*! \brief  Processes the Rx queue completely and triggers possible Tx events
960  *  \param  self    The instance
961  */
962 static void Fifo_RxService(CPmFifo *self)
963 {
964     while (self->rx.wait_processing == false)                    /* process all Rx messages if possible */
965     {
966         CMessage *msg_ptr;
967         uint8_t *header_ptr;
968         Pmp_MsgType_t type;
969         bool ok;
970
971         bool free_msg = true;                                   /* default: free every status or command message */
972         CDlNode *node_ptr = Dl_PopHead(&self->rx.queue);
973
974         if (node_ptr == NULL)
975         {
976             msg_ptr = NULL;                                     /* stop processing - no further messages in queue */
977             break;
978         }
979
980         msg_ptr = (CMessage*)node_ptr->data_ptr;
981         header_ptr = Msg_GetHeader(msg_ptr);
982         type = Pmp_GetMsgType(header_ptr);
983         ok = Pmp_VerifyHeader(header_ptr, MSG_SIZE_RSVD_BUFFER);
984
985         if (ok != false)
986         {
987             switch (type)
988             {
989                 case PMP_MSG_TYPE_CMD:
990                     Fifo_RxProcessCommand(self, msg_ptr);
991                     break;
992                 case PMP_MSG_TYPE_STATUS:
993                     Fifo_RxProcessStatus(self, msg_ptr);
994                     break;
995                 case PMP_MSG_TYPE_DATA:
996                     free_msg = Fifo_RxProcessData(self, msg_ptr);                   /* important: message can be freed */
997                     break;                                                          /*            synchronously */
998                 default:
999                     TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");   /* unknown FIFO message type */
1000                     break;
1001             }
1002         }
1003         else
1004         {
1005             TR_FAILED_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]");           /* invalid message header */
1006         }
1007
1008         if (free_msg != false)
1009         {
1010             Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr);
1011         }
1012     }
1013 }
1014
1015 /*! \brief   Evaluates the trigger condition to transmit a Rx status
1016  *  \details Needs to be called before and after processing Rx data messages
1017  *  \param   self    The instance
1018  */
1019 static void Fifo_RxCheckStatusTrigger(CPmFifo *self)
1020 {
1021     /* calculate the number of credits the INIC has consumed */
1022     /* if less messages are processing, the freed can be acknowledged */
1023     uint8_t consumed_inic_credits = (self->rx.expected_sid - self->rx.ack_last_ok_sid) - 1U;
1024     uint8_t possible_acks = consumed_inic_credits - self->rx.busy_num;
1025
1026     if ((consumed_inic_credits >= self->rx.ack_threshold) && (possible_acks > 0U))
1027     {
1028         if (Pmcmd_IsTriggered(&self->rx.status) == false)
1029         {
1030             Pmcmd_SetTrigger(&self->rx.status, true);       /* INIC might run out of credits */ 
1031             Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE);
1032         }
1033     }
1034 }
1035
1036 /*! \brief  This function shall be called before processing a valid FIFO data message
1037  *  \param  self    The instance
1038  */
1039 static void Fifo_RxGetCredit(CPmFifo *self)
1040 {
1041     self->rx.busy_num++;
1042     Fifo_RxCheckStatusTrigger(self);
1043 }
1044
1045 /*! \brief   This function shall be called after processing a valid FIFO data message
1046  *  \details It is important to call this function after the message object is freed,
1047  *           so that the flow control can be updated.
1048  *  \param   self    The instance
1049  */
1050 static void Fifo_RxReleaseCredit(CPmFifo *self)
1051 {
1052     self->rx.busy_num--;
1053     Fifo_RxCheckStatusTrigger(self);
1054 }
1055
1056 /*! \brief   Releases a FIFO data message which was received and forwarded by the FIFO
1057  *  \details The function returns the message to the channel's Rx message pool and
1058  *           has to update the number of credits (processing handles). 
1059  *           A FIFO data message is initially allocated from the channel's Rx message pool.
1060  *           When processing the handle the determined FIFO need to calculate the amount of 
1061  *           credits. When freeing the message the handle needs to be returned to the channel's
1062  *           Rx pool again and the FIFO needs to refresh the status and credits calculation.
1063  *           Therefore the message has to be freed to the respective FIFO again.
1064  *  \param   self    The instance
1065   * \param  msg_ptr The Rx data message
1066  */
1067 void Fifo_RxReleaseMsg(CPmFifo *self, CMessage *msg_ptr)
1068 {
1069     Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr);
1070     Fifo_RxReleaseCredit(self);
1071 }
1072
1073 /*! \brief  Processes an Rx data message
1074  *  \param  self    The instance
1075  *  \param  msg_ptr The Rx data message
1076  *  \return \c true if the message object is no longer needed.
1077  *          Otherwise \c false.
1078  */
1079 static bool Fifo_RxProcessData(CPmFifo *self, CMessage *msg_ptr)
1080 {
1081     bool free_msg = true;
1082     uint8_t content_header_sz = 0U;
1083     uint8_t sid = 0U;
1084     uint8_t *header_ptr = Msg_GetHeader(msg_ptr);
1085     sid = Pmp_GetSid(header_ptr);
1086
1087     if (self->sync_state != FIFO_S_SYNCED)
1088     {                                                       /* discard Rx messages while FIFO is not synced */
1089         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));
1090     }
1091     else if (sid == self->rx.expected_sid)                       /* check if SID is ok */
1092     {
1093         uint8_t pm_header_sz = Pmp_GetPmhl(header_ptr) + 3U;
1094         TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (pm_header_sz == self->rx.encoder_ptr->pm_hdr_sz));
1095
1096         self->rx.expected_sid++;                            /* update SID */
1097         content_header_sz = self->rx.encoder_ptr->msg_hdr_sz;
1098
1099         /* parasoft suppress item MISRA2004-17_4 reason "necessary offset usage" */
1100         self->rx.encoder_ptr->decode_fptr(Msg_GetMostTel(msg_ptr), &(header_ptr[pm_header_sz]));
1101         /* parasoft unsuppress item MISRA2004-17_4 reason "necessary offset usage" */
1102
1103         Msg_ReserveHeader(msg_ptr, content_header_sz + pm_header_sz);
1104         Msg_PullHeader(msg_ptr, content_header_sz + pm_header_sz);
1105
1106         if (Msg_VerifyContent(msg_ptr))
1107         {
1108             if (self->rx.on_complete_fptr != NULL)
1109             {
1110                 (void)Fifo_RxGetCredit(self);
1111                 free_msg =  false;                              /* callback is responsible to free the message  */
1112                 self->rx.on_complete_fptr(self->rx.on_complete_inst, msg_ptr);
1113                                                                 /* Fifo_RxReleaseCredit() is called when message is freed */
1114             }
1115         }
1116     }
1117     else
1118     {
1119         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));
1120     }
1121
1122     return free_msg;
1123 }
1124
1125 /*! \brief  Processes an Rx status message
1126  *  \param  self    The instance
1127  *  \param  msg_ptr The Rx status message
1128  */
1129 static void Fifo_RxProcessStatus(CPmFifo *self, CMessage *msg_ptr)
1130 {
1131     CPmh pm_header;
1132     uint8_t current_sid;
1133     uint8_t current_type;
1134     uint8_t current_code;
1135     uint8_t *header_ptr = Msg_GetHeader(msg_ptr);
1136
1137     Pmh_DecodeHeader(&pm_header, header_ptr);
1138     current_sid = pm_header.sid;
1139     current_type = (uint8_t)Pmh_GetExtStatusType(&pm_header);
1140     current_code = (uint8_t)Pmh_GetExtStatusCode(&pm_header);
1141
1142     self->wd.request_started = false;                                                   /* status finishes a wd request */
1143
1144     switch ((Pmp_StatusType_t)current_type)
1145     {
1146         case PMP_STATUS_TYPE_FAILURE:
1147             Fifo_TxUpdateCurrentStatus(self, current_sid, current_type, Fifo_RxCheckFailureCode(self, current_code));  /* just update status type FAILURE */
1148             break;
1149         case PMP_STATUS_TYPE_FLOW:
1150             Fifo_TxUpdateCurrentStatus(self, current_sid, current_type, current_code);  /* just update status type FLOW (codes: BUSY, NACK, SUCCESS, CANCELED) */
1151             break;
1152         case PMP_STATUS_TYPE_SYNCED:
1153             Fifo_RxProcessSyncStatus(self, current_sid, current_type, current_code, header_ptr);
1154             break;
1155         case PMP_STATUS_TYPE_UNSYNCED_BSY:
1156             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));
1157             if (self->sync_state != FIFO_S_SYNCING)
1158             {
1159                 Fifo_Stop(self, FIFO_S_UNSYNCED_BUSY, true);
1160             }
1161             break;
1162         case PMP_STATUS_TYPE_UNSYNCED_RDY:
1163             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));
1164             if (self->sync_state == FIFO_S_SYNCING)
1165             {
1166                 if (current_code == (uint8_t)PMP_UNSYNC_R_COMMAND)
1167                 {
1168                     Fifo_Synchronize(self);                 /* retry synchronization */
1169                 }
1170             }
1171             else
1172             {
1173                 Fifo_Stop(self, FIFO_S_UNSYNCED_READY, true);
1174             }
1175             break;
1176         default:
1177             /* ignore status */
1178             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));
1179             break;
1180     }
1181 }
1182
1183 /*! \brief  Checks failure_code and sets invalid code to UCS_MSG_STAT_ERROR_UNKNOWN
1184  *  \param  self         The instance
1185  *  \param  failure_code The INIC failure code
1186  *  \return Returns the checked failure code
1187  */
1188 static uint8_t Fifo_RxCheckFailureCode(CPmFifo *self, uint8_t failure_code)
1189 {
1190     uint8_t ret;
1191     MISC_UNUSED(self);
1192
1193     switch (failure_code)
1194     {
1195         case (uint8_t)UCS_MSG_STAT_ERROR_CFG_NO_RCVR:
1196         case (uint8_t)UCS_MSG_STAT_ERROR_BF:
1197         case (uint8_t)UCS_MSG_STAT_ERROR_CRC:
1198         case (uint8_t)UCS_MSG_STAT_ERROR_ID:
1199         case (uint8_t)UCS_MSG_STAT_ERROR_ACK:
1200         case (uint8_t)UCS_MSG_STAT_ERROR_TIMEOUT:
1201         case (uint8_t)UCS_MSG_STAT_ERROR_FATAL_WT:
1202         case (uint8_t)UCS_MSG_STAT_ERROR_FATAL_OA:
1203         case (uint8_t)UCS_MSG_STAT_ERROR_NA_TRANS:
1204         case (uint8_t)UCS_MSG_STAT_ERROR_NA_OFF:
1205             ret = failure_code;
1206             break;
1207         default:
1208             ret = (uint8_t)UCS_MSG_STAT_ERROR_UNKNOWN;
1209             break;
1210     }
1211
1212     return ret;
1213 }
1214
1215 /*! \brief  Processes an Rx command message
1216  *  \param  self    The instance
1217  *  \param  msg_ptr The Rx command message
1218  */
1219 static void Fifo_RxProcessCommand(CPmFifo *self, CMessage *msg_ptr)
1220 {
1221     MISC_UNUSED(msg_ptr);
1222                                                              /* be aware that PMHL might vary */
1223     Pmcmd_SetTrigger(&self->rx.status, true);                /* just trigger latest Rx status now */
1224 }
1225
1226 /*! \brief  Processes a status SYNCED from the INIC
1227  *  \param  self        The instance
1228  *  \param  sid         The sid of the sync status
1229  *  \param  type        The type of the sync status
1230  *  \param  code        The code of the sync status
1231  *  \param  header_ptr  Pointer to the raw port message
1232  *  \return The current synchronization state
1233  */
1234 static void Fifo_RxProcessSyncStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code, uint8_t *header_ptr)
1235 {
1236     bool check = false;
1237     uint8_t tx_credits = 0U;
1238
1239     TR_ASSERT(self->init.base_ptr->ucs_user_ptr, "[FIFO]", (type==(uint8_t)PMP_STATUS_TYPE_SYNCED));
1240     MISC_UNUSED(type);
1241     MISC_UNUSED(code);
1242
1243     if (Pmp_GetDataSize(header_ptr) == 4U)
1244     {
1245         tx_credits = Pmp_GetData(header_ptr, 0U) & (uint8_t)PMP_CREDITS_MASK;
1246
1247         if ((tx_credits >= PMP_CREDITS_MIN) && 
1248             (Pmp_GetData(header_ptr, 1U) == self->sync_params[1]) &&
1249             (Pmp_GetData(header_ptr, 2U) == self->sync_params[2]) &&
1250             (Pmp_GetData(header_ptr, 3U) == self->sync_params[3]) &&
1251             (sid == (self->sync_cnt)))
1252         {
1253             check = true;                                               /* the sync status parameters are correct */
1254         }
1255     }
1256
1257     if ((check != false) && (self->sync_state == FIFO_S_SYNCING))
1258     {
1259         Fifo_InitCounters(self, sid, tx_credits);                       /* values are incremented on each sync attempt */
1260         self->sync_state = FIFO_S_SYNCED;                               /* sync status shall have 4 bytes message body */
1261         self->rx.wait_processing = false;
1262         Fifo_TxStartWatchdog(self);
1263         Sub_Notify(&self->sync_state_subject, &self->config.fifo_id);
1264     }
1265 }
1266
1267 /*------------------------------------------------------------------------------------------------*/
1268 /* Synchronization                                                                                */
1269 /*------------------------------------------------------------------------------------------------*/
1270
1271 /*! \brief  Synchronizes the FIFO
1272  *  \param  self    The instance
1273  */
1274 void Fifo_Synchronize(CPmFifo *self)
1275 {
1276     TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_Synchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state));
1277     self->sync_state           = FIFO_S_SYNCING;
1278     Pmcmd_SetTrigger(&self->tx.sync_cmd, true);
1279     Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE);
1280 }
1281
1282 /*! \brief  Un-synchronizes the FIFO
1283  *  \param  self    The instance
1284  */
1285 void Fifo_Unsynchronize(CPmFifo *self)
1286 {
1287     TR_INFO((self->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_Unsynchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state));
1288     if (self->sync_state != FIFO_S_UNSYNCED_READY)
1289     {
1290         self->sync_state           = FIFO_S_UNSYNCING;
1291         Pmcmd_SetTrigger(&self->tx.sync_cmd, true);
1292         Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE);
1293     }
1294 }
1295
1296 /*! \brief  Retrieves the current synchronization state
1297  *  \param  self    The instance
1298  *  \return The current synchronization state
1299  */
1300 Fifo_SyncState_t Fifo_GetState(CPmFifo *self)
1301 {
1302     return self->sync_state;
1303 }
1304
1305 /*------------------------------------------------------------------------------------------------*/
1306 /* Watchdog                                                                                       */
1307 /*------------------------------------------------------------------------------------------------*/
1308
1309 /*! \brief  Starts the watchdog handling 
1310  *  \param  self    The instance
1311  */
1312 static void Fifo_TxStartWatchdog(CPmFifo *self)
1313 {
1314     self->wd.request_started = false;
1315
1316     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));
1317
1318     if (self->wd.timer_value != 0U)
1319     {
1320         Tm_SetTimer(&self->init.base_ptr->tm, &self->wd.timer, &Fifo_TxOnWatchdogTimer, 
1321                     self, 
1322                     self->wd.timer_value, 
1323                     self->wd.timer_value
1324                     );
1325     }
1326 }
1327
1328 /*! \brief   Callback function which is invoked if the watchdog timer expires 
1329  *  \param   self    The instance
1330  */
1331 static void Fifo_TxOnWatchdogTimer(void *self)
1332 {
1333     CPmFifo *self_ = (CPmFifo*)self;
1334
1335     TR_INFO((self_->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_TxOnWatchdogTimer(): FIFO: %u, state: %u", 2U, self_->config.fifo_id, self_->sync_state));
1336
1337     if (self_->wd.request_started == false)
1338     {
1339         if (Pmcmd_Reserve(&self_->wd.wd_cmd) != false)
1340         {
1341             self_->wd.request_started = true;       /* indicate that a status is expected */
1342             Pmcmd_UpdateContent(&self_->wd.wd_cmd, self_->tx.sid_next_to_use - 1U, PMP_CMD_TYPE_REQ_STATUS, PMP_CMD_CODE_REQ_STATUS);
1343             Pmch_Transmit(self_->init.channel_ptr, Pmcmd_GetLldTxObject(&self_->wd.wd_cmd));
1344         }
1345         else
1346         {
1347             TR_ERROR((self_->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Unable to reserve watchdog command ", 0U));
1348             Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true);
1349         }
1350     }
1351     else                                            /* status not received in time - notify communication error */
1352     {
1353         TR_ERROR((self_->init.base_ptr->ucs_user_ptr, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Missing response on status request", 0U));
1354         Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true);
1355     }
1356 }
1357
1358 /*!
1359  * @}
1360  * \endcond
1361  */
1362
1363 /*------------------------------------------------------------------------------------------------*/
1364 /* End of file                                                                                    */
1365 /*------------------------------------------------------------------------------------------------*/
1366