zephyr/drivers/bluetooth/hci/ipm_stm32wb.c

583 lines
15 KiB
C
Raw Normal View History

/* ipm_stm32wb.c - HCI driver for stm32wb shared ram */
/*
* Copyright (c) 2019 Linaro Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <init.h>
#include <sys/util.h>
#include <bluetooth/hci.h>
#include <drivers/bluetooth/hci_driver.h>
#include "bluetooth/addr.h"
#include <drivers/clock_control/stm32_clock_control.h>
#include "app_conf.h"
#include "stm32_wpan_common.h"
#include "shci.h"
#include "shci_tl.h"
#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH * 4 * \
DIVC((sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE), 4))
/* Private variables ---------------------------------------------------------*/
PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer;
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];
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t
HciAclDataBuffer[sizeof(TL_PacketHeader_t) + 5 + 251];
static void syscmd_status_not(SHCI_TL_CmdStatus_t status);
static void sysevt_received(void *pdata);
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME hci_ipm
#include "common/log.h"
#define HCI_CMD 0x01
#define HCI_ACL 0x02
#define HCI_SCO 0x03
#define HCI_EVT 0x04
#define STM32WB_C2_LOCK_TIMEOUT K_MSEC(500)
static K_SEM_DEFINE(c2_started, 0, 1);
static K_SEM_DEFINE(ble_sys_wait_cmd_rsp, 0, 1);
static K_SEM_DEFINE(acl_data_ack, 1, 1);
static K_SEM_DEFINE(ipm_busy, 1, 1);
struct aci_set_tx_power {
uint8_t cmd;
uint8_t value[2];
};
struct aci_set_ble_addr {
uint8_t config_offset;
uint8_t length;
uint8_t value[6];
} __packed;
#define ACI_WRITE_SET_TX_POWER_LEVEL BT_OP(BT_OGF_VS, 0xFC0F)
#define ACI_HAL_WRITE_CONFIG_DATA BT_OP(BT_OGF_VS, 0xFC0C)
#define HCI_CONFIG_DATA_PUBADDR_OFFSET 0
#define HCI_CONFIG_DATA_RANDOM_ADDRESS_OFFSET 0x2E
static bt_addr_t bd_addr_udn;
/* Rx thread definitions */
K_FIFO_DEFINE(ipm_rx_events_fifo);
static K_KERNEL_STACK_DEFINE(ipm_rx_stack, CONFIG_BT_STM32_IPM_RX_STACK_SIZE);
static struct k_thread ipm_rx_thread_data;
static 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_OPTIONS,
0 }
};
/**
* Starts the BLE Stack on CPU2
*/
SHCI_C2_BLE_Init(&ble_init_cmd_packet);
}
static void sysevt_received(void *pdata)
{
k_sem_give(&c2_started);
}
static void syscmd_status_not(SHCI_TL_CmdStatus_t status)
{
BT_DBG("status:%d", status);
}
/*
* https://github.com/zephyrproject-rtos/zephyr/issues/19509
* Tested on nucleo_wb55rg (stm32wb55rg) BLE stack (v1.2.0)
* Unresolved Resolvable Private Addresses (RPA)
* is reported in the peer_rpa field, and not in the peer address,
* as it should, when this happens the peer address is set to all FFs
* 0A 00 01 08 01 01 FF FF FF FF FF FF 00 00 00 00 00 00 0C AA C5 B3 3D 6B ...
* If such message is passed to HCI core than pairing will essentially fail.
* Solution: Rewrite the event with the RPA in the PEER address field
*/
static void tryfix_event(TL_Evt_t *tev)
{
struct bt_hci_evt_le_meta_event *mev = (void *)&tev->payload;
if (tev->evtcode != BT_HCI_EVT_LE_META_EVENT ||
mev->subevent != BT_HCI_EVT_LE_ENH_CONN_COMPLETE) {
return;
}
struct bt_hci_evt_le_enh_conn_complete *evt =
(void *)((uint8_t *)mev + (sizeof(*mev)));
if (!bt_addr_cmp(&evt->peer_addr.a, BT_ADDR_NONE)) {
BT_WARN("Invalid peer addr %s", bt_addr_le_str(&evt->peer_addr));
bt_addr_copy(&evt->peer_addr.a, &evt->peer_rpa);
evt->peer_addr.type = BT_ADDR_LE_RANDOM;
}
}
void TM_EvtReceivedCb(TL_EvtPacket_t *hcievt)
{
k_fifo_put(&ipm_rx_events_fifo, hcievt);
}
static void bt_ipm_rx_thread(void)
{
while (true) {
bool discardable = false;
k_timeout_t timeout = K_FOREVER;
static TL_EvtPacket_t *hcievt;
struct net_buf *buf = NULL;
struct bt_hci_acl_hdr acl_hdr;
TL_AclDataSerial_t *acl;
struct bt_hci_evt_le_meta_event *mev;
size_t buf_tailroom;
size_t buf_add_len;
hcievt = k_fifo_get(&ipm_rx_events_fifo, K_FOREVER);
k_sem_take(&ipm_busy, K_FOREVER);
switch (hcievt->evtserial.type) {
case HCI_EVT:
BT_DBG("EVT: hcievt->evtserial.evt.evtcode: 0x%02x",
hcievt->evtserial.evt.evtcode);
switch (hcievt->evtserial.evt.evtcode) {
case BT_HCI_EVT_VENDOR:
/* Vendor events are currently unsupported */
BT_ERR("Unknown evtcode type 0x%02x",
hcievt->evtserial.evt.evtcode);
TL_MM_EvtDone(hcievt);
goto end_loop;
default:
mev = (void *)&hcievt->evtserial.evt.payload;
if (hcievt->evtserial.evt.evtcode == BT_HCI_EVT_LE_META_EVENT &&
(mev->subevent == BT_HCI_EVT_LE_ADVERTISING_REPORT)) {
discardable = true;
timeout = K_NO_WAIT;
}
buf = bt_buf_get_evt(
hcievt->evtserial.evt.evtcode,
discardable, timeout);
if (!buf) {
BT_DBG("Discard adv report due to insufficient buf");
goto end_loop;
}
}
tryfix_event(&hcievt->evtserial.evt);
buf_tailroom = net_buf_tailroom(buf);
buf_add_len = hcievt->evtserial.evt.plen + 2;
if (buf_tailroom < buf_add_len) {
BT_ERR("Not enough space in buffer %zu/%zu",
buf_add_len, buf_tailroom);
net_buf_unref(buf);
goto end_loop;
}
net_buf_add_mem(buf, &hcievt->evtserial.evt,
buf_add_len);
break;
case HCI_ACL:
acl = &(((TL_AclDataPacket_t *)hcievt)->AclDataSerial);
buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER);
acl_hdr.handle = acl->handle;
acl_hdr.len = acl->length;
BT_DBG("ACL: handle %x, len %x",
acl_hdr.handle, acl_hdr.len);
net_buf_add_mem(buf, &acl_hdr, sizeof(acl_hdr));
buf_tailroom = net_buf_tailroom(buf);
buf_add_len = acl_hdr.len;
if (buf_tailroom < buf_add_len) {
BT_ERR("Not enough space in buffer %zu/%zu",
buf_add_len, buf_tailroom);
net_buf_unref(buf);
goto end_loop;
}
net_buf_add_mem(buf, (uint8_t *)&acl->acl_data,
buf_add_len);
break;
default:
BT_ERR("Unknown BT buf type %d",
hcievt->evtserial.type);
TL_MM_EvtDone(hcievt);
goto end_loop;
}
TL_MM_EvtDone(hcievt);
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
bt_recv(buf);
end_loop:
k_sem_give(&ipm_busy);
}
}
static void TM_AclDataAck(void)
{
k_sem_give(&acl_data_ack);
}
void shci_notify_asynch_evt(void *pdata)
{
shci_user_evt_proc();
}
void shci_cmd_resp_release(uint32_t flag)
{
k_sem_give(&ble_sys_wait_cmd_rsp);
}
void shci_cmd_resp_wait(uint32_t timeout)
{
k_sem_take(&ble_sys_wait_cmd_rsp, K_MSEC(timeout));
}
void ipcc_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 */
IRQ_CONNECT(IPCC_C1_RX_IRQn, 0, HW_IPCC_Rx_Handler, NULL, 0);
IRQ_CONNECT(IPCC_C1_TX_IRQn, 0, HW_IPCC_Tx_Handler, NULL, 0);
}
void transport_init(void)
{
TL_MM_Config_t tl_mm_config;
TL_BLE_InitConf_t tl_ble_config;
SHCI_TL_HciInitConf_t shci_init_config;
BT_DBG("BleCmdBuffer: %p", (void *)&BleCmdBuffer);
BT_DBG("HciAclDataBuffer: %p", (void *)&HciAclDataBuffer);
BT_DBG("SystemCmdBuffer: %p", (void *)&SystemCmdBuffer);
BT_DBG("EvtPool: %p", (void *)&EvtPool);
BT_DBG("SystemSpareEvtBuffer: %p", (void *)&SystemSpareEvtBuffer);
BT_DBG("BleSpareEvtBuffer: %p", (void *)&BleSpareEvtBuffer);
/**< Reference table initialization */
TL_Init();
/**< System channel initialization */
shci_init_config.p_cmdbuffer = (uint8_t *)&SystemCmdBuffer;
shci_init_config.StatusNotCallBack = syscmd_status_not;
shci_init(sysevt_received, (void *) &shci_init_config);
/**< Memory Manager channel initialization */
tl_mm_config.p_BleSpareEvtBuffer = BleSpareEvtBuffer;
tl_mm_config.p_SystemSpareEvtBuffer = SystemSpareEvtBuffer;
tl_mm_config.p_AsynchEvtPool = EvtPool;
tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
TL_MM_Init(&tl_mm_config);
/**< BLE channel initialization */
tl_ble_config.p_cmdbuffer = (uint8_t *)&BleCmdBuffer;
tl_ble_config.p_AclDataBuffer = HciAclDataBuffer;
tl_ble_config.IoBusEvtCallBack = TM_EvtReceivedCb;
tl_ble_config.IoBusAclDataTxAck = TM_AclDataAck;
TL_BLE_Init((void *)&tl_ble_config);
TL_Enable();
}
static int bt_ipm_send(struct net_buf *buf)
{
TL_CmdPacket_t *ble_cmd_buff = &BleCmdBuffer;
k_sem_take(&ipm_busy, K_FOREVER);
switch (bt_buf_get_type(buf)) {
case BT_BUF_ACL_OUT:
BT_DBG("ACL: buf %p type %u len %u", buf, bt_buf_get_type(buf),
buf->len);
k_sem_take(&acl_data_ack, K_FOREVER);
net_buf_push_u8(buf, HCI_ACL);
memcpy((void *)
&((TL_AclDataPacket_t *)HciAclDataBuffer)->AclDataSerial,
buf->data, buf->len);
TL_BLE_SendAclData(NULL, 0);
break;
case BT_BUF_CMD:
BT_DBG("CMD: buf %p type %u len %u", buf, bt_buf_get_type(buf),
buf->len);
ble_cmd_buff->cmdserial.type = HCI_CMD;
ble_cmd_buff->cmdserial.cmd.plen = buf->len;
memcpy((void *)&ble_cmd_buff->cmdserial.cmd, buf->data,
buf->len);
TL_BLE_SendCmd(NULL, 0);
break;
default:
k_sem_give(&ipm_busy);
BT_ERR("Unsupported type");
return -EINVAL;
}
k_sem_give(&ipm_busy);
net_buf_unref(buf);
return 0;
}
static void start_ble_rf(void)
{
if ((LL_RCC_IsActiveFlag_PINRST()) && (!LL_RCC_IsActiveFlag_SFTRST())) {
/* Simulate power off reset */
LL_PWR_EnableBkUpAccess();
LL_PWR_EnableBkUpAccess();
LL_RCC_ForceBackupDomainReset();
LL_RCC_ReleaseBackupDomainReset();
}
#if STM32_LSE_ENABLED
/* Select LSE clock */
LL_RCC_LSE_Enable();
while (!LL_RCC_LSE_IsReady()) {
}
/* Select wakeup source of BLE RF */
LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE);
LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
/* Switch OFF LSI */
LL_RCC_LSI2_Disable();
#else
LL_RCC_LSI2_Enable();
while (!LL_RCC_LSI2_IsReady()) {
}
/* Select wakeup source of BLE RF */
LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSI);
LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI);
#endif
/* Set RNG on HSI48 */
LL_RCC_HSI48_Enable();
while (!LL_RCC_HSI48_IsReady()) {
}
LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_HSI48);
}
bt_addr_t *bt_get_ble_addr(void)
{
bt_addr_t *bd_addr;
uint32_t udn;
uint32_t company_id;
uint32_t device_id;
/* Get the 64 bit Unique Device Number UID */
/* The UID is used by firmware to derive */
/* 48-bit Device Address EUI-48 */
udn = LL_FLASH_GetUDN();
if (udn != 0xFFFFFFFF) {
/* Get the ST Company ID */
company_id = LL_FLASH_GetSTCompanyID();
/* Get the STM32 Device ID */
device_id = LL_FLASH_GetDeviceID();
bd_addr_udn.val[0] = (uint8_t)(udn & 0x000000FF);
bd_addr_udn.val[1] = (uint8_t)((udn & 0x0000FF00) >> 8);
bd_addr_udn.val[2] = (uint8_t)((udn & 0x00FF0000) >> 16);
bd_addr_udn.val[3] = (uint8_t)device_id;
bd_addr_udn.val[4] = (uint8_t)(company_id & 0x000000FF);
bd_addr_udn.val[5] = (uint8_t)((company_id & 0x0000FF00) >> 8);
bd_addr = &bd_addr_udn;
} else {
bd_addr = NULL;
}
return bd_addr;
}
static int bt_ipm_set_addr(void)
{
bt_addr_t *uid_addr;
struct aci_set_ble_addr *param;
struct net_buf *buf, *rsp;
int err;
uid_addr = bt_get_ble_addr();
if (!uid_addr) {
return -ENOMSG;
}
buf = bt_hci_cmd_create(ACI_HAL_WRITE_CONFIG_DATA, sizeof(*param));
if (!buf) {
return -ENOBUFS;
}
param = net_buf_add(buf, sizeof(*param));
param->config_offset = HCI_CONFIG_DATA_PUBADDR_OFFSET;
param->length = 6;
param->value[0] = uid_addr->val[0];
param->value[1] = uid_addr->val[1];
param->value[2] = uid_addr->val[2];
param->value[3] = uid_addr->val[3];
param->value[4] = uid_addr->val[4];
param->value[5] = uid_addr->val[5];
err = bt_hci_cmd_send_sync(ACI_HAL_WRITE_CONFIG_DATA, buf, &rsp);
if (err) {
return err;
}
net_buf_unref(rsp);
return 0;
}
static int bt_ipm_ble_init(void)
{
struct aci_set_tx_power *param;
struct net_buf *buf, *rsp;
int err;
/* Send HCI_RESET */
err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp);
if (err) {
return err;
}
/* TDB: Something to do on reset complete? */
net_buf_unref(rsp);
err = bt_ipm_set_addr();
if (err) {
BT_ERR("Can't set BLE UID addr");
}
/* Send ACI_WRITE_SET_TX_POWER_LEVEL */
buf = bt_hci_cmd_create(ACI_WRITE_SET_TX_POWER_LEVEL, 3);
if (!buf) {
return -ENOBUFS;
}
param = net_buf_add(buf, sizeof(*param));
param->cmd = 0x0F;
param->value[0] = 0x18;
param->value[1] = 0x01;
err = bt_hci_cmd_send_sync(ACI_WRITE_SET_TX_POWER_LEVEL, buf, &rsp);
if (err) {
return err;
}
net_buf_unref(rsp);
return 0;
}
static int bt_ipm_open(void)
{
int err;
/* Start RX thread */
k_thread_create(&ipm_rx_thread_data, ipm_rx_stack,
K_KERNEL_STACK_SIZEOF(ipm_rx_stack),
(k_thread_entry_t)bt_ipm_rx_thread, NULL, NULL, NULL,
K_PRIO_COOP(CONFIG_BT_DRIVER_RX_HIGH_PRIO),
0, K_NO_WAIT);
err = bt_ipm_ble_init();
if (err) {
return err;
}
BT_DBG("IPM Channel Open Completed");
return 0;
}
static const struct bt_hci_driver drv = {
.name = "BT IPM",
.bus = BT_HCI_DRIVER_BUS_IPM,
.quirks = BT_QUIRK_NO_RESET,
.open = bt_ipm_open,
.send = bt_ipm_send,
};
static int _bt_ipm_init(const struct device *unused)
{
ARG_UNUSED(unused);
bt_hci_driver_register(&drv);
start_ble_rf();
/* Take BLE out of reset */
ipcc_reset();
transport_init();
/* Device will let us know when it's ready */
if (k_sem_take(&c2_started, STM32WB_C2_LOCK_TIMEOUT)) {
return -ETIMEDOUT;
}
BT_DBG("C2 unlocked");
stm32wb_start_ble();
return 0;
}
SYS_INIT(_bt_ipm_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);