From 6234b3578c43e6a82cae7743f86cff78a3cd647e Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Tue, 5 Feb 2019 18:44:38 +0100 Subject: [PATCH] NUCLEO_WB55RG: add Cordio HCI and Transport Layer driver The STM32WB Coridio driver includes: - the Cordio HCI driver handling the reset sequence. During reset sequence the TX POWER level is set and the BD address is defined if found in OTP or option bytes. The rest of the sequence is based on the standard CORDIO HCI driver example. - The Transport Layer part handles sending and receiving messages to the WB controller running on cortex-M0 of the STM32WB target. The messages are shared through shared memory and mailboxes system based on IPCC HW. --- .../targets/TARGET_STM/stm32wb_HCIDriver.cpp | 889 ++++++++++++++++++ 1 file changed, 889 insertions(+) create mode 100644 features/FEATURE_BLE/targets/TARGET_STM/stm32wb_HCIDriver.cpp diff --git a/features/FEATURE_BLE/targets/TARGET_STM/stm32wb_HCIDriver.cpp b/features/FEATURE_BLE/targets/TARGET_STM/stm32wb_HCIDriver.cpp new file mode 100644 index 0000000000..a1727adad9 --- /dev/null +++ b/features/FEATURE_BLE/targets/TARGET_STM/stm32wb_HCIDriver.cpp @@ -0,0 +1,889 @@ +/* + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "CordioBLE.h" +#include "CordioHCIDriver.h" +#include "CordioHCITransportDriver.h" +#include "mbed.h" +#include "hci_api.h" +#include "hci_cmd.h" +#include "hci_core.h" +#include "dm_api.h" +#include "bstream.h" +#include "hci_mbed_os_adaptation.h" +#include "mbed-trace/mbed_trace.h" + +/* STM32WB include files */ +#include "stm32wbxx_ll_ipcc.h" +#include "stm32wbxx_ll_system.h" +#include "tl.h" +#include "shci.h" +#include "shci_tl.h" +#include "hw.h" +#include "app_conf.h" +#include "otp.h" + + +/****************************************************************************** + * BLE config parameters + ******************************************************************************/ +/* Defined from WB Cube reference SW */ +#define CFG_TLBLE_EVT_QUEUE_LENGTH 5 +#define CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE 255 /**< Set to 255 with the memory manager and the mailbox */ +#define TL_BLE_EVENT_FRAME_SIZE ( TL_EVT_HDR_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE ) +#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH*4*DIVC(( sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE ), 4)) + +#define CONFIG_DATA_PUBADDR_OFFSET (0x00) /**< Bluetooth public address */ +#define CONFIG_DATA_PUBADDR_LEN (6) + +/* HCI related defines */ +#define HCI_RESET_RAND_CNT 4 +#define VENDOR_SPECIFIC_EVENT 0xFF +#define ACI_HAL_SET_TX_POWER_LEVEL 0xFC0F +#define ACI_WRITE_CONFIG_DATA_OPCODE 0xFC0C +#define ACI_READ_CONFIG_DATA_OPCODE 0xFC0D +#define MAX_HCI_ACL_PACKET_SIZE (sizeof(TL_PacketHeader_t) + 5 + 251) +#define MAX_HACI_EVT_SIZE (255+5) + +/* activate to add debug traces */ +#define PRINT_HCI_DATA 1 + + +/****************************************************************************** + * BLE config parameters + ******************************************************************************/ +static void evt_received(TL_EvtPacket_t *hcievt); +static void syscmd_status_not( SHCI_TL_CmdStatus_t status ); +static void sysevt_received(void *pdata); +static void acl_data_ack(void); +static bool acl_data_wait(void); +static void init_debug( void ); +static bool get_bd_address( uint8_t* bd_addr ); +static bool sysevt_wait( void); + + +namespace ble { +namespace vendor { +namespace stm32wb { + +/** + * stm32wb HCI driver implementation + * @see cordio::CordioHCIDriver + */ +class HCIDriver : public cordio::CordioHCIDriver +{ +public: + /** + * Construction of the HCIDriver. + * @param transport: Transport of the HCI commands. + * @param rst: Name of the reset pin + */ + HCIDriver( + cordio::CordioHCITransportDriver& transport_driver + ) : cordio::CordioHCIDriver(transport_driver) { } + + virtual cordio::buf_pool_desc_t get_buffer_pool_description(); + /** + * @see CordioHCIDriver::do_initialize + */ + virtual void do_initialize() { + // Nothig needed, init is only at transpot layer level + } + + /** + * @see CordioHCIDriver::do_terminate + */ + virtual void do_terminate() { + // Nothig needed, init is only at transpot layer level + } + + /** + * @see CordioHCIDriver::start_reset_sequence + */ + virtual void start_reset_sequence() { + /* send an HCI Reset command to start the sequence */ + HciResetCmd(); + } + + /** + * @see CordioHCIDriver::handle_reset_sequence + */ + virtual void handle_reset_sequence(uint8_t *pMsg) { + uint16_t opcode; + static uint8_t randCnt; + + /* if event is a command complete event */ + if (*pMsg == HCI_CMD_CMPL_EVT) + { +#if (PRINT_HCI_DATA == 1) + tr_debug("Command complete event\r\n"); + tr_debug("Command >> "); + for (uint8_t i = 0; i < 20; i++) { + tr_debug("%2X ", *((uint8_t *)pMsg + i)); + } + tr_debug("\r\n"); + /* parse parameters */ + tr_debug("HCI_EVT_HDR_LEN=%d\r\n", HCI_EVT_HDR_LEN); +#endif + pMsg += HCI_EVT_HDR_LEN; + pMsg++; /* skip num packets */ + BSTREAM_TO_UINT16(opcode, pMsg); + pMsg++; /* skip status */ + + /* decode opcode */ + switch (opcode) { + case HCI_OPCODE_RESET: + /* initialize rand command count */ + randCnt = 0; + tr_debug("WB Reset Received\r\n"); + /* Once reset complete evet is received we need + * to send a few more commands: + * Tx power and set bd addr + */ + if(get_bd_address(bd_addr)) { + aciWriteConfigData(CONFIG_DATA_PUBADDR_OFFSET, bd_addr); + tr_debug("Setting Bdaddr: %2x:%2x:%2x:%2x:%2x:%2x\r\n", + bd_addr[0], + bd_addr[1], + bd_addr[2], + bd_addr[3], + bd_addr[4], + bd_addr[5]); + } else { + tr_debug("couldn't find BDaddr\r\n"); + /* Skip to next step */ + aciSetTxPowerLevel(); + } + break; + + case ACI_WRITE_CONFIG_DATA_OPCODE: + tr_debug("BD address set\r\n"); + /* set the event mask to control which events are generated by the + * controller for the host */ + tr_debug("ACI_HAL_SET_TX_POWER_LEVEL\r\n"); + aciSetTxPowerLevel(); + break; + + + case ACI_HAL_SET_TX_POWER_LEVEL: + tr_debug("Tx Power Level set\r\n"); + //signal_reset_sequence_done(); + HciSetEventMaskCmd((uint8_t *) hciEventMask); + break; + + case HCI_OPCODE_SET_EVENT_MASK: + // set the event mask to control which LE events are generated by + // the controller for the host + HciLeSetEventMaskCmd((uint8_t *) hciLeEventMask); + break; + + case HCI_OPCODE_LE_SET_EVENT_MASK: + /* below command is not supported */ +#if COMMAND_NOT_SUPPORTED_SKIP_STEP + // set the event mask to control which events are generated by the + // controller for the host (2nd page of flags ) + HciSetEventMaskPage2Cmd((uint8_t *) hciEventMaskPage2); + break; + + case HCI_OPCODE_SET_EVENT_MASK_PAGE2: +#endif + // Ask the Bluetooth address of the controller + HciReadBdAddrCmd(); + break; + + case HCI_OPCODE_READ_BD_ADDR: + // Store the Bluetooth address in the stack runtime parameter + BdaCpy(hciCoreCb.bdAddr, pMsg); + // Read the size of the buffer of the controller + HciLeReadBufSizeCmd(); + break; + + case HCI_OPCODE_LE_READ_BUF_SIZE: + // Store the buffer parameters in the stack runtime parameters + BSTREAM_TO_UINT16(hciCoreCb.bufSize, pMsg); + BSTREAM_TO_UINT8(hciCoreCb.numBufs, pMsg); + /* initialize ACL buffer accounting */ + hciCoreCb.availBufs = hciCoreCb.numBufs; + // read the states and state combinations supported by the link + // layer of the controller + HciLeReadSupStatesCmd(); + break; + + case HCI_OPCODE_LE_READ_SUP_STATES: + // store supported state and combination in the runtime parameters + // of the stack + memcpy(hciCoreCb.leStates, pMsg, HCI_LE_STATES_LEN); + // read the total of whitelist entries that can be stored in the + // controller. + HciLeReadWhiteListSizeCmd(); + break; + + case HCI_OPCODE_LE_READ_WHITE_LIST_SIZE: + // store the number of whitelist entries in the stack runtime + // parameters + BSTREAM_TO_UINT8(hciCoreCb.whiteListSize, pMsg); + + // Read the LE features supported by the controller + HciLeReadLocalSupFeatCmd(); + break; + + case HCI_OPCODE_LE_READ_LOCAL_SUP_FEAT: + // Store the set of LE features supported by the controller + BSTREAM_TO_UINT16(hciCoreCb.leSupFeat, pMsg); + // read the total number of address translation entries which can be + // stored in the controller resolving list. + hciCoreReadResolvingListSize(); + break; + + case HCI_OPCODE_LE_READ_RES_LIST_SIZE: + // store the number of address translation entries in the stack + // runtime parameter + BSTREAM_TO_UINT8(hciCoreCb.resListSize, pMsg); + + // read the Controller’s maximum supported payload octets and packet + // duration times for transmission and reception + hciCoreReadMaxDataLen(); + break; + + case HCI_OPCODE_LE_READ_MAX_DATA_LEN: + { + // store payload definition in the runtime stack parameters. + uint16_t maxTxOctets; + uint16_t maxTxTime; + + BSTREAM_TO_UINT16(maxTxOctets, pMsg); + BSTREAM_TO_UINT16(maxTxTime, pMsg); + + /* use Controller's maximum supported payload octets and packet duration times + * for transmission as Host's suggested values for maximum transmission number + * of payload octets and maximum packet transmission time for new connections. + */ + HciLeWriteDefDataLen(maxTxOctets, maxTxTime); + } + break; + + case HCI_OPCODE_LE_WRITE_DEF_DATA_LEN: + if (hciCoreCb.extResetSeq) + { + /* send first extended command */ + (*hciCoreCb.extResetSeq)(pMsg, opcode); + } + else + { + /* initialize extended parameters */ + hciCoreCb.maxAdvDataLen = 0; + hciCoreCb.numSupAdvSets = 0; + hciCoreCb.perAdvListSize = 0; + /* send next command in sequence */ + HciLeRandCmd(); + } + break; + + case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN: + case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS: + case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE: + // handle extended command + if (hciCoreCb.extResetSeq) + { + /* send next extended command in sequence */ + (*hciCoreCb.extResetSeq)(pMsg, opcode); + } + break; + + case HCI_OPCODE_LE_RAND: + /* check if need to send second rand command */ + if (randCnt < (HCI_RESET_RAND_CNT-1)) { + randCnt++; + HciLeRandCmd(); + } else { + /* last command in sequence; set resetting state and call callback */ + tr_debug("signal_reset_sequence_done\r\n"); + signal_reset_sequence_done(); + } + break; + default: + tr_debug("Complete Event in reset seq with unknown opcode =0x%4X\r\n", opcode); + break; + } + } else if (*pMsg == HCI_CMD_STATUS_EVT) { + uint8_t status; + /* get status */ + /* parse parameters */ + pMsg += HCI_EVT_HDR_LEN; + status = *pMsg; + pMsg++; + pMsg++; /* skip num packets */ + BSTREAM_TO_UINT16(opcode, pMsg); + tr_debug("Command Status event, status:%d, opcode=0x%4X\r\n", status, opcode); + } else { + /** + * vendor specific event + */ + if (pMsg[0] == VENDOR_SPECIFIC_EVENT) { + /* parse parameters */ + pMsg += HCI_EVT_HDR_LEN; + BSTREAM_TO_UINT16(opcode, pMsg); + tr_debug("Vendor specific event, opcode=0x%4X\r\n", opcode); + } else { + tr_debug("Unknown event %d!\r\n", pMsg[0]); + } + } + } + +private: + uint8_t bd_addr[6]; + void aciSetTxPowerLevel() { + uint8_t *pBuf = hciCmdAlloc(ACI_HAL_SET_TX_POWER_LEVEL, 2); + if (!pBuf) { + return; + } + pBuf[HCI_CMD_HDR_LEN] = 0x1; + pBuf[HCI_CMD_HDR_LEN+1] = 0x18; + hciCmdSend(pBuf); + } + + void aciReadConfigParameter(uint8_t offset) { + uint8_t *pBuf = hciCmdAlloc(ACI_READ_CONFIG_DATA_OPCODE, 1); + if (!pBuf) { + return; + } + + pBuf[3] = offset; + hciCmdSend(pBuf); + } + + template + void aciWriteConfigData(uint8_t offset, uint8_t (&buf)[N]) { + uint8_t *pBuf = hciCmdAlloc(ACI_WRITE_CONFIG_DATA_OPCODE, 2 + N); + if (!pBuf) { + return; + } + + pBuf[3] = offset; + pBuf[4] = N; + memcpy(pBuf + 5, buf, N); + hciCmdSend(pBuf); + } + + void hciCoreReadResolvingListSize(void) + { + /* if LL Privacy is supported by Controller and included */ + if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_PRIVACY) && + (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_PRIVACY)) + { + /* send next command in sequence */ + HciLeReadResolvingListSize(); + } + else + { + hciCoreCb.resListSize = 0; + + /* send next command in sequence */ + hciCoreReadMaxDataLen(); + } + } + + void hciCoreReadMaxDataLen(void) + { + /* if LE Data Packet Length Extensions is supported by Controller and included */ + if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_DATA_LEN_EXT) && + (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_DATA_LEN_EXT)) + { + /* send next command in sequence */ + HciLeReadMaxDataLen(); + } + else + { + /* send next command in sequence */ + HciLeRandCmd(); + } + } +}; + +ble::vendor::cordio::buf_pool_desc_t ble::vendor::stm32wb::HCIDriver::get_buffer_pool_description() +{ + // Use default buffer pool + return ble::vendor::cordio::CordioHCIDriver::get_default_buffer_pool_description(); +} + + + +class TransportDriver : public cordio::CordioHCITransportDriver +{ +public: + TransportDriver(TL_CmdPacket_t *BleCmdBuffer, TL_CmdPacket_t *SystemCmdBuffer, uint8_t *EvtPool, uint8_t *SystemSpareEvtBuffer, uint8_t *BleSpareEvtBuffer, uint8_t *HciAclDataBuffer) { + + bleCmdBuf = BleCmdBuffer; + sysCmdBuf = SystemCmdBuffer; + evtPool = EvtPool; + sysSpareEvtBuf = SystemSpareEvtBuffer; + bleSpareEvtBuf = BleSpareEvtBuffer; + aclDataBuffer = HciAclDataBuffer; + } + + virtual ~TransportDriver() { } + + /** + * @see CordioHCITransportDriver::initialize + */ + virtual void initialize() + { + init_debug(); + stm32wb_reset(); + transport_init(); + } + + /** + * @see CordioHCITransportDriver::terminate + */ + virtual void terminate() { } + + /** + * @see CordioHCITransportDriver::write + */ + virtual uint16_t write(uint8_t type, uint16_t len, uint8_t *pData) + { + return mbox_write(type, len, pData); + } + +private: + void transport_init(void) { + TL_MM_Config_t tl_mm_config; + TL_BLE_InitConf_t tl_ble_Config; + /* STM32WB offers a System Channel HCI interface for + offering system services, with proprietary commands. + System Channel must be used as well for starting up + BLE service so we need to initialize it. */ + SHCI_TL_HciInitConf_t shci_init_config; + + /**< Reference table initialization */ + TL_Init(); + + /**< System channel initialization */ + shci_init_config.p_cmdbuffer = (uint8_t*)sysCmdBuf; + shci_init_config.StatusNotCallBack = syscmd_status_not; + shci_init(sysevt_received, (void*) &shci_init_config); + + /**< Memory Manager channel initialization */ + tl_mm_config.p_BleSpareEvtBuffer = bleSpareEvtBuf; + tl_mm_config.p_SystemSpareEvtBuffer = sysSpareEvtBuf; + tl_mm_config.p_AsynchEvtPool = evtPool; + tl_mm_config.AsynchEvtPoolSize = POOL_SIZE; + TL_MM_Init(&tl_mm_config); + + TL_Enable(); + + /* At this stage, we'll need to wait for ready event, + * passed thru TL_SYS_EvtReceived */ + if(!sysevt_wait()) { + tr_debug("ERROR booting WB controler\r\n"); + return; + } + + // TO DO : check if we need to disable LPM + // requires to import as well all lpm driver + tl_ble_Config.p_AclDataBuffer = aclDataBuffer; + tl_ble_Config.IoBusAclDataTxAck = acl_data_ack; + tl_ble_Config.p_cmdbuffer = (uint8_t *)bleCmdBuf; + tl_ble_Config.IoBusEvtCallBack = evt_received; + TL_BLE_Init(&tl_ble_Config); + + + /* Now start BLE service on firmware side, using Vendor specific + * command on the System Channe + */ + stm32wb_start_ble(); + } + + uint16_t mbox_write(uint8_t type, uint16_t len, uint8_t *pData) { + // Note: + // 01 command + // type 02 ACL DATA not yet supported + // type 03 SCO Voice + // type 04 event - sens remontant + // BleCmdBuffer.cmdserial.cmd.cmdcode = opcode; +#if (PRINT_HCI_DATA == 1) + tr_debug("mbox_write type:%d, len:%d\r\n", type, len); +#endif + /* TO DO : MANAGE ACL DATA CASE in separate buffer */ + switch(type) { + case 1://BLE command + bleCmdBuf->cmdserial.type = type; // for now this param is overwritten in TL_BLE_SendCmd + memcpy( (void*) &bleCmdBuf->cmdserial.cmd, pData, len ); +#if (PRINT_HCI_DATA == 1) + /* We're tracing here the command, after copy in shared mem but before + * * M0 trigger. */ + tr_debug("TX>> BLE CMD: "); + /* Trace the buffer including Type (+1 on lngth) */ + tr_debug("Type %2X ", bleCmdBuf->cmdserial.type); + tr_debug("Cmd %4X ", bleCmdBuf->cmdserial.cmd.cmdcode); + tr_debug("Len %2X ", bleCmdBuf->cmdserial.cmd.plen); + tr_debug("Payload "); + for (uint8_t i = 0; i < bleCmdBuf->cmdserial.cmd.plen; i++) { + tr_debug("%2X ", *(((uint8_t *)&bleCmdBuf->cmdserial.cmd.payload) + i)); + } + tr_debug("\r\n"); +#endif + TL_BLE_SendCmd(NULL,0); // unused parameters for now + break; + case 2://ACL DATA + if (!acl_data_wait()) { +#if (PRINT_HCI_DATA == 1) + tr_debug("ERROR: previous ACL message not ACK'd\r\n"); +#endif + /* return number of bytes sent, 0 in this error case */ + return 0; + } + TL_AclDataSerial_t* aclDataSerial = (TL_AclDataSerial_t*) (aclDataBuffer + sizeof(TL_PacketHeader_t)); + aclDataSerial->type = type; // for now this param is overwritten in TL_BLE_SendCmd + memcpy(aclDataBuffer + + sizeof(TL_PacketHeader_t) + sizeof(type), pData, len); + TL_BLE_SendAclData(NULL, 0); // unused parameters for now +#if (PRINT_HCI_DATA == 1) + tr_debug(" TX>> BLE ACL: "); + /* Trace the buffer for debug purpose */ + for (uint8_t i = 0; i < len+1+8; i++) { + tr_debug("%2X ", *(((uint8_t*) aclDataBuffer) + i)); + } + tr_debug("\r\n"); +#endif + break; + } + return len; + } + + void stm32wb_reset(void) + { + // Reset IPCC + LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC); + + LL_C1_IPCC_ClearFlag_CHx( + IPCC, + LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 + | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + + LL_C2_IPCC_ClearFlag_CHx( + IPCC, + LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 + | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + + LL_C1_IPCC_DisableTransmitChannel( + IPCC, + LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 + | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + + LL_C2_IPCC_DisableTransmitChannel( + IPCC, + LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 + | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + + LL_C1_IPCC_DisableReceiveChannel( + IPCC, + LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 + | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + + LL_C2_IPCC_DisableReceiveChannel( + IPCC, + LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 + | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + + /* Set IPCC default IRQ handlers */ + NVIC_SetVector(IPCC_C1_TX_IRQn, (uint32_t)HW_IPCC_Tx_Handler); + NVIC_SetVector(IPCC_C1_RX_IRQn, (uint32_t)HW_IPCC_Rx_Handler); + + return; + } // stm32wb_reset + + void stm32wb_start_ble(void) { + SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = + { + 0,0,0, /**< Header unused */ + 0, /** pBleBufferAddress not used */ + 0, /** BleBufferSize not used */ + CFG_BLE_NUM_GATT_ATTRIBUTES, + CFG_BLE_NUM_GATT_SERVICES, + CFG_BLE_ATT_VALUE_ARRAY_SIZE, + CFG_BLE_NUM_LINK, + CFG_BLE_DATA_LENGTH_EXTENSION, + CFG_BLE_PREPARE_WRITE_LIST_SIZE, + CFG_BLE_MBLOCK_COUNT, + CFG_BLE_MAX_ATT_MTU, + CFG_BLE_SLAVE_SCA, + CFG_BLE_MASTER_SCA, + CFG_BLE_LSE_SOURCE, + CFG_BLE_MAX_CONN_EVENT_LENGTH, + CFG_BLE_HSE_STARTUP_TIME, + CFG_BLE_VITERBI_MODE, + CFG_BLE_LL_ONLY, + 0 /** TODO Should be read from HW */ + }; + /** + * Starts the BLE Stack on CPU2 + */ + SHCI_C2_BLE_Init( &ble_init_cmd_packet ); + } + + TL_CmdPacket_t *bleCmdBuf; + TL_CmdPacket_t *sysCmdBuf; + uint8_t *evtPool; + uint8_t *sysSpareEvtBuf; + uint8_t *aclDataBuffer; + uint8_t *bleSpareEvtBuf; +}; // class TransportDriver + +} // namespace stm32wb +} // namespace vendor +} // namespace ble + +/* There must be only 1 instance of the Transport Driver in STM32WB + * and the command buffers needs to be located in correct memory areas + */ + +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; + +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t HciAclDataBuffer[MAX_HCI_ACL_PACKET_SIZE]; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t EvtPool[POOL_SIZE]; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t SystemCmdBuffer; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t SystemSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t BleSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; + + +/** + * Cordio HCI driver factory + */ +ble::vendor::cordio::CordioHCIDriver& ble_cordio_get_hci_driver() { + static ble::vendor::stm32wb::TransportDriver transport_driver( + &BleCmdBuffer, + &SystemCmdBuffer, + EvtPool, + SystemSpareEvtBuffer, + BleSpareEvtBuffer, + HciAclDataBuffer + ); + + static ble::vendor::stm32wb::HCIDriver hci_driver( + transport_driver /* other hci driver parameters */ + ); + + return hci_driver; +} + +static void evt_received(TL_EvtPacket_t *hcievt) { + uint16_t len = 0; + + // We need to memcpy the data before passing to higher layers. + switch (hcievt->evtserial.type) { + case TL_BLEEVT_PKT_TYPE: + len = hcievt->evtserial.evt.plen + TL_EVT_HDR_SIZE; + ble::vendor::stm32wb::TransportDriver::on_data_received((uint8_t *)&hcievt->evtserial, len); + break; + case TL_ACL_DATA_PKT_TYPE: + { + TL_AclDataSerial_t *acl = &(((TL_AclDataPacket_t *)hcievt)->AclDataSerial); + len = acl->length + 5; + ble::vendor::stm32wb::TransportDriver::on_data_received((uint8_t *)acl, len); + } + break; + default: + // should not happen - let's block to check + tr_error("BLE TL evt_received, wrong type:%d \r\n"); + break; + } + + /* In case Event belongs to the Evt Pool we need to inform */ + if (((uint8_t*)hcievt >= EvtPool) && ((uint8_t*)hcievt < (EvtPool + POOL_SIZE))) { + /* Free the message from shared memory */ + TL_MM_EvtDone(hcievt); + } +} + +/** + * TL Mailbox synchronisation means + */ + +/* Using Semaphore to implemented blocking cmd/resp on system channel */ +static rtos::Semaphore sys_event_sem(0, 1); +static rtos::Semaphore sys_resp_sem(0, 1); +static rtos::Semaphore acl_ack_sem(1, 1); + +static void acl_data_ack(void) { + /** + * The current implementation assumes the tackGUI will not send a new HCI ACL DATA packet before this ack is received + * ( which means the CPU2 has handled the previous packet ) + * In order to implement a secure mechanism, it is required either + * - a flow control with the stack + * - a local pool of buffer to store packets received from the stack + */ + acl_ack_sem.release(); + return; +} + +static bool acl_data_wait(void) { + + /* Wait 10 sec for previous ACL command to be ack'ed by Low Layers + * before sending the next one */ + if(acl_ack_sem.wait(10000) < 1) { + return false; + } else { + return true; + } +} + +/* WEAK callbacks from the BLE TL driver - will be called under Interrupt */ +static void sysevt_received( void* pdata) { + /* For now only READY event is received, so we know this is it */ + sys_event_sem.release(); + /* But later on ... we'll have to parse the answer */ + return; + } + +/* returns true if ssyevt was received, false otherwise */ +static bool sysevt_wait( void) { + /* Wait for 10sec max - if not return an error */ + if(sys_event_sem.wait(10000) < 1) { + return false; + } else { + return true; + } +} + +static void syscmd_status_not( SHCI_TL_CmdStatus_t status ) +{ + tr_debug("syscmd_status_not, status:%d\r\n", status); + return; +} + +void shci_notify_asynch_evt(void* pdata) +{ + /* Need to parse data in future version */ + shci_user_evt_proc(); + return; +} + +void shci_cmd_resp_release(uint32_t flag) +{ + sys_resp_sem.release(); + return; +} + +void shci_cmd_resp_wait(uint32_t timeout) +{ + /* TO DO: manage timeouts if we can return an error */ + if(sys_resp_sem.wait(timeout) < 1) { + tr_error("shci_cmd_resp_wait timed out\r\n"); + } +} + +void shci_register_io_bus(tSHciIO* fops) +{ + /* Register IO bus services */ + fops->Init = TL_SYS_Init; + fops->Send = TL_SYS_SendCmd; +} + +/** + * Few utilities functions + */ +static void init_debug( void ) +{ + tr_debug("WB init_debug: "); +#if (CFG_DEBUGGER_SUPPORTED == 1) + tr_debug("ENABLED\r\n"); + /** + * Keep debugger enabled while in any low power mode + */ + HAL_DBGMCU_EnableDBGSleepMode(); + HAL_DBGMCU_EnableDBGStopMode(); + HAL_DBGMCU_EnableDBGStandbyMode(); + + /***************** ENABLE DEBUGGER *************************************/ + LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + LL_C2_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + +#else + tr_debug("DISABLED\r\n"); + + GPIO_InitTypeDef gpio_config = {0}; + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_ANALOG; + + gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13; + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + __HAL_RCC_GPIOA_CLK_DISABLE(); + + gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3; + __HAL_RCC_GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + __HAL_RCC_GPIOB_CLK_DISABLE(); + + HAL_DBGMCU_DisableDBGSleepMode(); + HAL_DBGMCU_DisableDBGStopMode(); + HAL_DBGMCU_DisableDBGStandbyMode(); + +#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */ + + return; +} + +/* This function fills in a BD address table */ +bool get_bd_address( uint8_t* bd_addr ) +{ + uint8_t *otp_addr; + uint32_t udn; + uint32_t company_id; + uint32_t device_id; + bool bd_found; + + udn = LL_FLASH_GetUDN(); + + if(udn != 0xFFFFFFFF) + { + tr_debug("Found UDN: 0x%8lX\r\n", udn); + + company_id = LL_FLASH_GetSTCompanyID(); + device_id = LL_FLASH_GetDeviceID(); + + bd_addr[0] = (uint8_t)(udn & 0x000000FF); + bd_addr[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); + bd_addr[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); + bd_addr[3] = (uint8_t)device_id; + bd_addr[4] = (uint8_t)(company_id & 0x000000FF); + bd_addr[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); + + bd_found = true; + } + else + { + otp_addr = OTP_Read(0); + if(otp_addr) + { + memcpy(bd_addr, ((OTP_ID0_t*)otp_addr)->bd_address, CONFIG_DATA_PUBADDR_LEN); + bd_found = false; + } + else + { + tr_debug("Can't find BD ADDRESS to program - will leave hw default\r\n"); + bd_found = true; + } + } + + return bd_found; +} +