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