2c6bbbad5c4a593bd1b4b8a3eb2f2c0d834a632e
[apps/agl-service-unicens.git] / ucs2-lib / src / ucs_pmchannel.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 Channel
25  *
26  * \cond UCS_INTERNAL_DOC
27  * \addtogroup  G_PMC
28  * @{
29  */
30
31 /*------------------------------------------------------------------------------------------------*/
32 /* Includes                                                                                       */
33 /*------------------------------------------------------------------------------------------------*/
34 #include "ucs_pmchannel.h"
35 #include "ucs_pmp.h"
36 #include "ucs_pmcmd.h"
37 #include "ucs_misc.h"
38
39 /*------------------------------------------------------------------------------------------------*/
40 /* Internal Constants                                                                             */
41 /*------------------------------------------------------------------------------------------------*/
42
43 /*------------------------------------------------------------------------------------------------*/
44 /* Internal typedefs                                                                              */
45 /*------------------------------------------------------------------------------------------------*/
46
47 /*------------------------------------------------------------------------------------------------*/
48 /* Internal prototypes                                                                            */
49 /*------------------------------------------------------------------------------------------------*/
50 /* LLD related interface functions */
51 static Ucs_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size);
52 static void Pmch_RxUnused(void *self, Ucs_Lld_RxMsg_t *msg_ptr);
53 static void Pmch_RxReceive(void *self, Ucs_Lld_RxMsg_t *msg_ptr);
54 static void Pmch_TxRelease(void *self, Ucs_Lld_TxMsg_t *msg_ptr);
55
56 /*------------------------------------------------------------------------------------------------*/
57 /* Implementation                                                                                 */
58 /*------------------------------------------------------------------------------------------------*/
59
60 /*! \brief  Constructor of class CPmChannel
61  *  \param  self        The instance
62  *  \param  init_ptr    Reference to initialization data structure
63  */
64 void Pmch_Ctor(CPmChannel *self, const Pmch_InitData_t *init_ptr)
65 {
66     uint16_t cnt;
67     MISC_MEM_SET(self, 0, sizeof(*self));
68
69     self->init_data    = *init_ptr;
70     self->lld_active   = false;
71
72     self->ucs_iface.rx_allocate_fptr       = &Pmch_RxAllocate;
73     self->ucs_iface.rx_receive_fptr        = &Pmch_RxReceive;
74     self->ucs_iface.rx_free_unused_fptr    = &Pmch_RxUnused;
75     self->ucs_iface.tx_release_fptr        = &Pmch_TxRelease;
76
77     Pool_Ctor(&self->rx_msgs_pool, self->rx_msgs,                   /* initialize Rx message pool */
78               PMCH_POOL_SIZE_RX, self->init_data.ucs_user_ptr);
79     for (cnt = 0U; cnt < PMCH_POOL_SIZE_RX; cnt++)                  /* and assign LLD Rx handles  */
80     {
81         Msg_SetLldHandle(&self->rx_msgs[cnt], &self->lld_rx_msgs[cnt]);
82         self->lld_rx_msgs[cnt].msg_ptr = &self->rx_msgs[cnt];
83     }
84 }
85
86 /*! \brief      Registers an Rx callback function dedicated to one FIFO
87  *  \param      self      The instance
88  *  \param      fifo_id   The FIFO identifier
89  *  \param      rx_fptr   The Rx callback function
90  *  \param      inst_ptr  Reference to the instance required to invoke the callback
91  */
92 void Pmch_RegisterReceiver(CPmChannel *self, Pmp_FifoId_t fifo_id, Pmch_OnRxMsg_t rx_fptr, void *inst_ptr)
93 {
94     TR_ASSERT(self->init_data.ucs_user_ptr, "[PMCH]", (((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_ICM)||((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_MCM)||((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_RCM)));
95
96     self->receivers[fifo_id].rx_fptr = rx_fptr;
97     self->receivers[fifo_id].inst_ptr = inst_ptr;
98 }
99
100 /*! \brief      Un-initializes the LLD interface of the channel
101  *  \param      self    The instance
102  */
103 void Pmch_Initialize(CPmChannel *self)
104 {
105     if (self->lld_active == false)
106     {
107         self->lld_active   = true;
108         TR_INFO((self->init_data.ucs_user_ptr, "[PMCH]", "Pmch_Initialize(): LLD_START()", 0U));
109         self->init_data.lld_iface.start_fptr(&self->ucs_iface, self, self->init_data.lld_iface.lld_user_ptr);
110     }
111 }
112
113 /*! \brief      Un-initializes the LLD interface of the channel
114  *  \param      self    The instance
115  */
116 extern void Pmch_Uninitialize(CPmChannel *self)
117 {
118     TR_INFO((self->init_data.ucs_user_ptr, "[PMCH]", "Pmch_Uninitialize(): Channel un-synchronization started", 0U));
119
120     if (self->lld_active != false)
121     {
122         self->lld_active = false;
123         TR_INFO((self->init_data.ucs_user_ptr, "[PMCH]", "Pmch_Uninitialize(): LLD_STOP()", 0U));
124         self->init_data.lld_iface.stop_fptr(self->init_data.lld_iface.lld_user_ptr);
125     }
126 }
127
128 /*! \brief      Wrapper for LLD transmit
129  *  \details    This function which shall be used by all internal classes. No class shall
130  *              invoke the LLD transmit function directly. Thus, it might be possible 
131  *              in future to handle transmission failures and retries.
132  *  \param      self    The instance
133  *  \param      msg_ptr Reference to the public LLD message structure
134  */
135 void Pmch_Transmit(CPmChannel *self, Ucs_Lld_TxMsg_t *msg_ptr)
136 {
137     if (self->lld_active != false)
138     {
139         self->init_data.lld_iface.tx_transmit_fptr(msg_ptr, self->init_data.lld_iface.lld_user_ptr);
140     }
141     else
142     {
143         Pmch_TxRelease(self, msg_ptr);
144     }
145 }
146
147 /*------------------------------------------------------------------------------------------------*/
148 /* The exposed low-level driver interface                                                         */
149 /*------------------------------------------------------------------------------------------------*/
150 /*! \brief  Allocates an Rx message object 
151  *  \param  self        The instance
152  *  \param  buffer_size Size of the memory chunk in bytes which is needed to
153  *                      copy the Rx message.
154  *  \return Reference to an allocated Rx message object or \c NULL if no message object is available.
155  */
156 static Ucs_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size)
157 {
158     CMessage *msg_ptr = NULL;
159     Ucs_Lld_RxMsg_t *handle = NULL;
160     CPmChannel *self_ = (CPmChannel*)self;
161
162     if (buffer_size <= MSG_SIZE_RSVD_BUFFER)
163     {
164         msg_ptr = Pool_GetMsg(&self_->rx_msgs_pool);
165
166         if (msg_ptr != NULL)
167         {
168             Msg_Cleanup(msg_ptr);
169             handle = &((Lld_IntRxMsg_t*)Msg_GetLldHandle(msg_ptr))->lld_msg;
170
171             TR_ASSERT(self_->init_data.ucs_user_ptr, "[PMCH]", (handle != NULL));
172
173             handle->data_size       = buffer_size;
174             handle->data_ptr        = Msg_GetHeader(msg_ptr);
175         }
176         else
177         {
178             self_->rx_trigger_available = true;
179             TR_INFO((self_->init_data.ucs_user_ptr, "[PMCH]", "Pmch_RxAllocate(): Allocation failed, size=%u", 1U, buffer_size));
180         }
181     }
182     else
183     {
184         self_->rx_trigger_available = true;         
185         TR_FAILED_ASSERT(self_->init_data.ucs_user_ptr, "[PMCH]");
186     }
187
188     return handle;
189 }
190
191 /*! \brief  Frees an unused Rx message object
192  *  \param  self        The instance
193  *  \param  msg_ptr     Reference to the unused Rx message object
194  */
195 static void Pmch_RxUnused(void *self, Ucs_Lld_RxMsg_t *msg_ptr)
196 {
197     CPmChannel *self_ = (CPmChannel*)self;
198     CMessage *pb_handle = ((Lld_IntRxMsg_t*)(void*)msg_ptr)->msg_ptr;
199
200     TR_ASSERT(self_->init_data.ucs_user_ptr, "[PMCH]", (pb_handle != NULL));
201     Pmch_ReturnRxToPool(self_, pb_handle);
202 }
203
204 /*! \brief  Pass an Rx message to UNICENS 
205  *  \param  self        The instance
206  *  \param  msg_ptr     Reference to the Rx message object containing the received
207  *                      message.
208  */
209 static void Pmch_RxReceive(void *self, Ucs_Lld_RxMsg_t *msg_ptr)
210 {
211     bool found = false;
212     CPmChannel *self_ = (CPmChannel*)self;
213
214     if (msg_ptr->data_ptr != NULL)
215     {
216         if (msg_ptr->data_size >= PMP_PM_MIN_SIZE_HEADER)                   /* ignore incomplete messages */
217         {
218             uint8_t fifo_no = (uint8_t)Pmp_GetFifoId(msg_ptr->data_ptr);    /* get channel id (FIFO number) */
219
220             if ((fifo_no < PMP_MAX_NUM_FIFOS) && (self_->receivers[fifo_no].inst_ptr != NULL))
221             {
222                 CMessage *handle = ((Lld_IntRxMsg_t*)(void*)msg_ptr)->msg_ptr;
223                                                                             /* forward message to the respective FIFO/channel */
224                 self_->receivers[fifo_no].rx_fptr(self_->receivers[fifo_no].inst_ptr, handle); 
225                 found = true;
226             }
227             else
228             {
229                 TR_ERROR((self_->init_data.ucs_user_ptr, "[PMCH]", "Pmch_RxReceive(): received message for unregistered FIFO no=%u", 1U, fifo_no));
230             }
231         }
232         else
233         {
234             TR_ERROR((self_->init_data.ucs_user_ptr, "[PMCH]", "Pmch_RxReceive(): received incomplete message of size=%u", 1U, msg_ptr->data_size));
235         }
236     }
237     else
238     {
239         TR_ERROR((self_->init_data.ucs_user_ptr, "[PMCH]", "Pmch_RxReceive(): message data is not valid", 0U));
240     }
241
242     if (found == false) 
243     {
244         Pmch_RxUnused(self_, msg_ptr);                                      /* Just return message to pool until PMC is implemented */
245     }
246 }
247
248 /*! \brief  Notifies that the LLD no longer needs to access the Tx message object
249  *  \param  self        The instance
250  *  \param  msg_ptr     Reference to the Tx message object which is no longer accessed
251  *                      by the low-level driver
252  */
253 static void Pmch_TxRelease(void *self, Ucs_Lld_TxMsg_t *msg_ptr)
254 {
255     CPmChannel *self_ = (CPmChannel*)self;
256     Lld_IntTxMsg_t *tx_ptr = (Lld_IntTxMsg_t*)(void*)msg_ptr;
257
258     if ((tx_ptr->owner_ptr == NULL) && (tx_ptr->msg_ptr == NULL))           /* tx_ptr is command */
259     {
260         Pmcmd_Release((CPmCommand*)(void*)tx_ptr);
261     }
262     else if (tx_ptr->owner_ptr != NULL)                                     /* release message to FIFO */
263     {
264         self_->init_data.tx_release_fptr(tx_ptr->owner_ptr, msg_ptr);
265     }
266     else
267     {
268         TR_FAILED_ASSERT(self_->init_data.ucs_user_ptr, "[PMCH]"); /* unknown FIFO - invalid message object */
269     }
270
271     TR_ASSERT(self_->init_data.ucs_user_ptr, "[PMCH]", (msg_ptr->custom_next_msg_ptr == NULL) );  /* concatenation destroyed by the LLD */
272
273 }
274
275 /*------------------------------------------------------------------------------------------------*/
276 /* FIFO Related Callback Functions                                                                */
277 /*------------------------------------------------------------------------------------------------*/
278 /*! \brief  Returns an unused Rx message object back to the pool
279  *  \param  self    The instance
280  *  \param  msg_ptr The unused Rx message object 
281  */
282 void Pmch_ReturnRxToPool(void *self, CMessage *msg_ptr)
283 {
284     CPmChannel *self_ = (CPmChannel*)self;
285
286     Pool_ReturnMsg(msg_ptr);
287
288     if (self_->rx_trigger_available == true)
289     {
290         self_->rx_trigger_available = false;
291
292         if (self_->init_data.lld_iface.rx_available_fptr != NULL)
293         {
294             self_->init_data.lld_iface.rx_available_fptr(self_->init_data.lld_iface.lld_user_ptr);
295         }
296     }
297 }
298
299 /*!
300  * @}
301  * \endcond
302  */
303
304 /*------------------------------------------------------------------------------------------------*/
305 /* End of file                                                                                    */
306 /*------------------------------------------------------------------------------------------------*/
307