1 /**
2  ******************************************************************************
3  * @file    shci.c
4  * @author  MCD Application Team
5  * @brief   System HCI command implementation
6  ******************************************************************************
7  * @attention
8  *
9  * Copyright (c) 2018-2021 STMicroelectronics.
10  * All rights reserved.
11  *
12  * This software is licensed under terms that can be found in the LICENSE file
13  * in the root directory of this software component.
14  * If no LICENSE file comes with this software, it is provided AS-IS.
15  *
16  ******************************************************************************
17  */
18 
19 
20 /* Includes ------------------------------------------------------------------*/
21 #include "stm32_wpan_common.h"
22 
23 #include "stm_list.h"
24 #include "shci_tl.h"
25 
26 /* Private typedef -----------------------------------------------------------*/
27 typedef enum
28 {
29   SHCI_TL_CMD_RESP_RELEASE,
30   SHCI_TL_CMD_RESP_WAIT,
31 } SHCI_TL_CmdRespStatus_t;
32 
33 /* Private defines -----------------------------------------------------------*/
34 /**
35  * The default System HCI layer timeout is set to 33s
36  */
37 #define SHCI_TL_DEFAULT_TIMEOUT (33000)
38 
39 /* Private macros ------------------------------------------------------------*/
40 /* Public variables ---------------------------------------------------------*/
41 /* Private variables ---------------------------------------------------------*/
42 #if 1 /* Z-WB-2 */
43 /* SYSTEM_DRIVER_CONTEXT section is unused and generates useless warnings */
44 /* Provide alterative definitions */
45 static tListNode SHciAsynchEventQueue;
46 static volatile SHCI_TL_CmdStatus_t SHCICmdStatus;
47 static TL_CmdPacket_t *pCmdBuffer;
48 SHCI_TL_UserEventFlowStatus_t SHCI_TL_UserEventFlow;
49 #else
50 /**
51  * START of Section SYSTEM_DRIVER_CONTEXT
52  */
53 PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") static tListNode SHciAsynchEventQueue;
54 PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") static volatile SHCI_TL_CmdStatus_t SHCICmdStatus;
55 PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") static TL_CmdPacket_t *pCmdBuffer;
56 PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") SHCI_TL_UserEventFlowStatus_t SHCI_TL_UserEventFlow;
57 /**
58  * END of Section SYSTEM_DRIVER_CONTEXT
59  */
60 #endif
61 
62 static tSHciContext shciContext;
63 static void (* StatusNotCallBackFunction) (SHCI_TL_CmdStatus_t status);
64 
65 static volatile SHCI_TL_CmdRespStatus_t CmdRspStatusFlag;
66 
67 /* Private function prototypes -----------------------------------------------*/
68 static void Cmd_SetStatus(SHCI_TL_CmdStatus_t shcicmdstatus);
69 static void TlCmdEvtReceived(TL_EvtPacket_t *shcievt);
70 static void TlUserEvtReceived(TL_EvtPacket_t *shcievt);
71 static void TlInit( TL_CmdPacket_t * p_cmdbuffer );
72 
73 /* Interface ------- ---------------------------------------------------------*/
shci_init(void (* UserEvtRx)(void * pData),void * pConf)74 void shci_init(void(* UserEvtRx)(void* pData), void* pConf)
75 {
76   StatusNotCallBackFunction = ((SHCI_TL_HciInitConf_t *)pConf)->StatusNotCallBack;
77   shciContext.UserEvtRx = UserEvtRx;
78 
79   shci_register_io_bus (&shciContext.io);
80 
81   TlInit((TL_CmdPacket_t *)(((SHCI_TL_HciInitConf_t *)pConf)->p_cmdbuffer));
82 
83   return;
84 }
85 
shci_user_evt_proc(void)86 void shci_user_evt_proc(void)
87 {
88   TL_EvtPacket_t *phcievtbuffer;
89   tSHCI_UserEvtRxParam UserEvtRxParam;
90 
91   /**
92    * Up to release version v1.2.0, a while loop was implemented to read out events from the queue as long as
93    * it is not empty. However, in a bare metal implementation, this leads to calling in a "blocking" mode
94    * shci_user_evt_proc() as long as events are received without giving the opportunity to run other tasks
95    * in the background.
96    * From now, the events are reported one by one. When it is checked there is still an event pending in the queue,
97    * a request to the user is made to call again shci_user_evt_proc().
98    * This gives the opportunity to the application to run other background tasks between each event.
99    */
100 
101   /**
102    * It is more secure to use LST_remove_head()/LST_insert_head() compare to LST_get_next_node()/LST_remove_node()
103    * in case the user overwrite the header where the next/prev pointers are located
104    */
105   if((LST_is_empty(&SHciAsynchEventQueue) == FALSE) && (SHCI_TL_UserEventFlow != SHCI_TL_UserEventFlow_Disable))
106   {
107     LST_remove_head ( &SHciAsynchEventQueue, (tListNode **)&phcievtbuffer );
108 
109     if (shciContext.UserEvtRx != NULL)
110     {
111       UserEvtRxParam.pckt = phcievtbuffer;
112       UserEvtRxParam.status = SHCI_TL_UserEventFlow_Enable;
113       shciContext.UserEvtRx((void *)&UserEvtRxParam);
114       SHCI_TL_UserEventFlow = UserEvtRxParam.status;
115     }
116     else
117     {
118       SHCI_TL_UserEventFlow = SHCI_TL_UserEventFlow_Enable;
119     }
120 
121     if(SHCI_TL_UserEventFlow != SHCI_TL_UserEventFlow_Disable)
122     {
123       TL_MM_EvtDone( phcievtbuffer );
124     }
125     else
126     {
127       /**
128        * put back the event in the queue
129        */
130       LST_insert_head ( &SHciAsynchEventQueue, (tListNode *)phcievtbuffer );
131     }
132   }
133 
134   if((LST_is_empty(&SHciAsynchEventQueue) == FALSE) && (SHCI_TL_UserEventFlow != SHCI_TL_UserEventFlow_Disable))
135   {
136     shci_notify_asynch_evt((void*) &SHciAsynchEventQueue);
137   }
138 
139 
140   return;
141 }
142 
shci_resume_flow(void)143 void shci_resume_flow( void )
144 {
145   SHCI_TL_UserEventFlow = SHCI_TL_UserEventFlow_Enable;
146 
147   /**
148    * It is better to go through the background process as it is not sure from which context this API may
149    * be called
150    */
151   shci_notify_asynch_evt((void*) &SHciAsynchEventQueue);
152 
153   return;
154 }
155 
shci_send(uint16_t cmd_code,uint8_t len_cmd_payload,uint8_t * p_cmd_payload,TL_EvtPacket_t * p_rsp)156 void shci_send( uint16_t cmd_code, uint8_t len_cmd_payload, uint8_t * p_cmd_payload, TL_EvtPacket_t * p_rsp )
157 {
158   Cmd_SetStatus(SHCI_TL_CmdBusy);
159 
160   pCmdBuffer->cmdserial.cmd.cmdcode = cmd_code;
161   pCmdBuffer->cmdserial.cmd.plen = len_cmd_payload;
162 
163   memcpy(pCmdBuffer->cmdserial.cmd.payload, p_cmd_payload, len_cmd_payload );
164   CmdRspStatusFlag = SHCI_TL_CMD_RESP_WAIT;
165   shciContext.io.Send(0,0);
166 
167   shci_cmd_resp_wait(SHCI_TL_DEFAULT_TIMEOUT);
168 
169   /**
170    * The command complete of a system command does not have the header
171    * It starts immediately with the evtserial field
172    */
173   memcpy( &(p_rsp->evtserial), pCmdBuffer, ((TL_EvtSerial_t*)pCmdBuffer)->evt.plen + TL_EVT_HDR_SIZE );
174 
175   Cmd_SetStatus(SHCI_TL_CmdAvailable);
176 
177   return;
178 }
179 
180 /* Private functions ---------------------------------------------------------*/
TlInit(TL_CmdPacket_t * p_cmdbuffer)181 static void TlInit( TL_CmdPacket_t * p_cmdbuffer )
182 {
183   TL_SYS_InitConf_t Conf;
184 
185   pCmdBuffer = p_cmdbuffer;
186 
187   LST_init_head (&SHciAsynchEventQueue);
188 
189   Cmd_SetStatus(SHCI_TL_CmdAvailable);
190 
191   SHCI_TL_UserEventFlow = SHCI_TL_UserEventFlow_Enable;
192 
193   /* Initialize low level driver */
194   if (shciContext.io.Init)
195   {
196 
197     Conf.p_cmdbuffer = (uint8_t *)p_cmdbuffer;
198     Conf.IoBusCallBackCmdEvt = TlCmdEvtReceived;
199     Conf.IoBusCallBackUserEvt = TlUserEvtReceived;
200     shciContext.io.Init(&Conf);
201   }
202 
203   return;
204 }
205 
Cmd_SetStatus(SHCI_TL_CmdStatus_t shcicmdstatus)206 static void Cmd_SetStatus(SHCI_TL_CmdStatus_t shcicmdstatus)
207 {
208   if(shcicmdstatus == SHCI_TL_CmdBusy)
209   {
210     if(StatusNotCallBackFunction != 0)
211     {
212       StatusNotCallBackFunction( SHCI_TL_CmdBusy );
213     }
214     SHCICmdStatus = SHCI_TL_CmdBusy;
215   }
216   else
217   {
218     SHCICmdStatus = SHCI_TL_CmdAvailable;
219     if(StatusNotCallBackFunction != 0)
220     {
221       StatusNotCallBackFunction( SHCI_TL_CmdAvailable );
222     }
223   }
224 
225   return;
226 }
227 
TlCmdEvtReceived(TL_EvtPacket_t * shcievt)228 static void TlCmdEvtReceived(TL_EvtPacket_t *shcievt)
229 {
230   (void)(shcievt);
231   shci_cmd_resp_release(0); /**< Notify the application the Cmd response has been received */
232 
233   return;
234 }
235 
TlUserEvtReceived(TL_EvtPacket_t * shcievt)236 static void TlUserEvtReceived(TL_EvtPacket_t *shcievt)
237 {
238   LST_insert_tail(&SHciAsynchEventQueue, (tListNode *)shcievt);
239   shci_notify_asynch_evt((void*) &SHciAsynchEventQueue); /**< Notify the application a full HCI event has been received */
240 
241   return;
242 }
243 
244 /* Weak implementation ----------------------------------------------------------------*/
shci_cmd_resp_wait(uint32_t timeout)245 __WEAK void shci_cmd_resp_wait(uint32_t timeout)
246 {
247   (void)timeout;
248 
249   while(CmdRspStatusFlag != SHCI_TL_CMD_RESP_RELEASE);
250 
251   return;
252 }
253 
shci_cmd_resp_release(uint32_t flag)254 __WEAK void shci_cmd_resp_release(uint32_t flag)
255 {
256   (void)flag;
257 
258   CmdRspStatusFlag = SHCI_TL_CMD_RESP_RELEASE;
259 
260   return;
261 }
262