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