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>&copy; 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