mirror of https://github.com/ARMmbed/mbed-os.git
parent
339846a1bb
commit
b74756dceb
|
|
@ -1,888 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#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"
|
||||
|
||||
#define TRACE_GROUP "BLE_WB"
|
||||
|
||||
/******************************************************************************
|
||||
* 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);
|
||||
static bool sysevt_check( 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("could not 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) {
|
||||
HciReadLocalVerInfoCmd();
|
||||
} else {
|
||||
/* initialize extended parameters */
|
||||
hciCoreCb.maxAdvDataLen = 0;
|
||||
hciCoreCb.numSupAdvSets = 0;
|
||||
hciCoreCb.perAdvListSize = 0;
|
||||
/* send next command in sequence */
|
||||
HciLeRandCmd();
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_READ_LOCAL_VER_INFO:
|
||||
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 {
|
||||
uint8_t addr[6] = { 0 };
|
||||
memcpy(addr, pMsg, sizeof(addr));
|
||||
DM_RAND_ADDR_SET(addr, DM_RAND_ADDR_STATIC);
|
||||
// note: will invoke set rand address
|
||||
cordio::BLE::deviceInstance().getGap().setAddress(
|
||||
BLEProtocol::AddressType::RANDOM_STATIC,
|
||||
addr
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_SET_RAND_ADDR:
|
||||
/* send next command in sequence */
|
||||
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<size_t N>
|
||||
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() {
|
||||
/* Check whether M0 sub-system was started already by
|
||||
* checking if the system event was already received
|
||||
* before. If it was not, then go thru all init. */
|
||||
if(!sysevt_check()) {
|
||||
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: Until enum is avalable
|
||||
// type 01 Command
|
||||
// type 02 ACL DATA
|
||||
// type 03 SCO Voice (not supported)
|
||||
// type 04 event - uplink (not suported)
|
||||
#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 {
|
||||
/* release immmediately, now that M0 runs */
|
||||
sys_event_sem.release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* returns true if ssyevt was already received, which means M0 core is
|
||||
* already up and running */
|
||||
static bool sysevt_check( void) {
|
||||
/* Check if system is UP and runing already */
|
||||
if(sys_event_sem.wait(10) < 1) {
|
||||
return false;
|
||||
} else {
|
||||
/* release immmediately as M0 already runs */
|
||||
sys_event_sem.release();
|
||||
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: ");
|
||||
/* In case of MBED debug profile, configure debugger support */
|
||||
#if (defined(MBED_DEBUG) || (CFG_DEBUGGER_SUPPORTED == 1))
|
||||
/**
|
||||
* 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);
|
||||
|
||||
gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3;
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOB, &gpio_config);
|
||||
|
||||
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("Cannot find BD ADDRESS to program - will leave hw default\r\n");
|
||||
bd_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bd_found;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,915 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#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"
|
||||
|
||||
#define TRACE_GROUP "BLE_WB"
|
||||
|
||||
/******************************************************************************
|
||||
* 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);
|
||||
static bool sysevt_check(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("could not 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) {
|
||||
HciReadLocalVerInfoCmd();
|
||||
} else {
|
||||
/* initialize extended parameters */
|
||||
hciCoreCb.maxAdvDataLen = 0;
|
||||
hciCoreCb.numSupAdvSets = 0;
|
||||
hciCoreCb.perAdvListSize = 0;
|
||||
/* send next command in sequence */
|
||||
HciLeRandCmd();
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_READ_LOCAL_VER_INFO:
|
||||
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 {
|
||||
uint8_t addr[6] = { 0 };
|
||||
memcpy(addr, pMsg, sizeof(addr));
|
||||
DM_RAND_ADDR_SET(addr, DM_RAND_ADDR_STATIC);
|
||||
// note: will invoke set rand address
|
||||
cordio::BLE::deviceInstance().getGap().setAddress(
|
||||
BLEProtocol::AddressType::RANDOM_STATIC,
|
||||
addr
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_SET_RAND_ADDR:
|
||||
/* send next command in sequence */
|
||||
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<size_t N>
|
||||
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()
|
||||
{
|
||||
/* Check whether M0 sub-system was started already by
|
||||
* checking if the system event was already received
|
||||
* before. If it was not, then go thru all init. */
|
||||
if (!sysevt_check()) {
|
||||
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: Until enum is avalable
|
||||
// type 01 Command
|
||||
// type 02 ACL DATA
|
||||
// type 03 SCO Voice (not supported)
|
||||
// type 04 event - uplink (not suported)
|
||||
#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 {
|
||||
/* release immmediately, now that M0 runs */
|
||||
sys_event_sem.release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* returns true if ssyevt was already received, which means M0 core is
|
||||
* already up and running */
|
||||
static bool sysevt_check(void)
|
||||
{
|
||||
/* Check if system is UP and runing already */
|
||||
if (sys_event_sem.wait(10) < 1) {
|
||||
return false;
|
||||
} else {
|
||||
/* release immmediately as M0 already runs */
|
||||
sys_event_sem.release();
|
||||
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: ");
|
||||
/* In case of MBED debug profile, configure debugger support */
|
||||
#if (defined(MBED_DEBUG) || (CFG_DEBUGGER_SUPPORTED == 1))
|
||||
/**
|
||||
* 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);
|
||||
|
||||
gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3;
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOB, &gpio_config);
|
||||
|
||||
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("Cannot find BD ADDRESS to program - will leave hw default\r\n");
|
||||
bd_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bd_found;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue