335 lines
9.9 KiB
C
335 lines
9.9 KiB
C
/**
|
|
******************************************************************************
|
|
* @file shci.c
|
|
* @author MCD Application Team
|
|
* @brief System HCI command implementation
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* <h2><center>© Copyright (c) 2019 STMicroelectronics.
|
|
* All rights reserved.</center></h2>
|
|
*
|
|
* This software component is licensed by ST under BSD 3-Clause license,
|
|
* the "License"; You may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at:
|
|
* opensource.org/licenses/BSD-3-Clause
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "stm32_wpan_common.h"
|
|
|
|
#include "stm_list.h"
|
|
#include "shci_tl.h"
|
|
#include "tl_dbg_conf.h"
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
typedef enum
|
|
{
|
|
SHCI_TL_CMD_RESP_RELEASE,
|
|
SHCI_TL_CMD_RESP_WAIT,
|
|
} SHCI_TL_CmdRespStatus_t;
|
|
|
|
/* Private defines -----------------------------------------------------------*/
|
|
/**
|
|
* The default System HCI layer timeout is set to 33s
|
|
*/
|
|
#define SHCI_TL_DEFAULT_TIMEOUT (33000)
|
|
|
|
/* Private macros ------------------------------------------------------------*/
|
|
/* Public variables ---------------------------------------------------------*/
|
|
/* Private variables ---------------------------------------------------------*/
|
|
#if 1
|
|
/* SYSTEM_DRIVER_CONTEXT section is unused and generates useless warnings */
|
|
/* Provide alterative definitions */
|
|
static tListNode SHciAsynchEventQueue;
|
|
static volatile SHCI_TL_CmdStatus_t SHCICmdStatus;
|
|
static TL_CmdPacket_t *pCmdBuffer;
|
|
SHCI_TL_UserEventFlowStatus_t SHCI_TL_UserEventFlow;
|
|
#else
|
|
/**
|
|
* START of Section SYSTEM_DRIVER_CONTEXT
|
|
*/
|
|
PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") static tListNode SHciAsynchEventQueue;
|
|
PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") static volatile SHCI_TL_CmdStatus_t SHCICmdStatus;
|
|
PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") static TL_CmdPacket_t *pCmdBuffer;
|
|
PLACE_IN_SECTION("SYSTEM_DRIVER_CONTEXT") SHCI_TL_UserEventFlowStatus_t SHCI_TL_UserEventFlow;
|
|
/**
|
|
* END of Section SYSTEM_DRIVER_CONTEXT
|
|
*/
|
|
#endif
|
|
|
|
static tSHciContext shciContext;
|
|
static void (* StatusNotCallBackFunction) (SHCI_TL_CmdStatus_t status);
|
|
|
|
static volatile SHCI_TL_CmdRespStatus_t CmdRspStatusFlag;
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
static void Cmd_SetStatus(SHCI_TL_CmdStatus_t shcicmdstatus);
|
|
static void TlCmdEvtReceived(TL_EvtPacket_t *shcievt);
|
|
static void TlUserEvtReceived(TL_EvtPacket_t *shcievt);
|
|
static void TlInit( TL_CmdPacket_t * p_cmdbuffer );
|
|
static void OutputCmdTrace(TL_CmdPacket_t *pCmdBuffer);
|
|
static void OutputRspTrace(TL_EvtPacket_t *p_rsp);
|
|
static void OutputEvtTrace(TL_EvtPacket_t *phcievtbuffer);
|
|
|
|
/* Interface ------- ---------------------------------------------------------*/
|
|
void shci_init(void(* UserEvtRx)(void* pData), void* pConf)
|
|
{
|
|
StatusNotCallBackFunction = ((SHCI_TL_HciInitConf_t *)pConf)->StatusNotCallBack;
|
|
shciContext.UserEvtRx = UserEvtRx;
|
|
|
|
shci_register_io_bus (&shciContext.io);
|
|
|
|
TlInit((TL_CmdPacket_t *)(((SHCI_TL_HciInitConf_t *)pConf)->p_cmdbuffer));
|
|
|
|
return;
|
|
}
|
|
|
|
void shci_user_evt_proc(void)
|
|
{
|
|
TL_EvtPacket_t *phcievtbuffer;
|
|
tSHCI_UserEvtRxParam UserEvtRxParam;
|
|
|
|
/**
|
|
* Up to release version v1.2.0, a while loop was implemented to read out events from the queue as long as
|
|
* it is not empty. However, in a bare metal implementation, this leads to calling in a "blocking" mode
|
|
* shci_user_evt_proc() as long as events are received without giving the opportunity to run other tasks
|
|
* in the background.
|
|
* From now, the events are reported one by one. When it is checked there is still an event pending in the queue,
|
|
* a request to the user is made to call again shci_user_evt_proc().
|
|
* This gives the opportunity to the application to run other background tasks between each event.
|
|
*/
|
|
|
|
/**
|
|
* It is more secure to use LST_remove_head()/LST_insert_head() compare to LST_get_next_node()/LST_remove_node()
|
|
* in case the user overwrite the header where the next/prev pointers are located
|
|
*/
|
|
if((LST_is_empty(&SHciAsynchEventQueue) == FALSE) && (SHCI_TL_UserEventFlow != SHCI_TL_UserEventFlow_Disable))
|
|
{
|
|
LST_remove_head ( &SHciAsynchEventQueue, (tListNode **)&phcievtbuffer );
|
|
|
|
OutputEvtTrace(phcievtbuffer);
|
|
|
|
if (shciContext.UserEvtRx != NULL)
|
|
{
|
|
UserEvtRxParam.pckt = phcievtbuffer;
|
|
UserEvtRxParam.status = SHCI_TL_UserEventFlow_Enable;
|
|
shciContext.UserEvtRx((void *)&UserEvtRxParam);
|
|
SHCI_TL_UserEventFlow = UserEvtRxParam.status;
|
|
}
|
|
else
|
|
{
|
|
SHCI_TL_UserEventFlow = SHCI_TL_UserEventFlow_Enable;
|
|
}
|
|
|
|
if(SHCI_TL_UserEventFlow != SHCI_TL_UserEventFlow_Disable)
|
|
{
|
|
TL_MM_EvtDone( phcievtbuffer );
|
|
}
|
|
else
|
|
{
|
|
/**
|
|
* put back the event in the queue
|
|
*/
|
|
LST_insert_head ( &SHciAsynchEventQueue, (tListNode *)phcievtbuffer );
|
|
}
|
|
}
|
|
|
|
if((LST_is_empty(&SHciAsynchEventQueue) == FALSE) && (SHCI_TL_UserEventFlow != SHCI_TL_UserEventFlow_Disable))
|
|
{
|
|
shci_notify_asynch_evt((void*) &SHciAsynchEventQueue);
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
void shci_resume_flow( void )
|
|
{
|
|
SHCI_TL_UserEventFlow = SHCI_TL_UserEventFlow_Enable;
|
|
|
|
/**
|
|
* It is better to go through the background process as it is not sure from which context this API may
|
|
* be called
|
|
*/
|
|
shci_notify_asynch_evt((void*) &SHciAsynchEventQueue);
|
|
|
|
return;
|
|
}
|
|
|
|
void shci_send( uint16_t cmd_code, uint8_t len_cmd_payload, uint8_t * p_cmd_payload, TL_EvtPacket_t * p_rsp )
|
|
{
|
|
Cmd_SetStatus(SHCI_TL_CmdBusy);
|
|
|
|
pCmdBuffer->cmdserial.cmd.cmdcode = cmd_code;
|
|
pCmdBuffer->cmdserial.cmd.plen = len_cmd_payload;
|
|
|
|
memcpy(pCmdBuffer->cmdserial.cmd.payload, p_cmd_payload, len_cmd_payload );
|
|
|
|
OutputCmdTrace(pCmdBuffer);
|
|
|
|
shciContext.io.Send(0,0);
|
|
|
|
shci_cmd_resp_wait(SHCI_TL_DEFAULT_TIMEOUT);
|
|
|
|
/**
|
|
* The command complete of a system command does not have the header
|
|
* It starts immediately with the evtserial field
|
|
*/
|
|
memcpy( &(p_rsp->evtserial), pCmdBuffer, ((TL_EvtSerial_t*)pCmdBuffer)->evt.plen + TL_EVT_HDR_SIZE );
|
|
|
|
OutputRspTrace(p_rsp);
|
|
|
|
Cmd_SetStatus(SHCI_TL_CmdAvailable);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
static void TlInit( TL_CmdPacket_t * p_cmdbuffer )
|
|
{
|
|
TL_SYS_InitConf_t Conf;
|
|
|
|
pCmdBuffer = p_cmdbuffer;
|
|
|
|
LST_init_head (&SHciAsynchEventQueue);
|
|
|
|
Cmd_SetStatus(SHCI_TL_CmdAvailable);
|
|
|
|
SHCI_TL_UserEventFlow = SHCI_TL_UserEventFlow_Enable;
|
|
|
|
/* Initialize low level driver */
|
|
if (shciContext.io.Init)
|
|
{
|
|
|
|
Conf.p_cmdbuffer = (uint8_t *)p_cmdbuffer;
|
|
Conf.IoBusCallBackCmdEvt = TlCmdEvtReceived;
|
|
Conf.IoBusCallBackUserEvt = TlUserEvtReceived;
|
|
shciContext.io.Init(&Conf);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void Cmd_SetStatus(SHCI_TL_CmdStatus_t shcicmdstatus)
|
|
{
|
|
if(shcicmdstatus == SHCI_TL_CmdBusy)
|
|
{
|
|
if(StatusNotCallBackFunction != 0)
|
|
{
|
|
StatusNotCallBackFunction( SHCI_TL_CmdBusy );
|
|
}
|
|
SHCICmdStatus = SHCI_TL_CmdBusy;
|
|
}
|
|
else
|
|
{
|
|
SHCICmdStatus = SHCI_TL_CmdAvailable;
|
|
if(StatusNotCallBackFunction != 0)
|
|
{
|
|
StatusNotCallBackFunction( SHCI_TL_CmdAvailable );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TlCmdEvtReceived(TL_EvtPacket_t *shcievt)
|
|
{
|
|
(void)(shcievt);
|
|
shci_cmd_resp_release(0); /**< Notify the application the Cmd response has been received */
|
|
|
|
return;
|
|
}
|
|
|
|
static void TlUserEvtReceived(TL_EvtPacket_t *shcievt)
|
|
{
|
|
LST_insert_tail(&SHciAsynchEventQueue, (tListNode *)shcievt);
|
|
shci_notify_asynch_evt((void*) &SHciAsynchEventQueue); /**< Notify the application a full HCI event has been received */
|
|
|
|
return;
|
|
}
|
|
|
|
static void OutputCmdTrace(TL_CmdPacket_t *pCmdBuffer)
|
|
{
|
|
TL_SHCI_CMD_DBG_MSG("sys cmd: 0x%04X", pCmdBuffer->cmdserial.cmd.cmdcode);
|
|
|
|
if(pCmdBuffer->cmdserial.cmd.plen != 0)
|
|
{
|
|
TL_SHCI_CMD_DBG_MSG(" payload:");
|
|
TL_SHCI_CMD_DBG_BUF(pCmdBuffer->cmdserial.cmd.payload, pCmdBuffer->cmdserial.cmd.plen, "");
|
|
}
|
|
TL_SHCI_CMD_DBG_MSG("\r\n");
|
|
|
|
return;
|
|
}
|
|
|
|
static void OutputRspTrace(TL_EvtPacket_t *p_rsp)
|
|
{
|
|
switch(p_rsp->evtserial.evt.evtcode)
|
|
{
|
|
case TL_BLEEVT_CC_OPCODE:
|
|
TL_SHCI_CMD_DBG_MSG("sys rsp: 0x%02X", p_rsp->evtserial.evt.evtcode);
|
|
TL_SHCI_CMD_DBG_MSG(" cmd opcode: 0x%02X", ((TL_CcEvt_t*)(p_rsp->evtserial.evt.payload))->cmdcode);
|
|
TL_SHCI_CMD_DBG_MSG(" status: 0x%02X", ((TL_CcEvt_t*)(p_rsp->evtserial.evt.payload))->payload[0]);
|
|
if((p_rsp->evtserial.evt.plen-4) != 0)
|
|
{
|
|
TL_SHCI_CMD_DBG_MSG(" payload:");
|
|
TL_SHCI_CMD_DBG_BUF(&((TL_CcEvt_t*)(p_rsp->evtserial.evt.payload))->payload[1], p_rsp->evtserial.evt.plen-4, "");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TL_SHCI_CMD_DBG_MSG("unknown sys rsp received: %02X", p_rsp->evtserial.evt.evtcode);
|
|
break;
|
|
}
|
|
|
|
TL_SHCI_CMD_DBG_MSG("\r\n");
|
|
|
|
return;
|
|
}
|
|
|
|
static void OutputEvtTrace(TL_EvtPacket_t *phcievtbuffer)
|
|
{
|
|
if(phcievtbuffer->evtserial.evt.evtcode != TL_BLEEVT_VS_OPCODE)
|
|
{
|
|
TL_SHCI_EVT_DBG_MSG("unknown sys evt received: %02X", phcievtbuffer->evtserial.evt.evtcode);
|
|
}
|
|
else
|
|
{
|
|
TL_SHCI_EVT_DBG_MSG("sys evt: 0x%02X", phcievtbuffer->evtserial.evt.evtcode);
|
|
TL_SHCI_EVT_DBG_MSG(" subevtcode: 0x%04X", ((TL_AsynchEvt_t*)(phcievtbuffer->evtserial.evt.payload))->subevtcode);
|
|
if((phcievtbuffer->evtserial.evt.plen-2) != 0)
|
|
{
|
|
TL_SHCI_EVT_DBG_MSG(" payload:");
|
|
TL_SHCI_EVT_DBG_BUF(((TL_AsynchEvt_t*)(phcievtbuffer->evtserial.evt.payload))->payload, phcievtbuffer->evtserial.evt.plen-2, "");
|
|
}
|
|
}
|
|
|
|
TL_SHCI_EVT_DBG_MSG("\r\n");
|
|
|
|
return;
|
|
}
|
|
|
|
/* Weak implementation ----------------------------------------------------------------*/
|
|
__WEAK void shci_cmd_resp_wait(uint32_t timeout)
|
|
{
|
|
(void)timeout;
|
|
|
|
CmdRspStatusFlag = SHCI_TL_CMD_RESP_WAIT;
|
|
while(CmdRspStatusFlag != SHCI_TL_CMD_RESP_RELEASE);
|
|
|
|
return;
|
|
}
|
|
|
|
__WEAK void shci_cmd_resp_release(uint32_t flag)
|
|
{
|
|
(void)flag;
|
|
|
|
CmdRspStatusFlag = SHCI_TL_CMD_RESP_RELEASE;
|
|
|
|
return;
|
|
}
|