1 /*------------------------------------------------------------------------------------------------*/
2 /* UNICENS V2.1.0-3491 */
3 /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
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. */
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. */
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/>. */
18 /* You may also obtain this software under a propriety license from Microchip. */
19 /* Please contact Microchip for further information. */
20 /*------------------------------------------------------------------------------------------------*/
24 * \brief Implementation of Application Message Distributor
26 * \cond UCS_INTERNAL_DOC
31 /*------------------------------------------------------------------------------------------------*/
33 /*------------------------------------------------------------------------------------------------*/
37 /*! \brief Priority of the Application Message Distribution */
38 static const uint8_t AMD_SRV_PRIO = 248U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */
39 /*! \brief Event which starts the Rx message distribution */
40 static const Srv_Event_t AMD_EV_NOTIFY_RX = 1U;
41 /*! \brief Event triggers notification of messages in tx_notify_queue */
42 static const Srv_Event_t AMD_EV_NOTIFY_TX = 2U;
43 /*! \brief FBlockID of FBlock NetBlock */
44 static const uint8_t AMD_FB_NETBLOCK = 1U;
45 /*! \brief FBlockID of FBlock NetworkMaster */
46 static const uint8_t AMD_FB_NETWORKMASTER = 2U;
48 /*------------------------------------------------------------------------------------------------*/
49 /* Internal prototypes */
50 /*------------------------------------------------------------------------------------------------*/
51 static void Amd_Service(void *self);
52 static void Amd_OnAmsComplete(void* self, Ucs_AmsRx_Msg_t* msg_ptr);
53 static void Amd_OnEvent(void *self, void *error_code_ptr);
54 static void Amd_OnTerminateEvent(void *self, void *error_code_ptr);
55 static void Amd_RxFlush(CAmd *self, CDlList *list_ptr);
57 static bool Amd_TxIsRcmMsg(Ucs_AmsTx_Msg_t *msg_ptr);
58 static bool Amd_TxReceiveInternal(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr);
59 static void Amd_TxProcessNotifyQueue(CAmd *self);
62 /*------------------------------------------------------------------------------------------------*/
64 /*------------------------------------------------------------------------------------------------*/
65 /*! \brief Constructor of class CAmd
66 * \param self The instance
67 * \param base_ptr Reference to base services
68 * \param ams_ptr Reference to the AMS
70 void Amd_Ctor(CAmd *self, CBase *base_ptr, CAms *ams_ptr)
72 MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */
74 self->base_ptr = base_ptr;
75 self->ams_ptr = ams_ptr;
77 self->started = false;
78 Srv_Ctor(&self->service, AMD_SRV_PRIO, self, &Amd_Service); /* register service */
79 (void)Scd_AddService(&self->base_ptr->scd, &self->service);
81 Dl_Ctor(&self->pre_queue, self->base_ptr->ucs_user_ptr); /* init preprocessor queue */
82 Dl_Ctor(&self->rx_queue, self->base_ptr->ucs_user_ptr); /* init Rx queue */
83 /* register event observer */
84 Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Amd_OnEvent);
85 Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
86 /* register termination events */
87 Mobs_Ctor(&self->terminate_observer, self, EH_M_TERMINATION_EVENTS, &Amd_OnTerminateEvent);
88 Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->terminate_observer);
90 Ams_RxAssignReceiver(self->ams_ptr, &Amd_OnAmsComplete, self);
95 /*! \brief Constructor of class CAmd
96 * \param self The instance
97 * \param base_ptr Reference to base services
98 * \param ams_ptr Reference to the AMS
99 * \param pool_ptr Reference to the AMS message pool
100 * \param inic_ptr Reference to the INIC
101 * \param net_ptr Reference to the network management
103 void Amd_Ctor(CAmd *self, CBase *base_ptr, CAms *ams_ptr, CAmsMsgPool *pool_ptr, CInic *inic_ptr, CNetworkManagement *net_ptr)
105 MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */
107 self->base_ptr = base_ptr;
108 self->ams_ptr = ams_ptr;
109 self->pool_ptr = pool_ptr;
110 self->inic_ptr = inic_ptr;
111 self->net_ptr = net_ptr;
113 self->started = false;
114 Srv_Ctor(&self->service, AMD_SRV_PRIO, self, &Amd_Service); /* register service */
115 (void)Scd_AddService(&self->base_ptr->scd, &self->service);
117 Dl_Ctor(&self->tx_notify_queue, self->base_ptr->ucs_inst_id); /* init queues */
118 Dl_Ctor(&self->pre_queue, self->base_ptr->ucs_inst_id); /* init preprocessor queue */
119 Dl_Ctor(&self->rx_queue, self->base_ptr->ucs_inst_id); /* init Rx queue */
120 /* register event observer */
121 Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Amd_OnEvent);
122 Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
123 /* register termination events */
124 Mobs_Ctor(&self->terminate_observer, self, EH_M_TERMINATION_EVENTS, &Amd_OnTerminateEvent);
125 Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->terminate_observer);
127 Ams_RxAssignReceiver(self->ams_ptr, &Amd_OnAmsComplete, self);
128 Ams_TxAssignTrcvSelector(self->ams_ptr, &Amd_TxIsRcmMsg);
132 /*! \brief Assigns a pre-processor callback function for Rx messages
133 * \details This function must be called during initialization time.
134 * The AMS shall not already run.
135 * \param self The instance
136 * \param callback_fptr Reference to the callback function
137 * \param inst_ptr Reference to the pre-processor
139 void Amd_AssignPreprocessor(CAmd *self, Amd_RxMsgCompleteCb_t callback_fptr, void *inst_ptr)
141 if (callback_fptr != NULL)
143 self->preprocess_fptr = callback_fptr;
144 self->preprocess_inst_ptr = inst_ptr;
146 self->first_receive_fptr = callback_fptr;
147 self->first_receive_inst_ptr = inst_ptr;
148 self->first_q_ptr = &self->pre_queue;
152 /*! \brief Assigns a receiver callback function for Rx messages
153 * \details This function must be called during initialization time.
154 * The AMS shall not already run.
155 * \param self The instance
156 * \param callback_fptr Reference to the callback function
157 * \param inst_ptr Reference to the receiver
159 void Amd_AssignReceiver(CAmd *self, Amd_RxMsgCompleteCb_t callback_fptr, void *inst_ptr)
161 if (callback_fptr != NULL)
163 self->receive_fptr = callback_fptr;
164 self->receive_inst_ptr = inst_ptr;
166 if (self->first_receive_fptr == NULL)
168 self->first_receive_fptr = callback_fptr;
169 self->first_receive_inst_ptr = inst_ptr;
170 self->first_q_ptr = &self->rx_queue;
175 /*! \brief Assigns as callback function which is able to read and modify the Rx message
176 * \param self The instance
177 * \param callback_fptr Reference to the callback function
178 * \param inst_ptr Reference to the instance (owner of the callback function)
180 void Amd_RxAssignModificator(CAmd *self, Amd_RxModificationCb_t callback_fptr, void *inst_ptr)
182 if (callback_fptr != NULL)
184 self->rx_modification_fptr = callback_fptr;
185 self->rx_modification_inst_ptr = inst_ptr;
189 /*! \brief Service function of CAmd
190 * \details The processing of the Rx queues shall be started asynchronously
191 * after the initialization has succeeded.
192 * \param self The instance
194 static void Amd_Service(void *self)
196 CAmd *self_ = (CAmd*)self;
197 Srv_Event_t event_mask;
198 Srv_GetEvent(&self_->service, &event_mask);
200 if((event_mask & AMD_EV_NOTIFY_RX) == AMD_EV_NOTIFY_RX) /* triggered on internal transmission */
202 Srv_ClearEvent(&self_->service, AMD_EV_NOTIFY_RX);
203 if ((self_->started != false) && (self_->first_receive_fptr != NULL))
205 uint16_t size = Dl_GetSize(self_->first_q_ptr);
208 self_->first_receive_fptr(self_->first_receive_inst_ptr);
213 #ifdef AMD_TX_DISTRIB
214 if((event_mask & AMD_EV_NOTIFY_TX) == AMD_EV_NOTIFY_TX) /* notify Tx distribution failure asynchronously */
216 Srv_ClearEvent(&self_->service, AMD_EV_NOTIFY_TX);
217 Amd_TxProcessNotifyQueue(self_);
222 /*------------------------------------------------------------------------------------------------*/
224 /*------------------------------------------------------------------------------------------------*/
225 /*! \brief Callback function which is invoked on completed application message reception
226 * \param self The instance
227 * \param msg_ptr Reference to the completed application message
229 static void Amd_OnAmsComplete(void *self, Ucs_AmsRx_Msg_t *msg_ptr)
231 CAmd *self_ = (CAmd*)self;
233 if (self_->rx_modification_fptr != NULL)
235 self_->rx_modification_fptr(self_->rx_modification_inst_ptr, msg_ptr);
238 if (self_->first_receive_fptr != NULL)
240 Amsg_RxEnqueue(msg_ptr, self_->first_q_ptr);
242 if (self_->started != false)
244 self_->first_receive_fptr(self_->first_receive_inst_ptr);
249 Ams_RxFreeMsg(self_->ams_ptr, msg_ptr);
253 /*! \brief Callback function if an events leads to the termination of the MNS
254 * \param self The instance
255 * \param error_code_ptr Reference to the error code
257 static void Amd_OnTerminateEvent(void *self, void *error_code_ptr)
259 CAmd *self_ = (CAmd*)self;
260 MISC_UNUSED(error_code_ptr);
262 TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Starting AMD Cleanup", 0U));
263 Amd_RxFlush(self_, &self_->pre_queue);
264 Amd_RxFlush(self_, &self_->rx_queue);
265 #ifdef AMD_TX_DISTRIB
266 Amd_TxProcessNotifyQueue(self_);
268 TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Finished AMD Cleanup", 0U));
271 /*! \brief Callback function which is invoked if the initialization is complete
272 * \param self The instance
273 * \param error_code_ptr Reference to the error code
275 static void Amd_OnEvent(void *self, void *error_code_ptr)
277 CAmd *self_ = (CAmd*)self;
278 MISC_UNUSED(error_code_ptr);
280 TR_INFO((self_->base_ptr->ucs_user_ptr, "[AMD]", "Received init complete event", 0U));
281 self_->started = true;
282 Srv_SetEvent(&self_->service, AMD_EV_NOTIFY_RX);
285 /*! \brief Flushes a given application Rx message queue
286 * \param self The instance
287 * \param list_ptr Reference to a list containing application Rx message objects
289 static void Amd_RxFlush(CAmd *self, CDlList *list_ptr)
291 Ucs_AmsRx_Msg_t *msg_ptr;
293 for (msg_ptr = Amsg_RxDequeue(list_ptr); msg_ptr != NULL; msg_ptr = Amsg_RxDequeue(list_ptr))
295 Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
299 /*------------------------------------------------------------------------------------------------*/
300 /* Pre-processor methods */
301 /*------------------------------------------------------------------------------------------------*/
302 /*! \brief Peeks the front-most application message from the preprocessing queue
303 * \param self The instance
304 * \return Returns a reference to the front-most application message or \c NULL if the queue
307 Ucs_AmsRx_Msg_t* Amd_PrePeekMsg(CAmd *self)
309 return (Ucs_AmsRx_Msg_t*)(void*)Amsg_RxPeek(&self->pre_queue);
312 /*! \brief Removes the front-most application message from the preprocessing queue and frees it
313 * \param self The instance
315 void Amd_PreReleaseMsg(CAmd *self)
317 Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->pre_queue);
321 Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
325 /*! \brief Forwards the front-most application message from the preprocessing queue to the Rx queue
326 * \param self The instance
328 void Amd_PreForwardMsg(CAmd *self)
330 Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->pre_queue);
334 if (self->receive_fptr != NULL)
336 Amsg_RxEnqueue(msg_ptr, &self->rx_queue);
337 self->receive_fptr(self->receive_inst_ptr);
341 Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
346 /*------------------------------------------------------------------------------------------------*/
347 /* Receiver methods */
348 /*------------------------------------------------------------------------------------------------*/
349 /*! \brief Peeks the front-most application message from the Rx queue
350 * \param self The instance
351 * \return Returns a reference to the front-most application message or \c NULL if the queue
354 Ucs_AmsRx_Msg_t* Amd_RxPeekMsg(CAmd *self)
356 return (Ucs_AmsRx_Msg_t*)(void*)Amsg_RxPeek(&self->rx_queue);
359 /*! \brief Removes the front-most application message from the Rx queue and frees it
360 * \param self The instance
362 void Amd_RxReleaseMsg(CAmd *self)
364 Ucs_AmsRx_Msg_t *msg_ptr = Amsg_RxDequeue(&self->rx_queue);
368 Ams_RxFreeMsg(self->ams_ptr, msg_ptr);
372 /*! \brief Retrieves the number of messages which are appended to the Rx queue
373 * \param self The instance
374 * \return Returns the number of messages.
376 uint16_t Amd_RxGetMsgCnt(CAmd *self)
378 return Dl_GetSize(&self->rx_queue);
381 #ifdef AMD_TX_DISTRIB
382 /*------------------------------------------------------------------------------------------------*/
383 /* Transmitter methods */
384 /*------------------------------------------------------------------------------------------------*/
385 /*! \brief Distributes a Tx message internally as Rx message
386 * \param self The instance
387 * \param msg_ptr The Tx message
388 * \param tx_complete_sia_fptr Single instance API callback function which is invoked as soon as
389 * the transmission was finished.
390 * \param tx_complete_fptr Multi instance callback function which is invoked as soon as
391 * the transmission was finished.
392 * \param tx_complete_inst_ptr Instance pointer which is referred when tx_complete_fptr is invoked.
393 * \return Returns \c UCS_RET_SUCCESS if the message accepted for transmission.
394 * Returns \c UCS_RET_ERR_PARAM if the message is refused due to invalid message attributes.
396 Ucs_Return_t Amd_TxSendMsg(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr,
397 Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr)
399 Ucs_Return_t ret = UCS_RET_SUCCESS;
400 bool tx_internal = false;
401 bool tx_network = false;
402 bool tx_ignore_nw_failure = false;
403 bool tx_check_ni = true; /* if false, transmit to INIC even during NET_OFF */
405 if (Ams_TxIsValidMessage(msg_ptr) != false)
407 Net_IsOwnAddrResult_t addr_type = Net_IsOwnAddress(self->net_ptr, msg_ptr->destination_address);
409 TR_ASSERT(self->base_ptr->ucs_user_ptr, "[AMS]", (((tx_complete_sia_fptr != NULL) && (tx_complete_fptr != NULL)) == false));
410 Amsg_TxSetCompleteCallback(msg_ptr, tx_complete_sia_fptr, tx_complete_fptr, tx_complete_inst_ptr);
412 if (msg_ptr->destination_address == UCS_ADDR_INTERNAL)
414 tx_internal = true; /* do not forward internal messages to INIC */
416 else if ((msg_ptr->fblock_id == AMD_FB_NETBLOCK) || (msg_ptr->fblock_id == AMD_FB_NETWORKMASTER))
418 if (addr_type == NET_IS_OWN_ADDR_NODE) /* replace own node address by "1" to force INIC internal routing */
419 { /* do not replace multicast addresses (these are static and handled by INIC) */
420 Amsg_TxReplaceDestinationAddr(msg_ptr, MSG_ADDR_INIC);
422 else if ((addr_type == NET_IS_OWN_ADDR_GROUP) ||
423 (msg_ptr->destination_address == UCS_ADDR_BROADCAST_BLOCKING) ||
424 (msg_ptr->destination_address == UCS_ADDR_BROADCAST_UNBLOCKING))
426 tx_ignore_nw_failure = true;
429 tx_network = true; /* route FBlocks NB and NWM to INIC */
430 tx_check_ni = false; /* INIC performs checks independent from NI state */
432 else if ((msg_ptr->destination_address == UCS_ADDR_BROADCAST_BLOCKING) ||
433 (msg_ptr->destination_address == UCS_ADDR_BROADCAST_UNBLOCKING))
435 tx_internal = true; /* forward broadcast messages to INIC and distribute internally */
442 case NET_IS_OWN_ADDR_NODE:
445 case NET_IS_OWN_ADDR_GROUP:
449 case NET_IS_OWN_ADDR_NONE:
456 if ((Inic_GetAvailability(self->inic_ptr) == UCS_NW_NOT_AVAILABLE) && (tx_check_ni == true))
458 tx_network = false; /* abort network transmission */
459 Amsg_TxUpdateResult(msg_ptr, UCS_MSG_STAT_ERROR_NA_OFF);
462 if (tx_internal != false)
464 if (Amd_TxReceiveInternal(self, msg_ptr) == false)
465 { /* internal transmission failed */
466 Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_ERRBUF);
469 { /* internal transmission succeeded */
470 Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_SUCCESS);
473 else if (tx_ignore_nw_failure != false)
474 { /* INIC routing will succeed while NW transmission fails */
475 Amsg_TxUpdateInternalResult(msg_ptr, AMSG_TX_INTRES_SUCCESS);
478 if (tx_network == false)
479 { /* enqueue message to notification queue and set event */
480 Amsg_TxEnqueue(msg_ptr, &self->tx_notify_queue);
481 Srv_SetEvent(&self->service, AMD_EV_NOTIFY_TX);
485 Ams_TxSendMsgDirect(self->ams_ptr, msg_ptr);
490 ret = UCS_RET_ERR_PARAM; /* invalid message parameters */
496 /*! \brief Decides whether to root a message to MCM or RCM FIFO
497 * \param msg_ptr The Tx message object
498 * \return Returns \c true if a Tx message shall be routed to RCM FIFO, otherwise returns \c false.
500 static bool Amd_TxIsRcmMsg(Ucs_AmsTx_Msg_t *msg_ptr)
503 if (((msg_ptr->fblock_id == AMD_FB_NETBLOCK) && (msg_ptr->op_type <= UCS_OP_STARTACK)) /* is NB.Command */
504 || ((msg_ptr->fblock_id == AMD_FB_NETWORKMASTER) && (msg_ptr->op_type > UCS_OP_STARTACK))) /* or NWM.Report?*/
512 /*! \brief Distributes a Tx message internally as Rx message
513 * \param self The instance
514 * \param msg_ptr The Tx message
515 * \return Returns \c true if the message distributed successfully.
516 * Returns \c false if the allocation of the Rx message has failed.
518 static bool Amd_TxReceiveInternal(CAmd *self, Ucs_AmsTx_Msg_t *msg_ptr)
521 Ucs_AmsRx_Msg_t *rx_ptr = Amsp_AllocRxObj(self->pool_ptr, msg_ptr->data_size);
525 uint16_t src_addr = UCS_ADDR_INTERNAL;
526 if (msg_ptr->destination_address != UCS_ADDR_INTERNAL)
528 src_addr = Inic_GetNodeAddress(self->inic_ptr);
530 Amsg_RxBuildFromTx(rx_ptr, msg_ptr, src_addr);
531 if (self->first_q_ptr != NULL)
533 Amsg_RxEnqueue(rx_ptr, self->first_q_ptr);
534 Srv_SetEvent(&self->service, AMD_EV_NOTIFY_RX);
539 Amsp_FreeRxObj(self->pool_ptr, rx_ptr);
540 TR_FAILED_ASSERT(self->base_ptr->ucs_user_ptr, "[AMD]");
547 /*! \brief Notifies the transmission result for all messages in the tx_notify_queue
548 * \param self The instance
550 static void Amd_TxProcessNotifyQueue(CAmd *self)
552 Ucs_AmsTx_Msg_t *tx_ptr = NULL;
554 for (tx_ptr = Amsg_TxDequeue(&self->tx_notify_queue); tx_ptr != NULL; tx_ptr = Amsg_TxDequeue(&self->tx_notify_queue))
556 /* just just notify completion, the object is automatically freed to the pool */
557 Amsg_TxNotifyComplete(tx_ptr, Amsg_TxGetResultCode(tx_ptr), Amsg_TxGetResultInfo(tx_ptr));
567 /*------------------------------------------------------------------------------------------------*/
569 /*------------------------------------------------------------------------------------------------*/