1 /**
2 ******************************************************************************
3 * @file hci_tl.c
4 * @author MCD Application Team
5 * @brief Function for managing HCI interface.
6 ******************************************************************************
7 * @attention
8 *
9 * <h2><center>© Copyright (c) 2019 STMicroelectronics.
10 * All rights reserved.</center></h2>
11 *
12 * This software component is licensed by ST under BSD 3-Clause license,
13 * the "License"; You may not use this file except in compliance with the
14 * License. You may obtain a copy of the License at:
15 * opensource.org/licenses/BSD-3-Clause
16 *
17 ******************************************************************************
18 */
19
20
21 /* Includes ------------------------------------------------------------------*/
22 #include "ble_common.h"
23 #include "ble_const.h"
24
25 #include "stm_list.h"
26 #include "tl.h"
27 #include "hci_tl.h"
28
29
30 /* Private typedef -----------------------------------------------------------*/
31 /* Private defines -----------------------------------------------------------*/
32
33 /**
34 * The default HCI layer timeout is set to 33s
35 */
36 #define HCI_TL_DEFAULT_TIMEOUT (33000)
37
38 /* Private macros ------------------------------------------------------------*/
39 /* Public variables ---------------------------------------------------------*/
40 /* Private variables ---------------------------------------------------------*/
41 /**
42 * START of Section BLE_DRIVER_CONTEXT
43 */
44 PLACE_IN_SECTION("BLE_DRIVER_CONTEXT") static volatile uint8_t hci_timer_id;
45 PLACE_IN_SECTION("BLE_DRIVER_CONTEXT") static tListNode HciAsynchEventQueue;
46 PLACE_IN_SECTION("BLE_DRIVER_CONTEXT") static TL_CmdPacket_t *pCmdBuffer;
47 PLACE_IN_SECTION("BLE_DRIVER_CONTEXT") HCI_TL_UserEventFlowStatus_t UserEventFlow;
48 /**
49 * END of Section BLE_DRIVER_CONTEXT
50 */
51
52 static tHciContext hciContext;
53 static tListNode HciCmdEventQueue;
54 static void (* StatusNotCallBackFunction) (HCI_TL_CmdStatus_t status);
55
56 /* Private function prototypes -----------------------------------------------*/
57 static void NotifyCmdStatus(HCI_TL_CmdStatus_t hcicmdstatus);
58 static void SendCmd(uint16_t opcode, uint8_t plen, void *param);
59 static void TlEvtReceived(TL_EvtPacket_t *hcievt);
60 static void TlInit( TL_CmdPacket_t * p_cmdbuffer );
61
62 /* Interface ------- ---------------------------------------------------------*/
hci_init(void (* UserEvtRx)(void * pData),void * pConf)63 void hci_init(void(* UserEvtRx)(void* pData), void* pConf)
64 {
65 StatusNotCallBackFunction = ((HCI_TL_HciInitConf_t *)pConf)->StatusNotCallBack;
66 hciContext.UserEvtRx = UserEvtRx;
67
68 hci_register_io_bus (&hciContext.io);
69
70 TlInit((TL_CmdPacket_t *)(((HCI_TL_HciInitConf_t *)pConf)->p_cmdbuffer));
71
72 return;
73 }
74
hci_user_evt_proc(void)75 void hci_user_evt_proc(void)
76 {
77 TL_EvtPacket_t *phcievtbuffer;
78 tHCI_UserEvtRxParam UserEvtRxParam;
79
80 /**
81 * Up to release version v1.2.0, a while loop was implemented to read out events from the queue as long as
82 * it is not empty. However, in a bare metal implementation, this leads to calling in a "blocking" mode
83 * hci_user_evt_proc() as long as events are received without giving the opportunity to run other tasks
84 * in the background.
85 * From now, the events are reported one by one. When it is checked there is still an event pending in the queue,
86 * a request to the user is made to call again hci_user_evt_proc().
87 * This gives the opportunity to the application to run other background tasks between each event.
88 */
89
90 /**
91 * It is more secure to use LST_remove_head()/LST_insert_head() compare to LST_get_next_node()/LST_remove_node()
92 * in case the user overwrite the header where the next/prev pointers are located
93 */
94
95 if((LST_is_empty(&HciAsynchEventQueue) == FALSE) && (UserEventFlow != HCI_TL_UserEventFlow_Disable))
96 {
97 LST_remove_head ( &HciAsynchEventQueue, (tListNode **)&phcievtbuffer );
98
99 UserEventFlow = HCI_TL_UserEventFlow_Enable;
100
101 if (hciContext.UserEvtRx != NULL)
102 {
103 UserEvtRxParam.pckt = phcievtbuffer;
104 hciContext.UserEvtRx((void *)&UserEvtRxParam);
105 UserEventFlow = UserEvtRxParam.status;
106 }
107
108 if(UserEventFlow != HCI_TL_UserEventFlow_Disable)
109 {
110 TL_MM_EvtDone( phcievtbuffer );
111 }
112 else
113 {
114 /**
115 * put back the event in the queue
116 */
117 LST_insert_head ( &HciAsynchEventQueue, (tListNode *)phcievtbuffer );
118 }
119 }
120
121 if((LST_is_empty(&HciAsynchEventQueue) == FALSE) && (UserEventFlow != HCI_TL_UserEventFlow_Disable))
122 {
123 hci_notify_asynch_evt((void*) &HciAsynchEventQueue);
124 }
125
126
127 return;
128 }
129
hci_resume_flow(void)130 void hci_resume_flow( void )
131 {
132 UserEventFlow = HCI_TL_UserEventFlow_Enable;
133
134 /**
135 * It is better to go through the background process as it is not sure from which context this API may
136 * be called
137 */
138 hci_notify_asynch_evt((void*) &HciAsynchEventQueue);
139
140 return;
141 }
142
hci_send_req(struct hci_request * p_cmd,uint8_t async)143 int hci_send_req(struct hci_request *p_cmd, uint8_t async)
144 {
145 uint16_t opcode;
146 TL_CcEvt_t *pcommand_complete_event;
147 TL_CsEvt_t *pcommand_status_event;
148 TL_EvtPacket_t *pevtpacket;
149 uint8_t hci_cmd_complete_return_parameters_length;
150 HCI_TL_CmdStatus_t local_cmd_status;
151
152 NotifyCmdStatus(HCI_TL_CmdBusy);
153 local_cmd_status = HCI_TL_CmdBusy;
154 opcode = ((p_cmd->ocf) & 0x03ff) | ((p_cmd->ogf) << 10);
155 SendCmd(opcode, p_cmd->clen, p_cmd->cparam);
156
157 while(local_cmd_status == HCI_TL_CmdBusy)
158 {
159 hci_cmd_resp_wait(HCI_TL_DEFAULT_TIMEOUT);
160
161 /**
162 * Process Cmd Event
163 */
164 while(LST_is_empty(&HciCmdEventQueue) == FALSE)
165 {
166 LST_remove_head (&HciCmdEventQueue, (tListNode **)&pevtpacket);
167
168 if(pevtpacket->evtserial.evt.evtcode == TL_BLEEVT_CS_OPCODE)
169 {
170 pcommand_status_event = (TL_CsEvt_t*)pevtpacket->evtserial.evt.payload;
171 if(pcommand_status_event->cmdcode == opcode)
172 {
173 *(uint8_t *)(p_cmd->rparam) = pcommand_status_event->status;
174 }
175
176 if(pcommand_status_event->numcmd != 0)
177 {
178 local_cmd_status = HCI_TL_CmdAvailable;
179 }
180 }
181 else
182 {
183 pcommand_complete_event = (TL_CcEvt_t*)pevtpacket->evtserial.evt.payload;
184
185 if(pcommand_complete_event->cmdcode == opcode)
186 {
187 hci_cmd_complete_return_parameters_length = pevtpacket->evtserial.evt.plen - TL_EVT_HDR_SIZE;
188 p_cmd->rlen = MIN(hci_cmd_complete_return_parameters_length, p_cmd->rlen);
189 memcpy(p_cmd->rparam, pcommand_complete_event->payload, p_cmd->rlen);
190 }
191
192 if(pcommand_complete_event->numcmd != 0)
193 {
194 local_cmd_status = HCI_TL_CmdAvailable;
195 }
196 }
197 }
198 }
199
200 NotifyCmdStatus(HCI_TL_CmdAvailable);
201
202 return 0;
203 }
204
205 /* Private functions ---------------------------------------------------------*/
TlInit(TL_CmdPacket_t * p_cmdbuffer)206 static void TlInit( TL_CmdPacket_t * p_cmdbuffer )
207 {
208 TL_BLE_InitConf_t Conf;
209
210 /**
211 * Always initialize the command event queue
212 */
213 LST_init_head (&HciCmdEventQueue);
214
215 pCmdBuffer = p_cmdbuffer;
216
217 LST_init_head (&HciAsynchEventQueue);
218
219 UserEventFlow = HCI_TL_UserEventFlow_Enable;
220
221 /* Initialize low level driver */
222 if (hciContext.io.Init)
223 {
224
225 Conf.p_cmdbuffer = (uint8_t *)p_cmdbuffer;
226 Conf.IoBusEvtCallBack = TlEvtReceived;
227 hciContext.io.Init(&Conf);
228 }
229
230 return;
231 }
232
SendCmd(uint16_t opcode,uint8_t plen,void * param)233 static void SendCmd(uint16_t opcode, uint8_t plen, void *param)
234 {
235 pCmdBuffer->cmdserial.cmd.cmdcode = opcode;
236 pCmdBuffer->cmdserial.cmd.plen = plen;
237 memcpy( pCmdBuffer->cmdserial.cmd.payload, param, plen );
238
239 hciContext.io.Send(0,0);
240
241 return;
242 }
243
NotifyCmdStatus(HCI_TL_CmdStatus_t hcicmdstatus)244 static void NotifyCmdStatus(HCI_TL_CmdStatus_t hcicmdstatus)
245 {
246 if(hcicmdstatus == HCI_TL_CmdBusy)
247 {
248 if(StatusNotCallBackFunction != 0)
249 {
250 StatusNotCallBackFunction(HCI_TL_CmdBusy);
251 }
252 }
253 else
254 {
255 if(StatusNotCallBackFunction != 0)
256 {
257 StatusNotCallBackFunction(HCI_TL_CmdAvailable);
258 }
259 }
260
261 return;
262 }
263
TlEvtReceived(TL_EvtPacket_t * hcievt)264 static void TlEvtReceived(TL_EvtPacket_t *hcievt)
265 {
266 if ( ((hcievt->evtserial.evt.evtcode) == TL_BLEEVT_CS_OPCODE) || ((hcievt->evtserial.evt.evtcode) == TL_BLEEVT_CC_OPCODE ) )
267 {
268 LST_insert_tail(&HciCmdEventQueue, (tListNode *)hcievt);
269 hci_cmd_resp_release(0); /**< Notify the application a full Cmd Event has been received */
270 }
271 else
272 {
273 LST_insert_tail(&HciAsynchEventQueue, (tListNode *)hcievt);
274 hci_notify_asynch_evt((void*) &HciAsynchEventQueue); /**< Notify the application a full HCI event has been received */
275 }
276
277 return;
278 }
279