diff options
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/transport')
28 files changed, 4898 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/.gitignore b/src/libs/mynewt-nimble/nimble/transport/da1469x/.gitignore new file mode 100644 index 00000000..bba9f995 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/.gitignore @@ -0,0 +1,2 @@ +/src/libble_stack_da1469x.a + diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/README b/src/libs/mynewt-nimble/nimble/transport/da1469x/README new file mode 100644 index 00000000..b30a01c4 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/README @@ -0,0 +1,13 @@ +Integrated BLE Controller (CMAC) requires a binary firmware to be loaded. Such +firmware is distributed by Dialog Semiconductor in SDK package which has to be +obtained separately, see: +https://www.dialog-semiconductor.com/products/da1469x-product-family + +Firmware is available as part of following library in SDK package: +sdk/interfaces/ble/binaries/DA1469x-Release/libble_stack_da1469x.a + +By default, CMAC driver will look for this file in its package root directory +(i.e. nimble/transport/da1469x/cmac_driver) but this can be changed using +CMAC_IMAGE_FILE_NAME syscfg variable. + +Current version of CMAC driver was tested with SDK version 10.0.4.66. diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/include/cmac_driver/cmac_host.h b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/include/cmac_driver/cmac_host.h new file mode 100644 index 00000000..f623a48a --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/include/cmac_driver/cmac_host.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef __CMAC_HOST_H_ +#define __CMAC_HOST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (* cmac_mbox_read_cb)(const uint8_t *, uint16_t len); + +void cmac_host_init(void); +void cmac_mbox_write(const uint8_t *buf, size_t len); +void cmac_mbox_set_read_cb(cmac_mbox_read_cb cb); + +#ifdef __cplusplus +} +#endif + +#endif /* __CMAC_HOST_H_ */ diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/pkg.yml new file mode 100644 index 00000000..90a89318 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport/da1469x/cmac_driver +pkg.description: Driver for Dialog's BLE controller +pkg.author: +pkg.homepage: +pkg.keywords: + +pkg.pre_link_cmds.BLE_HOST: + scripts/build_libcmac.sh: 100 diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/scripts/build_libcmac.sh b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/scripts/build_libcmac.sh new file mode 100755 index 00000000..659c2b9f --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/scripts/build_libcmac.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +if [ ! -f ${MYNEWT_VAL_CMAC_IMAGE_FILE_NAME} ]; then + >&2 echo ERROR: BLE stack library not found. Please check nimble/transport/da1469x/README for details. + exit 1 +fi + +OBJCOPY=${MYNEWT_OBJCOPY_PATH} +AR=${MYNEWT_AR_PATH} +LIBBLE_A=$(readlink -e ${MYNEWT_VAL_CMAC_IMAGE_FILE_NAME}) +LIBCMAC_A=${MYNEWT_USER_SRC_DIR}/libcmac.a + +BASENAME_ROM=cmac.rom +BASENAME_RAM=cmac.ram + +cd ${MYNEWT_USER_WORK_DIR} + +# Extract firmware binary from .a since we do not need to link all other +# objects with our image. +${AR} x ${LIBBLE_A} cmac_fw.c.obj +${OBJCOPY} -O binary --only-section=.cmi_fw_area cmac_fw.c.obj cmac.bin + +# We need separate copies for RAM and ROM image since section names are derived +# from file names. For now files are the same, but it would be possible to +# link ROM image without data and shared sections which contains zeroes anyway. +cp cmac.bin ${BASENAME_ROM}.bin +cp cmac.bin ${BASENAME_RAM}.bin + +# Convert both binaries to objects and create archive to link +${OBJCOPY} -I binary -O elf32-littlearm -B armv8-m.main \ + --rename-section .data=.libcmac.rom ${BASENAME_ROM}.bin ${BASENAME_ROM}.o +${OBJCOPY} -I binary -O elf32-littlearm -B armv8-m.main \ + --rename-section .data=.libcmac.ram ${BASENAME_RAM}.bin ${BASENAME_RAM}.o +${AR} -rcs ${LIBCMAC_A} ${BASENAME_ROM}.o ${BASENAME_RAM}.o diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/src/cmac_host.c b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/src/cmac_host.c new file mode 100644 index 00000000..0bfb715e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/src/cmac_host.c @@ -0,0 +1,326 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include "os/os.h" +#include "mcu/cmsis_nvic.h" +#include "mcu/da1469x_lpclk.h" +#include "mcu/da1469x_hal.h" +#include "mcu/da1469x_pdc.h" +#include "mcu/mcu.h" +#include "cmac_driver/cmac_host.h" + +#define CMAC_SYM_CONFIG ((void *)(0x00818f20 + MEMCTRL->CMI_CODE_BASE_REG)) +#define CMAC_SYM_CONFIG_DYN ((void *)(0x00821af8 + MEMCTRL->CMI_CODE_BASE_REG)) +#define CMAC_SYM_MBOX_RX ((void *)(0x008216b0 + MEMCTRL->CMI_CODE_BASE_REG)) +#define CMAC_SYM_MBOX_TX ((void *)(0x008218b0 + MEMCTRL->CMI_CODE_BASE_REG)) + +#define CMAC_MBOX_SIZE 504 +#define CMAC_MBOX_F_RESET 0x0008 +#define CMAC_MBOX_F_WRITEPENDING 0x0010 + +struct cmac_config { + uint8_t bdaddr[6]; /* Device address */ + + uint8_t rf_calibration_delay; + + uint8_t lp_clock_freq; /* Sleep clock frequency (0 = 32768Hz, 1 = 32000Hz) */ + uint16_t lp_clock_sca; /* Sleep clock accuracy [ppm] */ + + uint16_t rx_buf_len; /* RX buffer size */ + uint16_t tx_buf_len; /* TX buffer size */ + bool initial_length_req; + + /* Channel assessment algorithm settings */ + uint16_t chan_assess_itvl; + uint8_t chan_assess_itvl_mult; + int8_t chan_assess_min_rssi; + uint16_t chan_assess_pkt_num; + uint16_t chan_assess_bad_pkt_num; + + /* Calibration settings */ + uint8_t system_tcs_length; + uint8_t synth_tcs_length; + uint8_t rfcu_tcs_length; + + uint8_t default_tx_power; /* Default TX power for connection/advertising */ + bool filter_dup_ov_discard; /* Discard unknown devices when filter buffer is full */ + bool use_hp_1m; + bool use_hp_2m; +}; + +struct cmac_config_dynamic { + bool enable_sleep; /* Enable sleep */ + + /* More options here, don't care now */ +}; + +struct cmac_mbox { + volatile uint16_t magic; + volatile uint16_t flags; + volatile uint16_t wr_off; + volatile uint16_t rd_off; + uint8_t data[CMAC_MBOX_SIZE]; +}; + +/* CMAC data */ +extern char _binary_cmac_rom_bin_start[]; +extern char _binary_cmac_rom_bin_end; +extern char _binary_cmac_ram_bin_start[]; +extern char _binary_cmac_ram_bin_end; + +struct cmac_image_info { + uint32_t _dummy1; + uint32_t size_rom; + uint32_t _dummy2; + uint32_t magic; + uint32_t _dummy3[5]; +}; + +/* Mailboxes for SYS<->CMAC communication */ +static struct cmac_mbox *cmac_mbox_rx; +static struct cmac_mbox *cmac_mbox_tx; + +/* PDC entry for waking up CMAC */ +static int8_t g_cmac_host_pdc_sys2cmac; +/* PDC entry for waking up M33 */ +static int8_t g_cmac_host_pdc_cmac2sys; +/* Callback for data ready from CMAC */ +static cmac_mbox_read_cb g_cmac_mbox_read_cb; + +static inline void +cmac_host_signal2cmac(void) +{ + da1469x_pdc_set(g_cmac_host_pdc_sys2cmac); +} + +static void +cmac2sys_isr(void) +{ + uint16_t wr_off; + uint16_t rd_off; + uint16_t chunk; + uint16_t len; + + os_trace_isr_enter(); + + /* Clear CMAC2SYS interrupt */ + *(volatile uint32_t *)0x40002000 = 2; + + if (*(volatile uint32_t *)0x40002000 & 0x1c00) { + /* XXX CMAC is in error state, need to recover */ + assert(0); + } + + if (cmac_mbox_rx->flags & CMAC_MBOX_F_RESET) { + cmac_mbox_rx->flags &= ~CMAC_MBOX_F_RESET; + goto done; + } + + if (!g_cmac_mbox_read_cb) { + cmac_mbox_rx->rd_off = cmac_mbox_rx->wr_off; + goto done; + } + + do { + rd_off = cmac_mbox_rx->rd_off; + wr_off = cmac_mbox_rx->wr_off; + + if (rd_off <= wr_off) { + chunk = wr_off - rd_off; + } else { + chunk = CMAC_MBOX_SIZE - rd_off; + } + + while (chunk) { + len = g_cmac_mbox_read_cb(&cmac_mbox_rx->data[rd_off], chunk); + + rd_off += len; + chunk -= len; + }; + + cmac_mbox_rx->rd_off = rd_off % CMAC_MBOX_SIZE; + } while (cmac_mbox_rx->rd_off != cmac_mbox_rx->wr_off); + +done: + + if (cmac_mbox_rx->flags & CMAC_MBOX_F_WRITEPENDING) { + cmac_host_signal2cmac(); + } + + os_trace_isr_exit(); +} + +static void +cmac_host_lpclk_cb(uint32_t freq) +{ + struct cmac_config_dynamic *cmac_config_dyn; + + cmac_config_dyn = CMAC_SYM_CONFIG_DYN; + cmac_config_dyn->enable_sleep = freq == 32768; +} + +void +cmac_mbox_write(const uint8_t *buf, size_t len) +{ + uint16_t wr_off; + uint16_t rd_off; + uint16_t chunk; + uint32_t primask; + + __HAL_DISABLE_INTERRUPTS(primask); + + while (len) { + rd_off = cmac_mbox_tx->rd_off; + wr_off = cmac_mbox_tx->wr_off; + + if (rd_off > wr_off) { + chunk = min(len, rd_off - wr_off); + } else { + chunk = min(len, CMAC_MBOX_SIZE - wr_off); + } + + if (chunk == 0) { + continue; + } + + memcpy(&cmac_mbox_tx->data[wr_off], buf, chunk); + + wr_off += chunk; + cmac_mbox_tx->wr_off = wr_off % CMAC_MBOX_SIZE; + + cmac_host_signal2cmac(); + + len -= chunk; + buf += chunk; + } + + __HAL_ENABLE_INTERRUPTS(primask); +} + +void +cmac_mbox_set_read_cb(cmac_mbox_read_cb cb) +{ + g_cmac_mbox_read_cb = cb; +} + +void +cmac_host_init(void) +{ + struct cmac_image_info ii; + uint32_t cmac_rom_size; + uint32_t cmac_ram_size; + struct cmac_config *cmac_config; + struct cmac_config_dynamic *cmac_config_dyn; + + /* Add PDC entry to wake up CMAC from M33 */ + g_cmac_host_pdc_sys2cmac = da1469x_pdc_add(MCU_PDC_TRIGGER_MAC_TIMER, + MCU_PDC_MASTER_CMAC, + MCU_PDC_EN_XTAL); + da1469x_pdc_set(g_cmac_host_pdc_sys2cmac); + da1469x_pdc_ack(g_cmac_host_pdc_sys2cmac); + + /* Add PDC entry to wake up M33 from CMAC, if does not exist yet */ + g_cmac_host_pdc_cmac2sys = da1469x_pdc_find(MCU_PDC_TRIGGER_COMBO, + MCU_PDC_MASTER_M33, 0); + if (g_cmac_host_pdc_cmac2sys < 0) { + g_cmac_host_pdc_cmac2sys = da1469x_pdc_add(MCU_PDC_TRIGGER_COMBO, + MCU_PDC_MASTER_M33, + MCU_PDC_EN_XTAL); + da1469x_pdc_set(g_cmac_host_pdc_cmac2sys); + da1469x_pdc_ack(g_cmac_host_pdc_cmac2sys); + } + + /* Enable Radio LDO */ + CRG_TOP->POWER_CTRL_REG |= CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk; + + /* Enable CMAC, but keep it in reset */ + CRG_TOP->CLK_RADIO_REG = (1 << CRG_TOP_CLK_RADIO_REG_RFCU_ENABLE_Pos) | + (1 << CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Pos) | + (0 << CRG_TOP_CLK_RADIO_REG_CMAC_CLK_SEL_Pos) | + (1 << CRG_TOP_CLK_RADIO_REG_CMAC_CLK_ENABLE_Pos) | + (0 << CRG_TOP_CLK_RADIO_REG_CMAC_DIV_Pos); + + /* Calculate size of ROM and RAM area (for now they should be the same) */ + cmac_rom_size = &_binary_cmac_rom_bin_end - &_binary_cmac_rom_bin_start[0]; + cmac_ram_size = &_binary_cmac_ram_bin_end - &_binary_cmac_ram_bin_start[0]; + assert(cmac_rom_size == cmac_ram_size); + + /* Load image header and check if image can be loaded */ + memcpy(&ii, &_binary_cmac_rom_bin_start, sizeof(ii)); + assert(ii.magic == 0x43414d43); /* "CMAC" */ + + /* Copy CMAC image to RAM */ + memset(&_binary_cmac_ram_bin_start, 0, cmac_ram_size); + memcpy(&_binary_cmac_ram_bin_start, &_binary_cmac_rom_bin_start[sizeof(ii)], + ii.size_rom); + + /* + * Setup memory controller for CMAC + * Code and data are set to the same address initially since CMAC will + * update data address on init. Also shared address is updated on init. + */ + MEMCTRL->CMI_CODE_BASE_REG = (uint32_t)&_binary_cmac_ram_bin_start; + MEMCTRL->CMI_DATA_BASE_REG = MEMCTRL->CMI_CODE_BASE_REG; + MEMCTRL->CMI_SHARED_BASE_REG = 0; + MEMCTRL->CMI_END_REG = (uint32_t)&_binary_cmac_ram_bin_end; + + /* Symbols below are in shared memory, can update them now */ + cmac_config = CMAC_SYM_CONFIG; + cmac_config_dyn = CMAC_SYM_CONFIG_DYN; + cmac_mbox_rx = CMAC_SYM_MBOX_RX; + cmac_mbox_tx = CMAC_SYM_MBOX_TX; + + /* Update CMAC configuration */ + cmac_config->lp_clock_freq = 0; + cmac_config->lp_clock_sca = 50; + cmac_config->rx_buf_len = 251 + 11; + cmac_config->tx_buf_len = 251 + 11; + cmac_config->initial_length_req = 0; + cmac_config->system_tcs_length = 0; + cmac_config->synth_tcs_length = 0; + cmac_config->rfcu_tcs_length = 0; + cmac_config->default_tx_power = 4; + cmac_config_dyn->enable_sleep = false; + + /* Release CMAC from reset */ + CRG_TOP->CLK_RADIO_REG &= ~CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Msk; + + /* Wait for CMAC to update registers */ + while (MEMCTRL->CMI_DATA_BASE_REG == MEMCTRL->CMI_CODE_BASE_REG); + while (MEMCTRL->CMI_SHARED_BASE_REG != (MEMCTRL->CMI_END_REG & 0xfffffc00)); + + /* Initialize mailboxes and sync with CMAC */ + cmac_mbox_tx->flags = CMAC_MBOX_F_RESET; + cmac_mbox_tx->wr_off = 0; + cmac_mbox_tx->rd_off = 0; + cmac_mbox_tx->magic = 0xa55a; + while (cmac_mbox_rx->magic != 0xa55a); + + NVIC_SetVector(CMAC2SYS_IRQn, (uint32_t)cmac2sys_isr); + NVIC_SetPriority(CMAC2SYS_IRQn, 0); + NVIC_EnableIRQ(CMAC2SYS_IRQn); + + cmac_host_signal2cmac(); + + da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/syscfg.yml new file mode 100644 index 00000000..be2e62d9 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/syscfg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + CMAC_IMAGE_FILE_NAME: + description: > + Path to library with CMAC firmware. See README for details. + value: "libble_stack_da1469x.a" diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/da1469x/pkg.yml new file mode 100644 index 00000000..5d9b533c --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport/da1469x +pkg.description: HCI transport for DA1469x +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - "@apache-mynewt-nimble/nimble" + - "@apache-mynewt-nimble/nimble/transport/da1469x/cmac_driver" + - "@apache-mynewt-core/kernel/os" + +pkg.apis: + - ble_transport + +pkg.init: + da1469x_ble_hci_init: 100 + da1469x_ble_hci_cmac_init: 201 diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/src/da1469x_ble_hci.c b/src/libs/mynewt-nimble/nimble/transport/da1469x/src/da1469x_ble_hci.c new file mode 100644 index 00000000..971d0cd0 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/src/da1469x_ble_hci.c @@ -0,0 +1,368 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <string.h> +#include "os/mynewt.h" +#include "nimble/ble.h" +#include "nimble/ble_hci_trans.h" +#include "nimble/hci_common.h" +#include "cmac_driver/cmac_host.h" + +#define HCI_PKT_NONE 0x00 +#define HCI_PKT_CMD 0x01 +#define HCI_PKT_ACL 0x02 +#define HCI_PKT_EVT 0x04 + +#define POOL_ACL_BLOCK_SIZE \ + OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) + \ + BLE_MBUF_MEMBLOCK_OVERHEAD + \ + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) + +struct da1469x_ble_hci_host_api { + ble_hci_trans_rx_cmd_fn *evt_cb; + void *evt_arg; + ble_hci_trans_rx_acl_fn *acl_cb; + void *acl_arg; +}; + +struct da1469x_ble_hci_rx_data { + uint8_t type; + uint8_t hdr[4]; + uint8_t min_len; + uint16_t len; + uint16_t expected_len; + union { + uint8_t *buf; + struct os_mbuf *om; + }; +}; + +struct da1469x_ble_hci_pool_cmd { + uint8_t cmd[BLE_HCI_TRANS_CMD_SZ]; + bool allocated; +}; + +/* (Pseudo)pool for HCI commands */ +static struct da1469x_ble_hci_pool_cmd da1469x_ble_hci_pool_cmd; + +/* Pools for HCI events (high and low priority) */ +static uint8_t da1469x_ble_hci_pool_evt_hi_buf[ OS_MEMPOOL_BYTES( + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) ]; +static struct os_mempool da1469x_ble_hci_pool_evt_hi; +static uint8_t da1469x_ble_hci_pool_evt_lo_buf[ OS_MEMPOOL_BYTES( + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) ]; +static struct os_mempool da1469x_ble_hci_pool_evt_lo; + +/* Pool for ACL data */ +static uint8_t da1469x_ble_hci_pool_acl_buf[ OS_MEMPOOL_BYTES( + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + POOL_ACL_BLOCK_SIZE) ]; +static struct os_mempool da1469x_ble_hci_pool_acl; +static struct os_mbuf_pool da1469x_ble_hci_pool_acl_mbuf; + +/* Interface to host */ +static struct da1469x_ble_hci_host_api da1469x_ble_hci_host_api; + +/* State of RX currently in progress (needs to reassemble frame) */ +static struct da1469x_ble_hci_rx_data da1469x_ble_hci_rx_data; + +int +ble_hci_trans_reset(void) +{ + /* XXX Should we do something with RF and/or BLE core? */ + return 0; +} + +void +ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *evt_cb, void *evt_arg, + ble_hci_trans_rx_acl_fn *acl_cb, void *acl_arg) +{ + da1469x_ble_hci_host_api.evt_cb = evt_cb; + da1469x_ble_hci_host_api.evt_arg = evt_arg; + da1469x_ble_hci_host_api.acl_cb = acl_cb; + da1469x_ble_hci_host_api.acl_arg = acl_arg; +} + +uint8_t * +ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + assert(!da1469x_ble_hci_pool_cmd.allocated); + da1469x_ble_hci_pool_cmd.allocated = 1; + buf = da1469x_ble_hci_pool_cmd.cmd; + break; + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&da1469x_ble_hci_pool_evt_hi); + if (buf) { + break; + } + /* no break */ + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&da1469x_ble_hci_pool_evt_lo); + break; + default: + assert(0); + buf = NULL; + } + + return buf; +} + +void +ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + + if (buf == da1469x_ble_hci_pool_cmd.cmd) { + assert(da1469x_ble_hci_pool_cmd.allocated); + da1469x_ble_hci_pool_cmd.allocated = 0; + } else if (os_memblock_from(&da1469x_ble_hci_pool_evt_hi, buf)) { + rc = os_memblock_put(&da1469x_ble_hci_pool_evt_hi, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&da1469x_ble_hci_pool_evt_lo, buf)); + rc = os_memblock_put(&da1469x_ble_hci_pool_evt_lo, buf); + assert(rc == 0); + } +} + +int +ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + uint8_t ind = HCI_PKT_CMD; + int len = 3 + cmd[2]; + + cmac_mbox_write(&ind, 1); + cmac_mbox_write(cmd, len); + + ble_hci_trans_buf_free(cmd); + + return 0; +} + +int +ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + uint8_t ind = HCI_PKT_ACL; + struct os_mbuf *x; + + cmac_mbox_write(&ind, 1); + + x = om; + while (x) { + cmac_mbox_write(x->om_data, x->om_len); + x = SLIST_NEXT(x, om_next); + } + + os_mbuf_free_chain(om); + + return 0; +} + +static int +da1469x_ble_hci_trans_ll_rx(const uint8_t *buf, uint16_t len) +{ + struct da1469x_ble_hci_rx_data *rxd = &da1469x_ble_hci_rx_data; + void *data; + int pool = BLE_HCI_TRANS_BUF_EVT_HI; + int rc; + + assert(len); + + if (rxd->type == HCI_PKT_NONE) { + rxd->type = buf[0]; + rxd->len = 0; + rxd->expected_len = 0; + + switch (rxd->type) { + case HCI_PKT_ACL: + rxd->min_len = 4; + break; + case HCI_PKT_EVT: + rxd->min_len = 2; + break; + default: + assert(0); + break; + } + + return 1; + } + + /* Ensure we have minimum length of bytes required to process header */ + if (rxd->len < rxd->min_len) { + len = min(len, rxd->min_len - rxd->len); + memcpy(&rxd->hdr[rxd->len], buf, len); + rxd->len += len; + return len; + } + + /* Parse header and allocate proper buffer if not done yet */ + if (rxd->expected_len == 0) { + switch (rxd->type) { + case HCI_PKT_ACL: + data = os_mbuf_get_pkthdr(&da1469x_ble_hci_pool_acl_mbuf, + sizeof(struct ble_mbuf_hdr)); + if (!data) { + return 0; + } + + rxd->om = data; + os_mbuf_append(rxd->om, rxd->hdr, rxd->len); + rxd->expected_len = get_le16(&rxd->hdr[2]) + 4; + break; + case HCI_PKT_EVT: + if (rxd->hdr[0] == BLE_HCI_EVCODE_LE_META) { + /* For LE Meta event we need 3 bytes to parse header */ + if (rxd->min_len < 3) { + rxd->min_len = 3; + return 0; + } + + /* Advertising reports shall be allocated from low-prio pool */ + if ((rxd->hdr[2] == BLE_HCI_LE_SUBEV_ADV_RPT) || + (rxd->hdr[2] == BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { + pool = BLE_HCI_TRANS_BUF_EVT_LO; + } + } + + data = ble_hci_trans_buf_alloc(pool); + if (!data) { + /* + * Only care about valid buffer when shall be allocated from + * high-prio pool, otherwise NULL is fine and we'll just skip + * this event. + */ + if (pool != BLE_HCI_TRANS_BUF_EVT_LO) { + data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (!data) { + return 0; + } + } + } + + rxd->buf = data; + memcpy(rxd->buf, rxd->hdr, rxd->len); + rxd->expected_len = rxd->hdr[1] + 2; + break; + default: + assert(0); + return len; + } + } + + len = min(len, rxd->expected_len - rxd->len); + + switch (rxd->type) { + case HCI_PKT_ACL: + os_mbuf_append(rxd->om, buf, len); + rxd->len += len; + + if (rxd->len == rxd->expected_len) { + rc = da1469x_ble_hci_host_api.acl_cb(rxd->om, + da1469x_ble_hci_host_api.acl_arg); + if (rc != 0) { + os_mbuf_free_chain(rxd->om); + } + rxd->type = HCI_PKT_NONE; + } + break; + case HCI_PKT_EVT: + if (rxd->buf) { + memcpy(&rxd->buf[rxd->len], buf, len); + } + rxd->len += len; + + if (rxd->len == rxd->expected_len) { + /* + * XXX for unknown reason at startup controller sends command + * complete for a vendor specific command which we never sent + * and this messes up with our ack code - just discard this + * event + */ + if ((rxd->buf[0] == 0x0E) && (get_le16(&rxd->buf[3]) == 0xfc11)) { + ble_hci_trans_buf_free(rxd->buf); + } else if (rxd->buf) { + rc = da1469x_ble_hci_host_api.evt_cb(rxd->buf, + da1469x_ble_hci_host_api.evt_arg); + if (rc != 0) { + ble_hci_trans_buf_free(rxd->buf); + } + } + rxd->type = HCI_PKT_NONE; + } + break; + default: + assert(0); + break; + } + + return len; +} + +static int +da1469x_ble_hci_read_cb(const uint8_t *buf, uint16_t len) +{ + return da1469x_ble_hci_trans_ll_rx(buf, len); +} + +void +da1469x_ble_hci_init(void) +{ + int rc; + + SYSINIT_ASSERT_ACTIVE(); + + rc = os_mempool_init(&da1469x_ble_hci_pool_acl, MYNEWT_VAL(BLE_ACL_BUF_COUNT), + POOL_ACL_BLOCK_SIZE, da1469x_ble_hci_pool_acl_buf, + "da1469x_ble_hci_pool_acl"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&da1469x_ble_hci_pool_acl_mbuf, + &da1469x_ble_hci_pool_acl, POOL_ACL_BLOCK_SIZE, + MYNEWT_VAL(BLE_ACL_BUF_COUNT)); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&da1469x_ble_hci_pool_evt_hi, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + da1469x_ble_hci_pool_evt_hi_buf, + "da1469x_ble_hci_pool_evt_hi"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&da1469x_ble_hci_pool_evt_lo, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + da1469x_ble_hci_pool_evt_lo_buf, + "da1469x_ble_hci_pool_evt_lo"); + SYSINIT_PANIC_ASSERT(rc == 0); +} + +void +da1469x_ble_hci_cmac_init(void) +{ + cmac_mbox_set_read_cb(da1469x_ble_hci_read_cb); + cmac_host_init(); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/da1469x/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/da1469x/syscfg.yml new file mode 100644 index 00000000..4ea9da9b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/da1469x/syscfg.yml @@ -0,0 +1,43 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_HCI_EVT_HI_BUF_COUNT: + description: 'Number of high-priority event buffers.' + value: 2 + + BLE_HCI_EVT_LO_BUF_COUNT: + description: 'Number of low-priority event buffers.' + value: 8 + + BLE_HCI_EVT_BUF_SIZE: + description: 'Size of each event buffer, in bytes.' + value: 70 + + BLE_ACL_BUF_COUNT: + description: 'The number of ACL data buffers' + value: 4 + + BLE_ACL_BUF_SIZE: + description: > + This is the maximum size of the data portion of HCI ACL data + packets. It does not include the HCI data header (of 4 bytes). + value: 255 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 274 diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h b/src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h new file mode 100644 index 00000000..738d9e6d --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef H_BLE_HCI_EMSPI_ +#define H_BLE_HCI_EMSPI_ + +#ifdef __cplusplus +extern "C" { +#endif + +void ble_hci_emspi_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml new file mode 100644 index 00000000..5df5a5bb --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport/emspi +pkg.description: "HCI transport using EM's HCI SPI protocol." +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - nimble + +pkg.apis: + - ble_transport + +pkg.init: + ble_hci_emspi_init: 'MYNEWT_VAL(BLE_HCI_EMSPI_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c b/src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c new file mode 100644 index 00000000..61c0c9cb --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c @@ -0,0 +1,965 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include "sysinit/sysinit.h" +#include "syscfg/syscfg.h" +#include "os/os_cputime.h" +#include "bsp/bsp.h" +#include "os/os.h" +#include "mem/mem.h" +#include "hal/hal_gpio.h" +#include "hal/hal_spi.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" + +#include "transport/emspi/ble_hci_emspi.h" + +#include "am_mcu_apollo.h" + +/*** + * NOTES: + * The emspi HCI transport doesn't use event buffer priorities. All incoming + * and outgoing events use buffers from the same pool. + * + */ + +#define BLE_HCI_EMSPI_PKT_EVT_COUNT \ + (MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT) + \ + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT)) + +#define BLE_HCI_EMSPI_PKT_NONE 0x00 +#define BLE_HCI_EMSPI_PKT_CMD 0x01 +#define BLE_HCI_EMSPI_PKT_ACL 0x02 +#define BLE_HCI_EMSPI_PKT_EVT 0x04 + +#define BLE_HCI_EMSPI_CTLR_STATUS_OK 0xc0 +#define BLE_HCI_EMSPI_OP_TX 0x42 +#define BLE_HCI_EMSPI_OP_RX 0x81 + +static os_event_fn ble_hci_emspi_event_txrx; + +static struct os_event ble_hci_emspi_ev_txrx = { + .ev_cb = ble_hci_emspi_event_txrx, +}; + +static struct os_eventq ble_hci_emspi_evq; +static struct os_task ble_hci_emspi_task; +static os_stack_t ble_hci_emspi_stack[MYNEWT_VAL(BLE_HCI_EMSPI_STACK_SIZE)]; + +static ble_hci_trans_rx_cmd_fn *ble_hci_emspi_rx_cmd_cb; +static void *ble_hci_emspi_rx_cmd_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_emspi_rx_acl_cb; +static void *ble_hci_emspi_rx_acl_arg; + +static struct os_mempool ble_hci_emspi_evt_hi_pool; +static os_membuf_t ble_hci_emspi_evt_hi_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_emspi_evt_lo_pool; +static os_membuf_t ble_hci_emspi_evt_lo_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_emspi_cmd_pool; +static os_membuf_t ble_hci_emspi_cmd_buf[ + OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ) +]; + +static struct os_mbuf_pool ble_hci_emspi_acl_mbuf_pool; +static struct os_mempool_ext ble_hci_emspi_acl_pool; + +/* + * The MBUF payload size must accommodate the HCI data header size plus the + * maximum ACL data packet length. The ACL block size is the size of the + * mbufs we will allocate. + */ +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) + +static os_membuf_t ble_hci_emspi_acl_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE) +]; + +/** + * A packet to be sent over the EMSPI. This can be a command, an event, or ACL + * data. + */ +struct ble_hci_emspi_pkt { + STAILQ_ENTRY(ble_hci_emspi_pkt) next; + void *data; + uint8_t type; +}; +STAILQ_HEAD(, ble_hci_emspi_pkt) ble_hci_emspi_tx_q; + +static struct os_mempool ble_hci_emspi_pkt_pool; +static os_membuf_t ble_hci_emspi_pkt_buf[ + OS_MEMPOOL_SIZE(BLE_HCI_EMSPI_PKT_EVT_COUNT + 1 + + MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT), + sizeof (struct ble_hci_emspi_pkt)) +]; + +static void +ble_hci_emspi_rdy_isr(void *arg) +{ + os_eventq_put(&ble_hci_emspi_evq, &ble_hci_emspi_ev_txrx); +} + +static void +ble_hci_emspi_initiate_write(void) +{ + hal_gpio_irq_disable(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN)); + + /* Assert slave select. */ + hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_SS_PIN), 0); + + /* Wait for controller to indicate ready-to-receive. */ + while (!hal_gpio_read(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN))) { } +} + +static void +ble_hci_emspi_terminate_write(void) +{ + const uint64_t rdy_mask = + AM_HAL_GPIO_BIT(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN)); + os_sr_t sr; + + am_hal_gpio_int_clear(rdy_mask); + + /* Deassert slave select. */ + hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_SS_PIN), 1); + + OS_ENTER_CRITICAL(sr); + hal_gpio_irq_enable(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN)); + + if (hal_gpio_read(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN))) { + am_hal_gpio_int_set(rdy_mask); + } + + OS_EXIT_CRITICAL(sr); +} + +static int +ble_hci_emspi_write_hdr(uint8_t first_byte, uint8_t *out_buf_size) +{ + const uint8_t hdr[2] = { first_byte, 0x00 }; + uint8_t rx[2]; + int rc; + + /* Send command header. */ + rc = hal_spi_txrx(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), (void *)hdr, rx, 2); + if (rc != 0) { + return rc; + } + + /* Check for "OK" status. */ + if (rx[0] != BLE_HCI_EMSPI_CTLR_STATUS_OK) { + return BLE_ERR_HW_FAIL; + } + + *out_buf_size = rx[1]; + return 0; +} + +/** + * Transmits a chunk of bytes to the controller. + */ +static int +ble_hci_emspi_tx_chunk(const uint8_t *data, int len, int *out_bytes_txed) +{ + uint8_t buf_size; + int rc; + + /* Silence spurious "may be used uninitialized" warning. */ + *out_bytes_txed = 0; + + ble_hci_emspi_initiate_write(); + + rc = ble_hci_emspi_write_hdr(BLE_HCI_EMSPI_OP_TX, &buf_size); + if (rc != 0) { + goto done; + } + + if (buf_size == 0) { + rc = 0; + goto done; + } + + if (buf_size < len) { + len = buf_size; + } + rc = hal_spi_txrx(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), (void *)data, NULL, + len); + if (rc != 0) { + goto done; + } + *out_bytes_txed = len; + +done: + ble_hci_emspi_terminate_write(); + return rc; +} + +/** + * Transmits a full command or ACL data packet to the controller. + */ +static int +ble_hci_emspi_tx(const uint8_t *data, int len) +{ + int bytes_txed; + int rc; + + while (len > 0) { + rc = ble_hci_emspi_tx_chunk(data, len, &bytes_txed); + if (rc != 0) { + goto done; + } + + data += bytes_txed; + len -= bytes_txed; + } + + rc = 0; + +done: + return rc; +} + +/** + * Reads the specified number of bytes from the controller. + */ +static int +ble_hci_emspi_rx(uint8_t *data, int max_len) +{ + uint8_t buf_size; + int rc; + + ble_hci_emspi_initiate_write(); + + rc = ble_hci_emspi_write_hdr(BLE_HCI_EMSPI_OP_RX, &buf_size); + if (rc != 0) { + goto done; + } + + if (buf_size > max_len) { + buf_size = max_len; + } + + rc = hal_spi_txrx(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), NULL, data, buf_size); + if (rc != 0) { + rc = BLE_ERR_HW_FAIL; + goto done; + } + +done: + ble_hci_emspi_terminate_write(); + return rc; +} + +/** + * Allocates a buffer (mbuf) for ACL operation. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +static struct os_mbuf * +ble_hci_trans_acl_buf_alloc(void) +{ + uint8_t usrhdr_len; + +#if MYNEWT_VAL(BLE_HS_FLOW_CTRL) + usrhdr_len = BLE_MBUF_HS_HDR_LEN; +#else + usrhdr_len = 0; +#endif + + return os_mbuf_get_pkthdr(&ble_hci_emspi_acl_mbuf_pool, usrhdr_len); +} + +/** + * Transmits an ACL data packet to the controller. The caller relinquishes the + * specified mbuf, regardless of return status. + */ +static int +ble_hci_emspi_acl_tx(struct os_mbuf *om) +{ + struct ble_hci_emspi_pkt *pkt; + os_sr_t sr; + + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + + pkt = os_memblock_get(&ble_hci_emspi_pkt_pool); + if (pkt == NULL) { + os_mbuf_free_chain(om); + return BLE_ERR_MEM_CAPACITY; + } + + pkt->type = BLE_HCI_EMSPI_PKT_ACL; + pkt->data = om; + + OS_ENTER_CRITICAL(sr); + STAILQ_INSERT_TAIL(&ble_hci_emspi_tx_q, pkt, next); + OS_EXIT_CRITICAL(sr); + + os_eventq_put(&ble_hci_emspi_evq, &ble_hci_emspi_ev_txrx); + + return 0; +} + +/** + * Transmits a command packet to the controller. The caller relinquishes the + * specified buffer, regardless of return status. + */ +static int +ble_hci_emspi_cmdevt_tx(uint8_t *cmd_buf, uint8_t pkt_type) +{ + struct ble_hci_emspi_pkt *pkt; + os_sr_t sr; + + pkt = os_memblock_get(&ble_hci_emspi_pkt_pool); + if (pkt == NULL) { + ble_hci_trans_buf_free(cmd_buf); + return BLE_ERR_MEM_CAPACITY; + } + + pkt->type = pkt_type; + pkt->data = cmd_buf; + + OS_ENTER_CRITICAL(sr); + STAILQ_INSERT_TAIL(&ble_hci_emspi_tx_q, pkt, next); + OS_EXIT_CRITICAL(sr); + + os_eventq_put(&ble_hci_emspi_evq, &ble_hci_emspi_ev_txrx); + + return 0; +} + +static int +ble_hci_emspi_tx_flat(const uint8_t *data, int len) +{ + int rc; + + rc = ble_hci_emspi_tx(data, len); + return rc; +} + +static int +ble_hci_emspi_tx_pkt_type(uint8_t pkt_type) +{ + return ble_hci_emspi_tx_flat(&pkt_type, 1); +} + +static int +ble_hci_emspi_tx_cmd(const uint8_t *data) +{ + int len; + int rc; + + rc = ble_hci_emspi_tx_pkt_type(BLE_HCI_EMSPI_PKT_CMD); + if (rc != 0) { + return rc; + } + + len = data[2] + sizeof(struct ble_hci_cmd); + rc = ble_hci_emspi_tx_flat(data, len); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_hci_emspi_tx_acl(struct os_mbuf *om) +{ + struct os_mbuf *cur; + int rc; + + rc = ble_hci_emspi_tx_pkt_type(BLE_HCI_EMSPI_PKT_ACL); + if (rc != 0) { + return rc; + } + + cur = om; + while (cur != NULL) { + rc = ble_hci_emspi_tx(cur->om_data, cur->om_len); + if (rc != 0) { + break; + } + + cur = SLIST_NEXT(cur, om_next); + } + + return rc; +} + +static struct ble_hci_emspi_pkt * +ble_hci_emspi_pull_next_tx(void) +{ + struct ble_hci_emspi_pkt *pkt; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + pkt = STAILQ_FIRST(&ble_hci_emspi_tx_q); + if (pkt != NULL) { + STAILQ_REMOVE(&ble_hci_emspi_tx_q, pkt, ble_hci_emspi_pkt, next); + } + OS_EXIT_CRITICAL(sr); + + return pkt; +} + +static int +ble_hci_emspi_tx_pkt(void) +{ + struct ble_hci_emspi_pkt *pkt; + int rc; + + pkt = ble_hci_emspi_pull_next_tx(); + if (pkt == NULL) { + return -1; + } + + switch (pkt->type) { + case BLE_HCI_EMSPI_PKT_CMD: + rc = ble_hci_emspi_tx_cmd(pkt->data); + ble_hci_trans_buf_free(pkt->data); + break; + + case BLE_HCI_EMSPI_PKT_ACL: + rc = ble_hci_emspi_tx_acl(pkt->data); + os_mbuf_free_chain(pkt->data); + break; + + default: + rc = -1; + break; + } + + os_memblock_put(&ble_hci_emspi_pkt_pool, pkt); + + return rc; +} + +static int +ble_hci_emspi_rx_evt(void) +{ + uint8_t *data; + uint8_t len; + int rc; + + /* XXX: we should not assert if host cannot allocate an event. Need + * to determine what to do here. + */ + data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + assert(data != NULL); + + rc = ble_hci_emspi_rx(data, sizeof(struct ble_hci_ev)); + if (rc != 0) { + goto err; + } + + len = data[1]; + if (len > 0) { + rc = ble_hci_emspi_rx(data + sizeof(struct ble_hci_ev), len); + if (rc != 0) { + goto err; + } + } + + assert(ble_hci_emspi_rx_cmd_cb != NULL); + ble_hci_emspi_rx_cmd_cb(data, ble_hci_emspi_rx_cmd_arg); + if (rc != 0) { + goto err; + } + + return 0; + +err: + ble_hci_trans_buf_free(data); + return rc; +} + +static int +ble_hci_emspi_rx_acl(void) +{ + struct os_mbuf *om; + uint16_t len; + int rc; + + /* XXX: we should not assert if host cannot allocate an mbuf. Need to + * determine what to do here. + */ + om = ble_hci_trans_acl_buf_alloc(); + assert(om != NULL); + + rc = ble_hci_emspi_rx(om->om_data, BLE_HCI_DATA_HDR_SZ); + if (rc != 0) { + goto err; + } + + len = get_le16(om->om_data + 2); + if (len > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) { + /* + * Data portion cannot exceed data length of acl buffer. If it does + * this is considered to be a loss of sync. + */ + rc = BLE_ERR_UNSPECIFIED; + goto err; + } + + if (len > 0) { + rc = ble_hci_emspi_rx(om->om_data + BLE_HCI_DATA_HDR_SZ, len); + if (rc != 0) { + goto err; + } + } + + OS_MBUF_PKTLEN(om) = BLE_HCI_DATA_HDR_SZ + len; + om->om_len = BLE_HCI_DATA_HDR_SZ + len; + + assert(ble_hci_emspi_rx_cmd_cb != NULL); + rc = ble_hci_emspi_rx_acl_cb(om, ble_hci_emspi_rx_acl_arg); + if (rc != 0) { + goto err; + } + + return 0; + +err: + os_mbuf_free_chain(om); + return rc; +} + +/** + * @return The type of packet to follow success; + * -1 if there is no valid packet to receive. + */ +static int +ble_hci_emspi_rx_pkt(void) +{ + uint8_t pkt_type; + int rc; + + /* XXX: This is awkward; should read the full packet in "one go". */ + rc = ble_hci_emspi_rx(&pkt_type, 1); + if (rc != 0) { + return rc; + } + + switch (pkt_type) { + case BLE_HCI_EMSPI_PKT_EVT: + return ble_hci_emspi_rx_evt(); + + case BLE_HCI_EMSPI_PKT_ACL: + return ble_hci_emspi_rx_acl(); + + default: + /* XXX */ + return -1; + } +} + +static void +ble_hci_emspi_set_rx_cbs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_emspi_rx_cmd_cb = cmd_cb; + ble_hci_emspi_rx_cmd_arg = cmd_arg; + ble_hci_emspi_rx_acl_cb = acl_cb; + ble_hci_emspi_rx_acl_arg = acl_arg; +} + +static void +ble_hci_emspi_free_pkt(uint8_t type, uint8_t *cmdevt, struct os_mbuf *acl) +{ + switch (type) { + case BLE_HCI_EMSPI_PKT_NONE: + break; + + case BLE_HCI_EMSPI_PKT_CMD: + case BLE_HCI_EMSPI_PKT_EVT: + ble_hci_trans_buf_free(cmdevt); + break; + + case BLE_HCI_EMSPI_PKT_ACL: + os_mbuf_free_chain(acl); + break; + + default: + assert(0); + break; + } +} + +/** + * Unsupported. This is a host-only transport. + */ +int +ble_hci_trans_ll_evt_tx(uint8_t *cmd) +{ + return BLE_ERR_UNSUPPORTED; +} + +/** + * Unsupported. This is a host-only transport. + */ +int +ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + return BLE_ERR_UNSUPPORTED; +} + +/** + * Sends an HCI command from the host to the controller. + * + * @param cmd The HCI command to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + int rc; + + rc = ble_hci_emspi_cmdevt_tx(cmd, BLE_HCI_EMSPI_PKT_CMD); + return rc; +} + +/** + * Sends ACL data from host to controller. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + int rc; + + rc = ble_hci_emspi_acl_tx(om); + return rc; +} + +/** + * Configures the HCI transport to call the specified callback upon receiving + * HCI packets from the controller. This function should only be called by by + * host. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * event. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void +ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_emspi_set_rx_cbs(cmd_cb, cmd_arg, acl_cb, acl_arg); +} + +/** + * Configures the HCI transport to operate with a host. The transport will + * execute specified callbacks upon receiving HCI packets from the controller. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * event. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void +ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + /* Unsupported. */ + assert(0); +} + +/** + * Allocates a flat buffer of the specified type. + * + * @param type The type of buffer to allocate; one of the + * BLE_HCI_TRANS_BUF_[...] constants. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +uint8_t * +ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + buf = os_memblock_get(&ble_hci_emspi_cmd_pool); + break; + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_emspi_evt_hi_pool); + if (buf == NULL) { + /* If no high-priority event buffers remain, try to grab a + * low-priority one. + */ + buf = os_memblock_get(&ble_hci_emspi_evt_lo_pool); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_emspi_evt_lo_pool); + break; + + default: + assert(0); + buf = NULL; + } + + return buf; +} + +/** + * Frees the specified flat buffer. The buffer must have been allocated via + * ble_hci_trans_buf_alloc(). + * + * @param buf The buffer to free. + */ +void +ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + + if (buf != NULL) { + if (os_memblock_from(&ble_hci_emspi_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_emspi_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_emspi_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_emspi_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_emspi_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_emspi_cmd_pool, buf); + assert(rc == 0); + } + } +} + +/** + * Configures a callback to get executed whenever an ACL data packet is freed. + * The function is called in lieu of actually freeing the packet. + * + * @param cb The callback to configure. + * + * @return 0 on success. + */ +int +ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg) +{ + ble_hci_emspi_acl_pool.mpe_put_cb = cb; + ble_hci_emspi_acl_pool.mpe_put_arg = arg; + return 0; +} + +/** + * Resets the HCI UART transport to a clean state. Frees all buffers and + * reconfigures the UART. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_reset(void) +{ + struct ble_hci_emspi_pkt *pkt; + + hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_RESET_PIN), 1); + + while ((pkt = STAILQ_FIRST(&ble_hci_emspi_tx_q)) != NULL) { + STAILQ_REMOVE(&ble_hci_emspi_tx_q, pkt, ble_hci_emspi_pkt, next); + ble_hci_emspi_free_pkt(pkt->type, pkt->data, pkt->data); + os_memblock_put(&ble_hci_emspi_pkt_pool, pkt); + } + + return 0; +} + +static void +ble_hci_emspi_event_txrx(struct os_event *ev) +{ + int rc; + + rc = ble_hci_emspi_rx_pkt(); + + do { + rc = ble_hci_emspi_tx_pkt(); + } while (rc == 0); +} + +static void +ble_hci_emspi_loop(void *unused) +{ + while (1) { + os_eventq_run(&ble_hci_emspi_evq); + } +} + +static void +ble_hci_emspi_init_hw(void) +{ + struct hal_spi_settings spi_cfg; + int rc; + + rc = hal_gpio_init_out(MYNEWT_VAL(BLE_HCI_EMSPI_RESET_PIN), 0); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = hal_gpio_init_out(MYNEWT_VAL(BLE_HCI_EMSPI_SS_PIN), 1); + SYSINIT_PANIC_ASSERT(rc == 0); + + spi_cfg.data_order = HAL_SPI_MSB_FIRST; + spi_cfg.data_mode = HAL_SPI_MODE0; + spi_cfg.baudrate = MYNEWT_VAL(BLE_HCI_EMSPI_BAUD); + spi_cfg.word_size = HAL_SPI_WORD_SIZE_8BIT; + + rc = hal_spi_config(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), &spi_cfg); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = hal_gpio_irq_init(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN), + ble_hci_emspi_rdy_isr, NULL, + HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = hal_spi_enable(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM)); + assert(rc == 0); + + hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_RESET_PIN), 1); +} + +/** + * Initializes the UART HCI transport module. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +void +ble_hci_emspi_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + rc = os_mempool_ext_init(&ble_hci_emspi_acl_pool, + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE, + ble_hci_emspi_acl_buf, + "ble_hci_emspi_acl_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&ble_hci_emspi_acl_mbuf_pool, + &ble_hci_emspi_acl_pool.mpe_mp, + ACL_BLOCK_SIZE, + MYNEWT_VAL(BLE_ACL_BUF_COUNT)); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of HCI command buffers. NOTE: we currently dont + * allow this to be configured. The controller will only allow one + * outstanding command. We decided to keep this a pool in case we allow + * allow the controller to handle more than one outstanding command. + */ + rc = os_mempool_init(&ble_hci_emspi_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + ble_hci_emspi_cmd_buf, + "ble_hci_emspi_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_emspi_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_emspi_evt_hi_buf, + "ble_hci_emspi_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_emspi_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_emspi_evt_lo_buf, + "ble_hci_emspi_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of packet list nodes. NOTE: the number of these + * buffers should be, at least, the total number of event buffers (hi + * and lo), the number of command buffers (currently 1) and the total + * number of buffers that the controller could possibly hand to the host. + */ + rc = os_mempool_init(&ble_hci_emspi_pkt_pool, + BLE_HCI_EMSPI_PKT_EVT_COUNT + 1 + + MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT), + sizeof (struct ble_hci_emspi_pkt), + ble_hci_emspi_pkt_buf, + "ble_hci_emspi_pkt_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + STAILQ_INIT(&ble_hci_emspi_tx_q); + + ble_hci_emspi_init_hw(); + + /* Initialize the LL task */ + os_eventq_init(&ble_hci_emspi_evq); + rc = os_task_init(&ble_hci_emspi_task, "ble_hci_emspi", ble_hci_emspi_loop, + NULL, MYNEWT_VAL(BLE_HCI_EMSPI_PRIO), OS_WAIT_FOREVER, + ble_hci_emspi_stack, + MYNEWT_VAL(BLE_HCI_EMSPI_STACK_SIZE)); + SYSINIT_PANIC_ASSERT(rc == 0); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml new file mode 100644 index 00000000..4751271b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml @@ -0,0 +1,99 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_HCI_EMSPI: + description: 'Indicates that the emspi host HCI transport is present.' + value: 1 + restrictions: + # XXX: This package only builds with the apollo2 MCU. + # MCU-dependencies need to be removed. + - MCU_APOLLO2 + + # This is a host-only transport. + - BLE_HOST + + BLE_HCI_EVT_HI_BUF_COUNT: + description: 'Number of high-priority event buffers.' + value: 2 + + BLE_HCI_EVT_LO_BUF_COUNT: + description: 'Number of low-priority event buffers.' + value: 8 + + BLE_HCI_EVT_BUF_SIZE: + description: 'Size of each event buffer, in bytes.' + value: 70 + + BLE_ACL_BUF_COUNT: + description: 'The number of ACL data buffers' + value: 4 + + BLE_ACL_BUF_SIZE: + description: > + This is the maximum size of the data portion of HCI ACL data + packets. It does not include the HCI data header (of 4 bytes). + value: 255 + + BLE_HCI_ACL_OUT_COUNT: + description: > + This count is used in creating a pool of elements used by the + code to enqueue various elements. In the case of the controller + only HCI, this number should be equal to the number of mbufs in + the msys pool. For host only, it is really dependent on the + number of ACL buffers that the controller tells the host it + has. + value: 12 + + BLE_HCI_EMSPI_SPI_NUM: + description: The index of the SPI device to use for HCI. + value: 5 + + BLE_HCI_EMSPI_BAUD: + description: The SPI baud rate. + value: 8000000 + + BLE_HCI_EMSPI_RESET_PIN: + description: The GPIO pin that resets the EM controller. + value: 42 + + BLE_HCI_EMSPI_SS_PIN: + description: The GPIO pin to use for SPI slave select. + value: 45 + + BLE_HCI_EMSPI_RDY_PIN: + description: > + The GPIO pin that the EM controller uses to indicate data ready. + value: 26 + + BLE_HCI_EMSPI_PRIO: + description: The priority of the emspi task. + type: task_priority + value: 5 + + BLE_HCI_EMSPI_STACK_SIZE: + description: 'The size of the emspi task (units: 4-byte words).' + value: 256 + + BLE_HCI_EMSPI_SYSINIT_STAGE: + description: > + Sysinit stage for the EMSPI BLE transport. + value: 100 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 257 diff --git a/src/libs/mynewt-nimble/nimble/transport/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/pkg.yml new file mode 100644 index 00000000..2bc4ac29 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/pkg.yml @@ -0,0 +1,45 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport +pkg.description: Meta-package for NimBLE HCI transport +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps.'BLE_HCI_TRANSPORT == "builtin"': + - nimble/transport/ram + - nimble/controller + +pkg.deps.'BLE_HCI_TRANSPORT == "emspi"': + - nimble/transport/emspi + +pkg.deps.'BLE_HCI_TRANSPORT == "ram"': + - nimble/transport/ram + +pkg.deps.'BLE_HCI_TRANSPORT == "socket"': + - nimble/transport/socket + +pkg.deps.'BLE_HCI_TRANSPORT == "uart"': + - nimble/transport/uart + +pkg.deps.'BLE_HCI_TRANSPORT == "da1469x"': + - nimble/transport/da1469x diff --git a/src/libs/mynewt-nimble/nimble/transport/ram/include/transport/ram/ble_hci_ram.h b/src/libs/mynewt-nimble/nimble/transport/ram/include/transport/ram/ble_hci_ram.h new file mode 100644 index 00000000..3c37e329 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/ram/include/transport/ram/ble_hci_ram.h @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef H_BLE_HCI_RAM_ +#define H_BLE_HCI_RAM_ + +#include "nimble/ble_hci_trans.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void ble_hci_ram_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/nimble/transport/ram/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/ram/pkg.yml new file mode 100644 index 00000000..bb8397bf --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/ram/pkg.yml @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport/ram +pkg.description: XXX +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - nimble + +pkg.apis: + - ble_transport + +pkg.init: + ble_hci_ram_init: 'MYNEWT_VAL(BLE_TRANS_RAM_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/transport/ram/src/ble_hci_ram.c b/src/libs/mynewt-nimble/nimble/transport/ram/src/ble_hci_ram.c new file mode 100644 index 00000000..3f10e9df --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/ram/src/ble_hci_ram.c @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <errno.h> +#include <stddef.h> +#include "syscfg/syscfg.h" +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "mem/mem.h" +#include "nimble/ble.h" +#include "nimble/ble_hci_trans.h" +#include "transport/ram/ble_hci_ram.h" + +static ble_hci_trans_rx_cmd_fn *ble_hci_ram_rx_cmd_hs_cb; +static void *ble_hci_ram_rx_cmd_hs_arg; + +static ble_hci_trans_rx_cmd_fn *ble_hci_ram_rx_cmd_ll_cb; +static void *ble_hci_ram_rx_cmd_ll_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_ram_rx_acl_hs_cb; +static void *ble_hci_ram_rx_acl_hs_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_ram_rx_acl_ll_cb; +static void *ble_hci_ram_rx_acl_ll_arg; + +static struct os_mempool ble_hci_ram_cmd_pool; +static os_membuf_t ble_hci_ram_cmd_buf[ + OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ) +]; + +static struct os_mempool ble_hci_ram_evt_hi_pool; +static os_membuf_t ble_hci_ram_evt_hi_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_ram_evt_lo_pool; +static os_membuf_t ble_hci_ram_evt_lo_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +void +ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_ram_rx_cmd_hs_cb = cmd_cb; + ble_hci_ram_rx_cmd_hs_arg = cmd_arg; + ble_hci_ram_rx_acl_hs_cb = acl_cb; + ble_hci_ram_rx_acl_hs_arg = acl_arg; +} + +void +ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_ram_rx_cmd_ll_cb = cmd_cb; + ble_hci_ram_rx_cmd_ll_arg = cmd_arg; + ble_hci_ram_rx_acl_ll_cb = acl_cb; + ble_hci_ram_rx_acl_ll_arg = acl_arg; +} + +int +ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + int rc; + + assert(ble_hci_ram_rx_cmd_ll_cb != NULL); + + rc = ble_hci_ram_rx_cmd_ll_cb(cmd, ble_hci_ram_rx_cmd_ll_arg); + return rc; +} + +int +ble_hci_trans_ll_evt_tx(uint8_t *hci_ev) +{ + int rc; + + assert(ble_hci_ram_rx_cmd_hs_cb != NULL); + + rc = ble_hci_ram_rx_cmd_hs_cb(hci_ev, ble_hci_ram_rx_cmd_hs_arg); + return rc; +} + +int +ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + int rc; + + assert(ble_hci_ram_rx_acl_ll_cb != NULL); + + rc = ble_hci_ram_rx_acl_ll_cb(om, ble_hci_ram_rx_acl_ll_arg); + return rc; +} + +int +ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + int rc; + + assert(ble_hci_ram_rx_acl_hs_cb != NULL); + + rc = ble_hci_ram_rx_acl_hs_cb(om, ble_hci_ram_rx_acl_hs_arg); + return rc; +} + +uint8_t * +ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + buf = os_memblock_get(&ble_hci_ram_cmd_pool); + break; + + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_ram_evt_hi_pool); + if (buf == NULL) { + /* If no high-priority event buffers remain, try to grab a + * low-priority one. + */ + buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_ram_evt_lo_pool); + break; + + default: + assert(0); + buf = NULL; + } + + return buf; +} + +void +ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + + /* XXX: this may look a bit odd, but the controller uses the command + * buffer to send back the command complete/status as an immediate + * response to the command. This was done to insure that the controller + * could always send back one of these events when a command was received. + * Thus, we check to see which pool the buffer came from so we can free + * it to the appropriate pool + */ + if (os_memblock_from(&ble_hci_ram_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_ram_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_ram_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_ram_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_ram_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_ram_cmd_pool, buf); + assert(rc == 0); + } +} + +/** + * Unsupported; the RAM transport does not have a dedicated ACL data packet + * pool. + */ +int +ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg) +{ + return BLE_ERR_UNSUPPORTED; +} + +int +ble_hci_trans_reset(void) +{ + /* No work to do. All allocated buffers are owned by the host or + * controller, and they will get freed by their owners. + */ + return 0; +} + +void +ble_hci_ram_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + /* + * Create memory pool of HCI command buffers. NOTE: we currently dont + * allow this to be configured. The controller will only allow one + * outstanding command. We decided to keep this a pool in case we allow + * allow the controller to handle more than one outstanding command. + */ + rc = os_mempool_init(&ble_hci_ram_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + ble_hci_ram_cmd_buf, + "ble_hci_ram_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_ram_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_ram_evt_hi_buf, + "ble_hci_ram_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_ram_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_ram_evt_lo_buf, + "ble_hci_ram_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/ram/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/ram/syscfg.yml new file mode 100644 index 00000000..3b822fcc --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/ram/syscfg.yml @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_HCI_EVT_HI_BUF_COUNT: + description: 'Number of high-priority event buffers.' + value: 2 + + BLE_HCI_EVT_LO_BUF_COUNT: + description: 'Number of low-priority event buffers.' + value: 8 + + BLE_HCI_EVT_BUF_SIZE: + description: 'Size of each event buffer, in bytes.' + value: 70 + + BLE_ACL_BUF_COUNT: + description: 'The number of ACL data buffers' + value: 4 + + BLE_ACL_BUF_SIZE: + description: > + This is the maximum size of the data portion of HCI ACL data + packets. It does not include the HCI data header (of 4 bytes). + value: 255 + + BLE_TRANS_RAM_SYSINIT_STAGE: + description: > + Sysinit stage for the RAM BLE transport. + value: 100 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 257 diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h b/src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h new file mode 100644 index 00000000..d17074f5 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef _BLE_HCI_SOCKET_H_ +#define _BLE_HCI_SOCKET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_eventq; +void ble_hci_sock_set_evq(struct os_eventq *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml new file mode 100644 index 00000000..fb4144d3 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport/socket +pkg.description: Provides HCI transport over socket interface +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - "@apache-mynewt-core/hw/hal" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/util/mem" + - nimble + +pkg.apis: + - ble_transport + +pkg.init: + ble_hci_sock_init: 'MYNEWT_VAL(BLE_SOCK_CLI_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c b/src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c new file mode 100644 index 00000000..8bf56f39 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c @@ -0,0 +1,886 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* + * Provides HCI transport over sockets. Either over + * TCP sockets, or Linux bluetooth socket. + * + * E.g. to connect to TCP target, start in another window + * socat -x PIPE:/dev/ttyACM1 TCP4-LISTEN:14433,fork,reuseaddr + * When building this package, set BLE_SOCK_USE_TCP=1 and + * BLE_SOCK_TCP_PORT controls the target port this transport + * connects to. + * + * To use Linux Bluetooth sockets, create an interface: + * sudo hciattach -r -n /dev/ttyUSB0 any 57600 + * And build this package with BLE_SOCK_USE_LINUX_BLUE=1, + * BLE_SOCK_LINUX_DEV=<interface index of the target interface> + * + */ +#include "syscfg/syscfg.h" + +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#include <sys/socket.h> + +#if MYNEWT_VAL(BLE_SOCK_USE_TCP) +#include <sys/errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE) +#include <sys/errno.h> +#define BTPROTO_HCI 1 +#define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIDEVRESET _IOW('H', 203, int) +#define HCIGETDEVLIST _IOR('H', 210, int) + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#endif + +#include <fcntl.h> +#include <sys/ioctl.h> + +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "mem/mem.h" + +#include "stats/stats.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/nimble_npl.h" +#include "nimble/ble_hci_trans.h" +#include "socket/ble_hci_socket.h" + +/*** + * NOTES: + * The UART HCI transport doesn't use event buffer priorities. All incoming + * and outgoing events use buffers from the same pool. + * + * The "skip" definitions are here so that when buffers cannot be allocated, + * the command or acl packets are simply skipped so that the HCI interface + * does not lose synchronization and resets dont (necessarily) occur. + */ + +STATS_SECT_START(hci_sock_stats) + STATS_SECT_ENTRY(imsg) + STATS_SECT_ENTRY(icmd) + STATS_SECT_ENTRY(ievt) + STATS_SECT_ENTRY(iacl) + STATS_SECT_ENTRY(ibytes) + STATS_SECT_ENTRY(ierr) + STATS_SECT_ENTRY(imem) + STATS_SECT_ENTRY(omsg) + STATS_SECT_ENTRY(oacl) + STATS_SECT_ENTRY(ocmd) + STATS_SECT_ENTRY(oevt) + STATS_SECT_ENTRY(obytes) + STATS_SECT_ENTRY(oerr) +STATS_SECT_END + +STATS_SECT_DECL(hci_sock_stats) hci_sock_stats; +STATS_NAME_START(hci_sock_stats) + STATS_NAME(hci_sock_stats, imsg) + STATS_NAME(hci_sock_stats, icmd) + STATS_NAME(hci_sock_stats, ievt) + STATS_NAME(hci_sock_stats, iacl) + STATS_NAME(hci_sock_stats, ibytes) + STATS_NAME(hci_sock_stats, ierr) + STATS_NAME(hci_sock_stats, imem) + STATS_NAME(hci_sock_stats, omsg) + STATS_NAME(hci_sock_stats, oacl) + STATS_NAME(hci_sock_stats, ocmd) + STATS_NAME(hci_sock_stats, oevt) + STATS_NAME(hci_sock_stats, obytes) + STATS_NAME(hci_sock_stats, oerr) +STATS_NAME_END(hci_sock_stats) + +/*** + * NOTES: + * The "skip" definitions are here so that when buffers cannot be allocated, + * the command or acl packets are simply skipped so that the HCI interface + * does not lose synchronization and resets dont (necessarily) occur. + */ +#define BLE_HCI_UART_H4_NONE 0x00 +#define BLE_HCI_UART_H4_CMD 0x01 +#define BLE_HCI_UART_H4_ACL 0x02 +#define BLE_HCI_UART_H4_SCO 0x03 +#define BLE_HCI_UART_H4_EVT 0x04 +#define BLE_HCI_UART_H4_SYNC_LOSS 0x80 +#define BLE_HCI_UART_H4_SKIP_CMD 0x81 +#define BLE_HCI_UART_H4_SKIP_ACL 0x82 + +#if MYNEWT + +#define BLE_SOCK_STACK_SIZE \ + OS_STACK_ALIGN(MYNEWT_VAL(BLE_SOCK_STACK_SIZE)) + +struct os_task ble_sock_task; + +#endif + +static struct os_mempool ble_hci_sock_evt_hi_pool; +static os_membuf_t ble_hci_sock_evt_hi_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_sock_evt_lo_pool; +static os_membuf_t ble_hci_sock_evt_lo_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_sock_cmd_pool; +static os_membuf_t ble_hci_sock_cmd_buf[ + OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ) +]; + +static struct os_mempool ble_hci_sock_acl_pool; +static struct os_mbuf_pool ble_hci_sock_acl_mbuf_pool; + +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) +/* + * The MBUF payload size must accommodate the HCI data header size plus the + * maximum ACL data packet length. The ACL block size is the size of the + * mbufs we will allocate. + */ + +static os_membuf_t ble_hci_sock_acl_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE) +]; + +static ble_hci_trans_rx_cmd_fn *ble_hci_sock_rx_cmd_cb; +static void *ble_hci_sock_rx_cmd_arg; +static ble_hci_trans_rx_acl_fn *ble_hci_sock_rx_acl_cb; +static void *ble_hci_sock_rx_acl_arg; + +static struct ble_hci_sock_state { + int sock; + struct ble_npl_eventq evq; + struct ble_npl_event ev; + struct ble_npl_callout timer; + + uint16_t rx_off; + uint8_t rx_data[512]; +} ble_hci_sock_state; + +#if MYNEWT_VAL(BLE_SOCK_USE_TCP) +static int s_ble_hci_device = MYNEWT_VAL(BLE_SOCK_TCP_PORT); +#elif MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE) +static int s_ble_hci_device = MYNEWT_VAL(BLE_SOCK_LINUX_DEV); +#endif + +/** + * Allocates a buffer (mbuf) for ACL operation. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +static struct os_mbuf * +ble_hci_trans_acl_buf_alloc(void) +{ + struct os_mbuf *m; + + /* + * XXX: note that for host only there would be no need to allocate + * a user header. Address this later. + */ + m = os_mbuf_get_pkthdr(&ble_hci_sock_acl_mbuf_pool, + sizeof(struct ble_mbuf_hdr)); + return m; +} + +static int +ble_hci_sock_acl_tx(struct os_mbuf *om) +{ + struct msghdr msg; + struct iovec iov[8]; + int i; + struct os_mbuf *m; + uint8_t ch; + + memset(&msg, 0, sizeof(msg)); + memset(iov, 0, sizeof(iov)); + + msg.msg_iov = iov; + + ch = BLE_HCI_UART_H4_ACL; + iov[0].iov_len = 1; + iov[0].iov_base = &ch; + i = 1; + for (m = om; m; m = SLIST_NEXT(m, om_next)) { + iov[i].iov_base = m->om_data; + iov[i].iov_len = m->om_len; + i++; + } + msg.msg_iovlen = i; + + STATS_INC(hci_sock_stats, omsg); + STATS_INC(hci_sock_stats, oacl); + STATS_INCN(hci_sock_stats, obytes, OS_MBUF_PKTLEN(om) + 1); + i = sendmsg(ble_hci_sock_state.sock, &msg, 0); + os_mbuf_free_chain(om); + if (i != OS_MBUF_PKTLEN(om) + 1) { + if (i < 0) { + dprintf(1, "sendmsg() failed : %d\n", errno); + } else { + dprintf(1, "sendmsg() partial write: %d\n", i); + } + STATS_INC(hci_sock_stats, oerr); + return BLE_ERR_MEM_CAPACITY; + } + return 0; +} + +static int +ble_hci_sock_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type) +{ + struct msghdr msg; + struct iovec iov[8]; + int len; + int i; + uint8_t ch; + + memset(&msg, 0, sizeof(msg)); + memset(iov, 0, sizeof(iov)); + + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + ch = h4_type; + iov[0].iov_len = 1; + iov[0].iov_base = &ch; + iov[1].iov_base = hci_ev; + if (h4_type == BLE_HCI_UART_H4_CMD) { + len = sizeof(struct ble_hci_cmd) + hci_ev[2]; + STATS_INC(hci_sock_stats, ocmd); + } else if (h4_type == BLE_HCI_UART_H4_EVT) { + len = sizeof(struct ble_hci_ev) + hci_ev[1]; + STATS_INC(hci_sock_stats, oevt); + } else { + assert(0); + } + iov[1].iov_len = len; + + STATS_INC(hci_sock_stats, omsg); + STATS_INCN(hci_sock_stats, obytes, len + 1); + + i = sendmsg(ble_hci_sock_state.sock, &msg, 0); + ble_hci_trans_buf_free(hci_ev); + if (i != len + 1) { + if (i < 0) { + dprintf(1, "sendmsg() failed : %d\n", errno); + } else { + dprintf(1, "sendmsg() partial write: %d\n", i); + } + STATS_INC(hci_sock_stats, oerr); + return BLE_ERR_MEM_CAPACITY; + } + + return 0; +} + +static int +ble_hci_sock_rx_msg(void) +{ + struct ble_hci_sock_state *bhss; + int len; + struct os_mbuf *m; + uint8_t *data; + int sr; + int rc; + + bhss = &ble_hci_sock_state; + if (bhss->sock < 0) { + return -1; + } + len = read(bhss->sock, bhss->rx_data + bhss->rx_off, + sizeof(bhss->rx_data) - bhss->rx_off); + if (len < 0) { + return -2; + } + if (len == 0) { + return -1; + } + bhss->rx_off += len; + STATS_INCN(hci_sock_stats, ibytes, len); + + while (bhss->rx_off > 0) { + switch (bhss->rx_data[0]) { +#if MYNEWT_VAL(BLE_CONTROLLER) + case BLE_HCI_UART_H4_CMD: + if (bhss->rx_off < sizeof(struct ble_hci_cmd)) { + return -1; + } + len = 1 + sizeof(struct ble_hci_cmd) + bhss->rx_data[3]; + if (bhss->rx_off < len) { + return -1; + } + STATS_INC(hci_sock_stats, imsg); + STATS_INC(hci_sock_stats, icmd); + data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + if (!data) { + STATS_INC(hci_sock_stats, ierr); + break; + } + memcpy(data, &bhss->rx_data[1], len - 1); + OS_ENTER_CRITICAL(sr); + rc = ble_hci_sock_rx_cmd_cb(data, ble_hci_sock_rx_cmd_arg); + OS_EXIT_CRITICAL(sr); + if (rc) { + ble_hci_trans_buf_free(data); + STATS_INC(hci_sock_stats, ierr); + break; + } + break; +#endif +#if MYNEWT_VAL(BLE_HOST) + case BLE_HCI_UART_H4_EVT: + if (bhss->rx_off < sizeof(struct ble_hci_ev)) { + return -1; + } + len = 1 + sizeof(struct ble_hci_ev) + bhss->rx_data[2]; + if (bhss->rx_off < len) { + return -1; + } + STATS_INC(hci_sock_stats, imsg); + STATS_INC(hci_sock_stats, ievt); + data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!data) { + STATS_INC(hci_sock_stats, ierr); + break; + } + memcpy(data, &bhss->rx_data[1], len - 1); + OS_ENTER_CRITICAL(sr); + rc = ble_hci_sock_rx_cmd_cb(data, ble_hci_sock_rx_cmd_arg); + OS_EXIT_CRITICAL(sr); + if (rc) { + ble_hci_trans_buf_free(data); + STATS_INC(hci_sock_stats, ierr); + return 0; + } + break; +#endif + case BLE_HCI_UART_H4_ACL: + if (bhss->rx_off < BLE_HCI_DATA_HDR_SZ) { + return -1; + } + len = 1 + BLE_HCI_DATA_HDR_SZ + (bhss->rx_data[4] << 8) + + bhss->rx_data[3]; + if (bhss->rx_off < len) { + return -1; + } + STATS_INC(hci_sock_stats, imsg); + STATS_INC(hci_sock_stats, iacl); + m = ble_hci_trans_acl_buf_alloc(); + if (!m) { + STATS_INC(hci_sock_stats, imem); + break; + } + if (os_mbuf_append(m, &bhss->rx_data[1], len - 1)) { + STATS_INC(hci_sock_stats, imem); + os_mbuf_free_chain(m); + break; + } + OS_ENTER_CRITICAL(sr); + ble_hci_sock_rx_acl_cb(m, ble_hci_sock_rx_acl_arg); + OS_EXIT_CRITICAL(sr); + break; + default: + STATS_INC(hci_sock_stats, ierr); + break; + } + memmove(bhss->rx_data, &bhss->rx_data[len], bhss->rx_off - len); + bhss->rx_off -= len; + } + return 0; +} + +static void +ble_hci_sock_rx_ev(struct ble_npl_event *ev) +{ + int rc; + ble_npl_time_t timeout; + + rc = ble_hci_sock_rx_msg(); + if (rc == 0) { + ble_npl_eventq_put(&ble_hci_sock_state.evq, &ble_hci_sock_state.ev); + } else { + rc = ble_npl_time_ms_to_ticks(10, &timeout); + ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout); + } +} + +#if MYNEWT_VAL(BLE_SOCK_USE_TCP) +static int +ble_hci_sock_config(void) +{ + struct ble_hci_sock_state *bhss = &ble_hci_sock_state; + struct sockaddr_in sin; + ble_npl_time_t timeout; + int s; + int rc; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(s_ble_hci_device); + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); +#ifdef MN_OSX + sin.sin_len = sizeof(sin); +#endif + +#if 0 + if (bhss->sock >= 0) { + close(bhss->sock); + bhss->sock = -1; + } +#endif + if (bhss->sock < 0) { + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + goto err; + } + + rc = connect(s, (struct sockaddr *)&sin, sizeof(sin)); + if (rc) { + dprintf(1, "connect() failed: %d\n", errno); + goto err; + } + + rc = 1; + + rc = ioctl(s, FIONBIO, (char *)&rc); + if (rc) { + goto err; + } + bhss->sock = s; + } + rc = ble_npl_time_ms_to_ticks(10, &timeout); + if (rc) { + goto err; + } + ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout); + + return 0; +err: + if (s >= 0) { + close(s); + } + return BLE_ERR_HW_FAIL; +} +#endif + +#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE) +static int +ble_hci_sock_config(void) +{ + struct sockaddr_hci shci; + int s; + int rc; + ble_npl_time_t timeout; + + memset(&shci, 0, sizeof(shci)); + shci.hci_family = AF_BLUETOOTH; + shci.hci_dev = s_ble_hci_device; + shci.hci_channel = HCI_CHANNEL_USER; + + if (ble_hci_sock_state.sock >= 0) { + close(ble_hci_sock_state.sock); + ble_hci_sock_state.sock = -1; + } + + s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (s < 0) { + dprintf(1, "socket() failed %d\n", errno); + goto err; + } + + /* + * HCI User Channel requires exclusive access to the device. + * The device has to be down at the time of binding. + */ + ioctl(s, HCIDEVDOWN, shci.hci_dev); + + rc = bind(s, (struct sockaddr *)&shci, sizeof(shci)); + if (rc) { + dprintf(1, "bind() failed %d hci%d\n", errno, shci.hci_dev); + goto err; + } + + rc = 1; + + rc = ioctl(s, FIONBIO, (char *)&rc); + if (rc) { + goto err; + } + ble_hci_sock_state.sock = s; + + rc = ble_npl_time_ms_to_ticks(10, &timeout); + if (rc) { + goto err; + } + ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout); + + return 0; +err: + if (s >= 0) { + close(s); + } + return BLE_ERR_HW_FAIL; +} +#endif +/** + * Sends an HCI event from the controller to the host. + * + * @param cmd The HCI event to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_ll_evt_tx(uint8_t *cmd) +{ + return ble_hci_sock_cmdevt_tx(cmd, BLE_HCI_UART_H4_EVT); +} + +/** + * Sends ACL data from controller to host. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + return ble_hci_sock_acl_tx(om); +} + +/** + * Sends an HCI command from the host to the controller. + * + * @param cmd The HCI command to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + return ble_hci_sock_cmdevt_tx(cmd, BLE_HCI_UART_H4_CMD); +} + +/** + * Sends ACL data from host to controller. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + return ble_hci_sock_acl_tx(om); +} + +/** + * Configures the HCI transport to call the specified callback upon receiving + * HCI packets from the controller. This function should only be called by by + * host. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * event. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void +ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_sock_rx_cmd_cb = cmd_cb; + ble_hci_sock_rx_cmd_arg = cmd_arg; + ble_hci_sock_rx_acl_cb = acl_cb; + ble_hci_sock_rx_acl_arg = acl_arg; +} + +/** + * Configures the HCI transport to operate with a host. The transport will + * execute specified callbacks upon receiving HCI packets from the controller. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * event. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void +ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_sock_rx_cmd_cb = cmd_cb; + ble_hci_sock_rx_cmd_arg = cmd_arg; + ble_hci_sock_rx_acl_cb = acl_cb; + ble_hci_sock_rx_acl_arg = acl_arg; +} + +/** + * Allocates a flat buffer of the specified type. + * + * @param type The type of buffer to allocate; one of the + * BLE_HCI_TRANS_BUF_[...] constants. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +uint8_t * +ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + buf = os_memblock_get(&ble_hci_sock_cmd_pool); + break; + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_sock_evt_hi_pool); + if (buf == NULL) { + /* If no high-priority event buffers remain, try to grab a + * low-priority one. + */ + buf = os_memblock_get(&ble_hci_sock_evt_lo_pool); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_sock_evt_lo_pool); + break; + + default: + assert(0); + buf = NULL; + } + + return buf; +} + +/** + * Frees the specified flat buffer. The buffer must have been allocated via + * ble_hci_trans_buf_alloc(). + * + * @param buf The buffer to free. + */ +void +ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + + /* + * XXX: this may look a bit odd, but the controller uses the command + * buffer to send back the command complete/status as an immediate + * response to the command. This was done to insure that the controller + * could always send back one of these events when a command was received. + * Thus, we check to see which pool the buffer came from so we can free + * it to the appropriate pool + */ + if (os_memblock_from(&ble_hci_sock_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_sock_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_sock_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_sock_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_sock_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_sock_cmd_pool, buf); + assert(rc == 0); + } +} + +/** + * Resets the HCI UART transport to a clean state. Frees all buffers and + * reconfigures the UART. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_reset(void) +{ + int rc; + + ble_npl_callout_stop(&ble_hci_sock_state.timer); + + /* Reopen the UART. */ + rc = ble_hci_sock_config(); + if (rc != 0) { + dprintf(1, "Failure restarting socket HCI\n"); + return rc; + } + + return 0; +} + +void +ble_hci_sock_ack_handler(void *arg) +{ + struct ble_npl_event *ev; + + while (1) { + ev = ble_npl_eventq_get(&ble_hci_sock_state.evq, BLE_NPL_TIME_FOREVER); + ble_npl_event_run(ev); + } +} + +static void +ble_hci_sock_init_task(void) +{ + ble_npl_eventq_init(&ble_hci_sock_state.evq); + ble_npl_callout_stop(&ble_hci_sock_state.timer); + ble_npl_callout_init(&ble_hci_sock_state.timer, &ble_hci_sock_state.evq, + ble_hci_sock_rx_ev, NULL); + +#if MYNEWT + { + os_stack_t *pstack; + + pstack = malloc(sizeof(os_stack_t)*BLE_SOCK_STACK_SIZE); + assert(pstack); + os_task_init(&ble_sock_task, "hci_sock", ble_hci_sock_ack_handler, NULL, + MYNEWT_VAL(BLE_SOCK_TASK_PRIO), BLE_NPL_TIME_FOREVER, pstack, + BLE_SOCK_STACK_SIZE); + } +#else +/* + * For non-Mynewt OS it is required that OS creates task for HCI SOCKET + * to run ble_hci_sock_ack_handler. + */ + +#endif + +} + +void +ble_hci_sock_set_device(int dev) +{ + s_ble_hci_device = dev; +} + +/** + * Initializes the UART HCI transport module. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +void +ble_hci_sock_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + memset(&ble_hci_sock_state, 0, sizeof(ble_hci_sock_state)); + ble_hci_sock_state.sock = -1; + + ble_hci_sock_init_task(); + ble_npl_event_init(&ble_hci_sock_state.ev, ble_hci_sock_rx_ev, NULL); + + rc = os_mempool_init(&ble_hci_sock_acl_pool, + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE, + ble_hci_sock_acl_buf, + "ble_hci_sock_acl_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&ble_hci_sock_acl_mbuf_pool, + &ble_hci_sock_acl_pool, + ACL_BLOCK_SIZE, + MYNEWT_VAL(BLE_ACL_BUF_COUNT)); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of HCI command buffers. NOTE: we currently dont + * allow this to be configured. The controller will only allow one + * outstanding command. We decided to keep this a pool in case we allow + * allow the controller to handle more than one outstanding command. + */ + rc = os_mempool_init(&ble_hci_sock_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + &ble_hci_sock_cmd_buf, + "ble_hci_sock_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_sock_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + &ble_hci_sock_evt_hi_buf, + "ble_hci_sock_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_sock_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_sock_evt_lo_buf, + "ble_hci_sock_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_hci_sock_config(); + SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failure configuring socket HCI"); + + rc = stats_init_and_reg(STATS_HDR(hci_sock_stats), + STATS_SIZE_INIT_PARMS(hci_sock_stats, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(hci_sock_stats), "hci_socket"); + SYSINIT_PANIC_ASSERT(rc == 0); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml new file mode 100644 index 00000000..2050f646 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml @@ -0,0 +1,79 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_HCI_EVT_BUF_SIZE: + description: 'The size of the allocated event buffers' + value: 70 + BLE_HCI_EVT_HI_BUF_COUNT: + description: 'The number of high priority event buffers' + value: 8 + BLE_HCI_EVT_LO_BUF_COUNT: + description: 'The number of low priority event buffers' + value: 8 + BLE_ACL_BUF_COUNT: + description: 'The number of ACL data buffers' + value: 24 + BLE_ACL_BUF_SIZE: + description: > + This is the maximum size of the data portion of HCI ACL data + packets. It does not include the HCI data header (of 4 bytes). + value: 255 + + BLE_HCI_ACL_OUT_COUNT: + description: > + This count is used in creating a pool of elements used by the + code to enqueue various elements. In the case of the controller + only HCI, this number should be equal to the number of mbufs in + the msys pool. For host only, it is really dependent on the + number of ACL buffers that the controller tells the host it + has. + value: 12 + + BLE_SOCK_USE_TCP: + description: 'Use TCP socket, connects to BLE_SOCK_TCP_PORT' + value: 1 + + BLE_SOCK_TCP_PORT: + description: 'ipv4 tcp port to connect to' + value: 14433 + + BLE_SOCK_USE_LINUX_BLUE: + description: 'Use Linux bluetooth raw socket' + value: 0 + + BLE_SOCK_LINUX_DEV: + description: 'linux kernel device' + value: 0 + + BLE_SOCK_TASK_PRIO: + description: 'Priority of the HCI socket task.' + type: task_priority + value: 9 + + BLE_SOCK_STACK_SIZE: + description: 'Size of the HCI socket stack (units=words).' + value: 80 + + BLE_SOCK_CLI_SYSINIT_STAGE: + description: > + Sysinit stage for the socket BLE transport. + value: 500 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 257 diff --git a/src/libs/mynewt-nimble/nimble/transport/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/syscfg.yml new file mode 100644 index 00000000..137d6e94 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/syscfg.yml @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_HCI_TRANSPORT: + description: > + Selects HCI transport to be included in build. + This has virtually the same effect as including package dependency + manually, but it allows to easily override HCI transport package in + application or target settings. + value: builtin + restrictions: $notnull + choices: + - builtin # Built-in NimBLE controller and RAM transport + - custom # Custom transport, has to be included manually by user + - ram # RAM transport + - uart # UART HCI H4 transport + - socket # Socket transport (for native builds) + - emspi # SPI transport for EM Microelectionic controllers + - da1469x # Dialog DA1469x integrated controller + +# Deprecated settings + BLE_HCI_TRANSPORT_NIMBLE_BUILTIN: + description: Use BLE_HCI_TRANSPORT instead. + value: 0 + deprecated: 1 + BLE_HCI_TRANSPORT_EMSPI: + description: Use BLE_HCI_TRANSPORT instead. + value: 0 + deprecated: 1 + BLE_HCI_TRANSPORT_RAM: + description: Use BLE_HCI_TRANSPORT instead. + value: 0 + deprecated: 1 + BLE_HCI_TRANSPORT_SOCKET: + description: Use BLE_HCI_TRANSPORT instead. + value: 0 + deprecated: 1 + BLE_HCI_TRANSPORT_UART: + description: Use BLE_HCI_TRANSPORT instead. + value: 0 + deprecated: 1 + +syscfg.vals.BLE_HCI_TRANSPORT_NIMBLE_BUILTIN: + BLE_HCI_TRANSPORT: builtin +syscfg.vals.BLE_HCI_TRANSPORT_RAM: + BLE_HCI_TRANSPORT: ram +syscfg.vals.BLE_HCI_TRANSPORT_UART: + BLE_HCI_TRANSPORT: uart +syscfg.vals.BLE_HCI_TRANSPORT_SOCKET: + BLE_HCI_TRANSPORT: socket +syscfg.vals.BLE_HCI_TRANSPORT_EMSPI: + BLE_HCI_TRANSPORT: emspi diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h b/src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h new file mode 100644 index 00000000..e5e10841 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef H_BLE_HCI_UART_ +#define H_BLE_HCI_UART_ + +#ifdef __cplusplus +extern "C" { +#endif + +void ble_hci_uart_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml new file mode 100644 index 00000000..95681373 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/transport/uart +pkg.description: XXX +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - "@apache-mynewt-core/hw/hal" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/util/mem" + - nimble + +pkg.apis: + - ble_transport + +pkg.init: + ble_hci_uart_init: 'MYNEWT_VAL(BLE_TRANS_UART_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c b/src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c new file mode 100644 index 00000000..ac6af28e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c @@ -0,0 +1,1187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include "sysinit/sysinit.h" +#include "syscfg/syscfg.h" +#include "os/os_cputime.h" +#include "bsp/bsp.h" +#include "os/os.h" +#include "mem/mem.h" +#include "hal/hal_gpio.h" +#include "hal/hal_uart.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" + +#include "transport/uart/ble_hci_uart.h" + +#define BLE_HCI_UART_EVT_COUNT \ + (MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT) + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT)) + +/*** + * NOTES: + * The UART HCI transport doesn't use event buffer priorities. All incoming + * and outgoing events use buffers from the same pool. + * + * The "skip" definitions are here so that when buffers cannot be allocated, + * the command or acl packets are simply skipped so that the HCI interface + * does not lose synchronization and resets dont (necessarily) occur. + */ + +/* XXX: for now, define this here */ +#if MYNEWT_VAL(BLE_CONTROLLER) +extern void ble_ll_data_buffer_overflow(void); +extern void ble_ll_hw_error(uint8_t err); + +static const uint8_t ble_hci_uart_reset_cmd[4] = { 0x01, 0x03, 0x0C, 0x00 }; +#endif + +/*** + * NOTES: + * The "skip" definitions are here so that when buffers cannot be allocated, + * the command or acl packets are simply skipped so that the HCI interface + * does not lose synchronization and resets dont (necessarily) occur. + */ +#define BLE_HCI_UART_H4_NONE 0x00 +#define BLE_HCI_UART_H4_CMD 0x01 +#define BLE_HCI_UART_H4_ACL 0x02 +#define BLE_HCI_UART_H4_SCO 0x03 +#define BLE_HCI_UART_H4_EVT 0x04 +#define BLE_HCI_UART_H4_SYNC_LOSS 0x80 +#define BLE_HCI_UART_H4_SKIP_CMD 0x81 +#define BLE_HCI_UART_H4_SKIP_ACL 0x82 +#define BLE_HCI_UART_H4_LE_EVT 0x83 +#define BLE_HCI_UART_H4_SKIP_EVT 0x84 + +static ble_hci_trans_rx_cmd_fn *ble_hci_uart_rx_cmd_cb; +static void *ble_hci_uart_rx_cmd_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_uart_rx_acl_cb; +static void *ble_hci_uart_rx_acl_arg; + +static struct os_mempool ble_hci_uart_evt_hi_pool; +static os_membuf_t ble_hci_uart_evt_hi_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_uart_evt_lo_pool; +static os_membuf_t ble_hci_uart_evt_lo_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) +]; + +static struct os_mempool ble_hci_uart_cmd_pool; +static os_membuf_t ble_hci_uart_cmd_buf[ + OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ) +]; + +static struct os_mbuf_pool ble_hci_uart_acl_mbuf_pool; +static struct os_mempool_ext ble_hci_uart_acl_pool; + +/* + * The MBUF payload size must accommodate the HCI data header size plus the + * maximum ACL data packet length. The ACL block size is the size of the + * mbufs we will allocate. + */ +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) + +static os_membuf_t ble_hci_uart_acl_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE) +]; + +/** + * A packet to be sent over the UART. This can be a command, an event, or ACL + * data. + */ +struct ble_hci_uart_pkt { + STAILQ_ENTRY(ble_hci_uart_pkt) next; + void *data; + uint8_t type; +}; + +static struct os_mempool ble_hci_uart_pkt_pool; +static os_membuf_t ble_hci_uart_pkt_buf[ + OS_MEMPOOL_SIZE(BLE_HCI_UART_EVT_COUNT + 1 + + MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT), + sizeof (struct ble_hci_uart_pkt)) +]; + +/** + * An incoming or outgoing command or event. + */ +struct ble_hci_uart_cmd { + uint8_t *data; /* Pointer to ble_hci_uart_cmd data */ + uint16_t cur; /* Number of bytes read/written */ + uint16_t len; /* Total number of bytes to read/write */ +}; + +/** + * An incoming ACL data packet. + */ +struct ble_hci_uart_acl { + struct os_mbuf *buf; /* Buffer containing the data */ + uint8_t *dptr; /* Pointer to where bytes should be placed */ + uint16_t len; /* Target size when buf is considered complete */ + uint16_t rxd_bytes; /* current count of bytes received for packet */ +}; + +/** + * Structure for transmitting ACL packets over UART + * + */ +struct ble_hci_uart_h4_acl_tx +{ + uint8_t *dptr; + struct os_mbuf *tx_acl; +}; + +static struct { + /*** State of data received over UART. */ + uint8_t rx_type; /* Pending packet type. 0 means nothing pending */ + union { + struct ble_hci_uart_cmd rx_cmd; + struct ble_hci_uart_acl rx_acl; + }; + + /*** State of data transmitted over UART. */ + uint8_t tx_type; /* Pending packet type. 0 means nothing pending */ + uint16_t rem_tx_len; /* Used for acl tx only currently */ + union { + struct ble_hci_uart_cmd tx_cmd; + struct ble_hci_uart_h4_acl_tx tx_pkt; + }; + STAILQ_HEAD(, ble_hci_uart_pkt) tx_pkts; /* Packet queue to send to UART */ +} ble_hci_uart_state; + +/** + * Allocates a buffer (mbuf) for ACL operation. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +static struct os_mbuf * +ble_hci_trans_acl_buf_alloc(void) +{ + struct os_mbuf *m; + uint8_t usrhdr_len; + +#if MYNEWT_VAL(BLE_CONTROLLER) + usrhdr_len = sizeof(struct ble_mbuf_hdr); +#elif MYNEWT_VAL(BLE_HS_FLOW_CTRL) + usrhdr_len = BLE_MBUF_HS_HDR_LEN; +#else + usrhdr_len = 0; +#endif + + m = os_mbuf_get_pkthdr(&ble_hci_uart_acl_mbuf_pool, usrhdr_len); + return m; +} + +static int +ble_hci_uart_acl_tx(struct os_mbuf *om) +{ + struct ble_hci_uart_pkt *pkt; + os_sr_t sr; + + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + + pkt = os_memblock_get(&ble_hci_uart_pkt_pool); + if (pkt == NULL) { + os_mbuf_free_chain(om); + return BLE_ERR_MEM_CAPACITY; + } + + pkt->type = BLE_HCI_UART_H4_ACL; + pkt->data = om; + + OS_ENTER_CRITICAL(sr); + STAILQ_INSERT_TAIL(&ble_hci_uart_state.tx_pkts, pkt, next); + OS_EXIT_CRITICAL(sr); + + hal_uart_start_tx(MYNEWT_VAL(BLE_HCI_UART_PORT)); + + return 0; +} + +static int +ble_hci_uart_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type) +{ + struct ble_hci_uart_pkt *pkt; + os_sr_t sr; + + pkt = os_memblock_get(&ble_hci_uart_pkt_pool); + if (pkt == NULL) { + ble_hci_trans_buf_free(hci_ev); + return BLE_ERR_MEM_CAPACITY; + } + + pkt->type = h4_type; + pkt->data = hci_ev; + + OS_ENTER_CRITICAL(sr); + STAILQ_INSERT_TAIL(&ble_hci_uart_state.tx_pkts, pkt, next); + OS_EXIT_CRITICAL(sr); + + hal_uart_start_tx(MYNEWT_VAL(BLE_HCI_UART_PORT)); + + return 0; +} + +/** + * @return The packet type to transmit on success; + * -1 if there is nothing to send. + */ +static int +ble_hci_uart_tx_pkt_type(void) +{ + struct ble_hci_uart_pkt *pkt; + struct os_mbuf *om; + os_sr_t sr; + int rc; + + OS_ENTER_CRITICAL(sr); + + pkt = STAILQ_FIRST(&ble_hci_uart_state.tx_pkts); + if (!pkt) { + OS_EXIT_CRITICAL(sr); + return -1; + } + + STAILQ_REMOVE(&ble_hci_uart_state.tx_pkts, pkt, ble_hci_uart_pkt, next); + + OS_EXIT_CRITICAL(sr); + + rc = pkt->type; + switch (pkt->type) { + case BLE_HCI_UART_H4_CMD: + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_CMD; + ble_hci_uart_state.tx_cmd.data = pkt->data; + ble_hci_uart_state.tx_cmd.cur = 0; + ble_hci_uart_state.tx_cmd.len = ble_hci_uart_state.tx_cmd.data[2] + + sizeof(struct ble_hci_cmd); + break; + + case BLE_HCI_UART_H4_EVT: + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_EVT; + ble_hci_uart_state.tx_cmd.data = pkt->data; + ble_hci_uart_state.tx_cmd.cur = 0; + ble_hci_uart_state.tx_cmd.len = ble_hci_uart_state.tx_cmd.data[1] + + sizeof(struct ble_hci_ev); + break; + + case BLE_HCI_UART_H4_ACL: + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_ACL; + om = (struct os_mbuf *)pkt->data; + /* NOTE: first mbuf must have non-zero length */ + os_mbuf_trim_front(om); + ble_hci_uart_state.tx_pkt.tx_acl = om; + ble_hci_uart_state.tx_pkt.dptr = om->om_data; + ble_hci_uart_state.rem_tx_len = OS_MBUF_PKTLEN(om); + break; + + default: + rc = -1; + break; + } + + os_memblock_put(&ble_hci_uart_pkt_pool, pkt); + + return rc; +} + +/** + * @return The byte to transmit on success; + * -1 if there is nothing to send. + */ +static int +ble_hci_uart_tx_char(void *arg) +{ + uint8_t u8; + int rc; + struct os_mbuf *om; + + switch (ble_hci_uart_state.tx_type) { + case BLE_HCI_UART_H4_NONE: /* No pending packet, pick one from the queue */ + rc = ble_hci_uart_tx_pkt_type(); + break; + + case BLE_HCI_UART_H4_CMD: + case BLE_HCI_UART_H4_EVT: + rc = ble_hci_uart_state.tx_cmd.data[ble_hci_uart_state.tx_cmd.cur++]; + + if (ble_hci_uart_state.tx_cmd.cur == ble_hci_uart_state.tx_cmd.len) { + ble_hci_trans_buf_free(ble_hci_uart_state.tx_cmd.data); + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE; + } + break; + + case BLE_HCI_UART_H4_ACL: + /* Copy the first unsent byte from the tx buffer and remove it from the + * source. + */ + u8 = ble_hci_uart_state.tx_pkt.dptr[0]; + --ble_hci_uart_state.rem_tx_len; + if (ble_hci_uart_state.rem_tx_len == 0) { + os_mbuf_free_chain(ble_hci_uart_state.tx_pkt.tx_acl); + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE; + } else { + om = ble_hci_uart_state.tx_pkt.tx_acl; + --om->om_len; + if (om->om_len == 0) { + /* Remove and free any zero mbufs */ + while ((om != NULL) && (om->om_len == 0)) { + ble_hci_uart_state.tx_pkt.tx_acl = SLIST_NEXT(om, om_next); + os_mbuf_free(om); + om = ble_hci_uart_state.tx_pkt.tx_acl; + } + /* NOTE: om should never be NULL! What to do? */ + if (om == NULL) { + assert(0); + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE; + } else { + ble_hci_uart_state.tx_pkt.dptr = om->om_data; + } + } else { + ble_hci_uart_state.tx_pkt.dptr++; + } + } + rc = u8; + break; + default: + rc = -1; + break; + } + + return rc; +} + +#if MYNEWT_VAL(BLE_CONTROLLER) +/** + * HCI uart sync lost. + * + * This occurs when the controller receives an invalid packet type or a length + * field that is out of range. The controller needs to send a HW error to the + * host and wait to find a LL reset command. + */ +static void +ble_hci_uart_sync_lost(void) +{ + ble_hci_uart_state.rx_cmd.len = 0; + ble_hci_uart_state.rx_cmd.cur = 0; + ble_hci_uart_state.rx_cmd.data = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + ble_ll_hw_error(BLE_HW_ERR_HCI_SYNC_LOSS); + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SYNC_LOSS; +} +#endif + +/** + * @return The type of packet to follow success; + * -1 if there is no valid packet to receive. + */ +static int +ble_hci_uart_rx_pkt_type(uint8_t data) +{ + struct os_mbuf *m; + + ble_hci_uart_state.rx_type = data; + + switch (ble_hci_uart_state.rx_type) { + /* Host should never receive a command! */ +#if MYNEWT_VAL(BLE_CONTROLLER) + case BLE_HCI_UART_H4_CMD: + ble_hci_uart_state.rx_cmd.len = 0; + ble_hci_uart_state.rx_cmd.cur = 0; + ble_hci_uart_state.rx_cmd.data = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + if (ble_hci_uart_state.rx_cmd.data == NULL) { + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SKIP_CMD; + } + break; +#endif + + /* Controller should never receive an event */ +#if MYNEWT_VAL(BLE_HOST) + case BLE_HCI_UART_H4_EVT: + /* + * The event code is unknown at the moment. Depending on event priority, + * buffer *shall* be allocated from ble_hci_uart_evt_hi_pool + * or "may* be allocated from ble_hci_uart_evt_lo_pool. + * Thus do not allocate the buffer yet. + */ + ble_hci_uart_state.rx_cmd.data = NULL; + ble_hci_uart_state.rx_cmd.len = 0; + ble_hci_uart_state.rx_cmd.cur = 0; + break; +#endif + + case BLE_HCI_UART_H4_ACL: + ble_hci_uart_state.rx_acl.len = 0; + ble_hci_uart_state.rx_acl.rxd_bytes = 0; + m = ble_hci_trans_acl_buf_alloc(); + if (m) { + ble_hci_uart_state.rx_acl.dptr = m->om_data; + } else { + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SKIP_ACL; + } + ble_hci_uart_state.rx_acl.buf = m; + break; + + default: +#if MYNEWT_VAL(BLE_CONTROLLER) + /* + * If we receive an unknown HCI packet type this is considered a loss + * of sync. + */ + ble_hci_uart_sync_lost(); +#else + /* + * XXX: not sure what to do about host in this case. Just go back to + * none for now. + */ + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; +#endif + break; + } + + return 0; +} + +#if MYNEWT_VAL(BLE_CONTROLLER) +/** + * HCI uart sync loss. + * + * Find a LL reset command in the byte stream. The LL reset command is a + * sequence of 4 bytes: + * 0x01 HCI Packet Type = HCI CMD + * 0x03 OCF for reset command + * 0x0C OGF for reset command (0x03 shifted left by two bits as the OGF + * occupies the uopper 6 bits of this byte. + * 0x00 Parameter length of reset command (no parameters). + * + * @param data Byte received over serial port + */ +void +ble_hci_uart_rx_sync_loss(uint8_t data) +{ + int rc; + int index; + + /* + * If we couldnt allocate a command buffer (should not occur but + * possible) try to allocate one on each received character. If we get + * a reset and buffer is not available we have to ignore reset. + */ + if (ble_hci_uart_state.rx_cmd.data == NULL) { + ble_hci_uart_state.rx_cmd.data = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + } + + index = ble_hci_uart_state.rx_cmd.cur; + if (data == ble_hci_uart_reset_cmd[index]) { + if (index == 3) { + if (ble_hci_uart_state.rx_cmd.data == NULL) { + index = 0; + } else { + assert(ble_hci_uart_rx_cmd_cb != NULL); + ble_hci_uart_state.rx_cmd.data[0] = 0x03; + ble_hci_uart_state.rx_cmd.data[1] = 0x0C; + ble_hci_uart_state.rx_cmd.data[2] = 0x00; + rc = ble_hci_uart_rx_cmd_cb(ble_hci_uart_state.rx_cmd.data, + ble_hci_uart_rx_cmd_arg); + if (rc != 0) { + ble_hci_trans_buf_free(ble_hci_uart_state.rx_cmd.data); + } + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } + } else { + ++index; + } + } else { + index = 0; + } + + ble_hci_uart_state.rx_cmd.cur = index; +} + +static void +ble_hci_uart_rx_cmd(uint8_t data) +{ + int rc; + + ble_hci_uart_state.rx_cmd.data[ble_hci_uart_state.rx_cmd.cur++] = data; + + if (ble_hci_uart_state.rx_cmd.cur < sizeof(struct ble_hci_cmd)) { + return; + } + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_cmd)) { + ble_hci_uart_state.rx_cmd.len = ble_hci_uart_state.rx_cmd.data[2] + + sizeof(struct ble_hci_cmd); + } + + if (ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) { + assert(ble_hci_uart_rx_cmd_cb != NULL); + rc = ble_hci_uart_rx_cmd_cb(ble_hci_uart_state.rx_cmd.data, + ble_hci_uart_rx_cmd_arg); + if (rc != 0) { + ble_hci_trans_buf_free(ble_hci_uart_state.rx_cmd.data); + } + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } +} + +static void +ble_hci_uart_rx_skip_cmd(uint8_t data) +{ + ble_hci_uart_state.rx_cmd.cur++; + + if (ble_hci_uart_state.rx_cmd.cur < sizeof(struct ble_hci_cmd)) { + return; + } + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_cmd)) { + ble_hci_uart_state.rx_cmd.len = data + sizeof(struct ble_hci_cmd); + } + + if (ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) { + /* + * XXX: for now we simply skip the command and do nothing. This + * should not happen but at least we retain HCI synch. The host + * can decide what to do in this case. It may be appropriate for + * the controller to attempt to send back a command complete or + * command status in this case. + */ + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } +} +#endif + +#if MYNEWT_VAL(BLE_HOST) +static inline void +ble_hci_uart_rx_evt_cb(void) +{ + int rc; + + if (ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) { + assert(ble_hci_uart_rx_cmd_cb != NULL); + rc = ble_hci_uart_rx_cmd_cb(ble_hci_uart_state.rx_cmd.data, + ble_hci_uart_rx_cmd_arg); + if (rc != 0) { + ble_hci_trans_buf_free(ble_hci_uart_state.rx_cmd.data); + } + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } +} + +static void +ble_hci_uart_rx_evt(uint8_t data) +{ + /* Determine event priority to allocate buffer */ + if (!ble_hci_uart_state.rx_cmd.data) { + /* In case of LE Meta Event priority might be still unknown */ + if (data == BLE_HCI_EVCODE_LE_META) { + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_LE_EVT; + ble_hci_uart_state.rx_cmd.cur++; + return; + } + + ble_hci_uart_state.rx_cmd.data = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + assert(ble_hci_uart_state.rx_cmd.data != NULL); + } + + ble_hci_uart_state.rx_cmd.data[ble_hci_uart_state.rx_cmd.cur++] = data; + + if (ble_hci_uart_state.rx_cmd.cur < sizeof(struct ble_hci_ev)) { + return; + } + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_ev)) { + ble_hci_uart_state.rx_cmd.len = ble_hci_uart_state.rx_cmd.data[1] + + sizeof(struct ble_hci_ev); + } + + ble_hci_uart_rx_evt_cb(); +} + +static void +ble_hci_uart_rx_le_evt(uint8_t data) +{ + ble_hci_uart_state.rx_cmd.cur++; + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_ev)) { + /* LE Meta Event parameter length is never 0 */ + assert(data != 0); + ble_hci_uart_state.rx_cmd.len = data + sizeof(struct ble_hci_ev); + return; + } + + /* Determine event priority to allocate buffer */ + if (!ble_hci_uart_state.rx_cmd.data) { + /* Determine event priority to allocate buffer */ + if (data == BLE_HCI_LE_SUBEV_ADV_RPT || + data == BLE_HCI_LE_SUBEV_EXT_ADV_RPT) { + ble_hci_uart_state.rx_cmd.data = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (ble_hci_uart_state.rx_cmd.data == NULL) { + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SKIP_EVT; + return; + } + } else { + ble_hci_uart_state.rx_cmd.data = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + assert(ble_hci_uart_state.rx_cmd.data != NULL); + } + + ble_hci_uart_state.rx_cmd.data[0] = BLE_HCI_EVCODE_LE_META; + ble_hci_uart_state.rx_cmd.data[1] = + ble_hci_uart_state.rx_cmd.len - sizeof(struct ble_hci_ev); + } + + ble_hci_uart_state.rx_cmd.data[ble_hci_uart_state.rx_cmd.cur - 1] = data; + ble_hci_uart_rx_evt_cb(); +} + +static void +ble_hci_uart_rx_skip_evt(uint8_t data) +{ + if (++ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) { + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } +} +#endif + +static void +ble_hci_uart_rx_acl(uint8_t data) +{ + uint16_t rxd_bytes; + uint16_t pktlen; + + rxd_bytes = ble_hci_uart_state.rx_acl.rxd_bytes; + ble_hci_uart_state.rx_acl.dptr[rxd_bytes] = data; + ++rxd_bytes; + ble_hci_uart_state.rx_acl.rxd_bytes = rxd_bytes; + + if (rxd_bytes < BLE_HCI_DATA_HDR_SZ) { + return; + } + + if (rxd_bytes == BLE_HCI_DATA_HDR_SZ) { + pktlen = ble_hci_uart_state.rx_acl.dptr[3]; + pktlen = (pktlen << 8) + ble_hci_uart_state.rx_acl.dptr[2]; + ble_hci_uart_state.rx_acl.len = pktlen + BLE_HCI_DATA_HDR_SZ; + + /* + * Data portion cannot exceed data length of acl buffer. If it does + * this is considered to be a loss of sync. + */ + if (pktlen > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) { + os_mbuf_free_chain(ble_hci_uart_state.rx_acl.buf); +#if MYNEWT_VAL(BLE_CONTROLLER) + ble_hci_uart_sync_lost(); +#else + /* + * XXX: not sure what to do about host in this case. Just go back to + * none for now. + */ + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; +#endif + } + } + + if (rxd_bytes == ble_hci_uart_state.rx_acl.len) { + assert(ble_hci_uart_rx_acl_cb != NULL); + /* XXX: can this callback fail? What if it does? */ + OS_MBUF_PKTLEN(ble_hci_uart_state.rx_acl.buf) = rxd_bytes; + ble_hci_uart_state.rx_acl.buf->om_len = rxd_bytes; + ble_hci_uart_rx_acl_cb(ble_hci_uart_state.rx_acl.buf, + ble_hci_uart_rx_acl_arg); + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } +} + +static void +ble_hci_uart_rx_skip_acl(uint8_t data) +{ + uint16_t rxd_bytes; + uint16_t pktlen; + + rxd_bytes = ble_hci_uart_state.rx_acl.rxd_bytes; + ++rxd_bytes; + ble_hci_uart_state.rx_acl.rxd_bytes = rxd_bytes; + + if (rxd_bytes == (BLE_HCI_DATA_HDR_SZ - 1)) { + ble_hci_uart_state.rx_acl.len = data; + return; + } + + if (rxd_bytes == BLE_HCI_DATA_HDR_SZ) { + pktlen = data; + pktlen = (pktlen << 8) + ble_hci_uart_state.rx_acl.len; + ble_hci_uart_state.rx_acl.len = pktlen + BLE_HCI_DATA_HDR_SZ; + } + + if (rxd_bytes == ble_hci_uart_state.rx_acl.len) { +/* XXX: I dont like this but for now this denotes controller only */ +#if MYNEWT_VAL(BLE_CONTROLLER) + ble_ll_data_buffer_overflow(); +#endif + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + } +} + +static int +ble_hci_uart_rx_char(void *arg, uint8_t data) +{ + switch (ble_hci_uart_state.rx_type) { + case BLE_HCI_UART_H4_NONE: + return ble_hci_uart_rx_pkt_type(data); +#if MYNEWT_VAL(BLE_CONTROLLER) + case BLE_HCI_UART_H4_CMD: + ble_hci_uart_rx_cmd(data); + return 0; + case BLE_HCI_UART_H4_SKIP_CMD: + ble_hci_uart_rx_skip_cmd(data); + return 0; + case BLE_HCI_UART_H4_SYNC_LOSS: + ble_hci_uart_rx_sync_loss(data); + return 0; +#endif +#if MYNEWT_VAL(BLE_HOST) + case BLE_HCI_UART_H4_EVT: + ble_hci_uart_rx_evt(data); + return 0; + case BLE_HCI_UART_H4_LE_EVT: + ble_hci_uart_rx_le_evt(data); + return 0; + case BLE_HCI_UART_H4_SKIP_EVT: + ble_hci_uart_rx_skip_evt(data); + return 0; +#endif + case BLE_HCI_UART_H4_ACL: + ble_hci_uart_rx_acl(data); + return 0; + case BLE_HCI_UART_H4_SKIP_ACL: + ble_hci_uart_rx_skip_acl(data); + return 0; + default: + /* This should never happen! */ + assert(0); + return 0; + } +} + +static void +ble_hci_uart_set_rx_cbs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_uart_rx_cmd_cb = cmd_cb; + ble_hci_uart_rx_cmd_arg = cmd_arg; + ble_hci_uart_rx_acl_cb = acl_cb; + ble_hci_uart_rx_acl_arg = acl_arg; +} + +static void +ble_hci_uart_free_pkt(uint8_t type, uint8_t *cmdevt, struct os_mbuf *acl) +{ + switch (type) { + case BLE_HCI_UART_H4_NONE: + break; + + case BLE_HCI_UART_H4_CMD: + case BLE_HCI_UART_H4_EVT: + ble_hci_trans_buf_free(cmdevt); + break; + + case BLE_HCI_UART_H4_ACL: + os_mbuf_free_chain(acl); + break; + + default: + assert(0); + break; + } +} + +static int +ble_hci_uart_config(void) +{ + int rc; + + rc = hal_uart_init_cbs(MYNEWT_VAL(BLE_HCI_UART_PORT), + ble_hci_uart_tx_char, NULL, + ble_hci_uart_rx_char, NULL); + if (rc != 0) { + return BLE_ERR_UNSPECIFIED; + } + + rc = hal_uart_config(MYNEWT_VAL(BLE_HCI_UART_PORT), + MYNEWT_VAL(BLE_HCI_UART_BAUD), + MYNEWT_VAL(BLE_HCI_UART_DATA_BITS), + MYNEWT_VAL(BLE_HCI_UART_STOP_BITS), + MYNEWT_VAL(BLE_HCI_UART_PARITY), + MYNEWT_VAL(BLE_HCI_UART_FLOW_CTRL)); + if (rc != 0) { + return BLE_ERR_HW_FAIL; + } + + return 0; +} + +/** + * Sends an HCI event from the controller to the host. + * + * @param cmd The HCI event to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_ll_evt_tx(uint8_t *cmd) +{ + int rc; + + rc = ble_hci_uart_cmdevt_tx(cmd, BLE_HCI_UART_H4_EVT); + return rc; +} + +/** + * Sends ACL data from controller to host. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + int rc; + + rc = ble_hci_uart_acl_tx(om); + return rc; +} + +/** + * Sends an HCI command from the host to the controller. + * + * @param cmd The HCI command to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + int rc; + + rc = ble_hci_uart_cmdevt_tx(cmd, BLE_HCI_UART_H4_CMD); + return rc; +} + +/** + * Sends ACL data from host to controller. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + int rc; + + rc = ble_hci_uart_acl_tx(om); + return rc; +} + +/** + * Configures the HCI transport to call the specified callback upon receiving + * HCI packets from the controller. This function should only be called by by + * host. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * event. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void +ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_uart_set_rx_cbs(cmd_cb, cmd_arg, acl_cb, acl_arg); +} + +/** + * Configures the HCI transport to operate with a host. The transport will + * execute specified callbacks upon receiving HCI packets from the controller. + * + * @param cmd_cb The callback to execute upon receiving an HCI + * event. + * @param cmd_arg Optional argument to pass to the command + * callback. + * @param acl_cb The callback to execute upon receiving ACL + * data. + * @param acl_arg Optional argument to pass to the ACL + * callback. + */ +void +ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_uart_set_rx_cbs(cmd_cb, cmd_arg, acl_cb, acl_arg); +} + +/** + * Allocates a flat buffer of the specified type. + * + * @param type The type of buffer to allocate; one of the + * BLE_HCI_TRANS_BUF_[...] constants. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +uint8_t * +ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + buf = os_memblock_get(&ble_hci_uart_cmd_pool); + break; + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_uart_evt_hi_pool); + if (buf == NULL) { + /* If no high-priority event buffers remain, try to grab a + * low-priority one. + */ + buf = os_memblock_get(&ble_hci_uart_evt_lo_pool); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_uart_evt_lo_pool); + break; + + default: + assert(0); + buf = NULL; + } + + return buf; +} + +/** + * Frees the specified flat buffer. The buffer must have been allocated via + * ble_hci_trans_buf_alloc(). + * + * @param buf The buffer to free. + */ +void +ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + + /* + * XXX: this may look a bit odd, but the controller uses the command + * buffer to send back the command complete/status as an immediate + * response to the command. This was done to insure that the controller + * could always send back one of these events when a command was received. + * Thus, we check to see which pool the buffer came from so we can free + * it to the appropriate pool + */ + if (os_memblock_from(&ble_hci_uart_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_uart_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_uart_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_uart_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_uart_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_uart_cmd_pool, buf); + assert(rc == 0); + } +} + +/** + * Configures a callback to get executed whenever an ACL data packet is freed. + * The function is called in lieu of actually freeing the packet. + * + * @param cb The callback to configure. + * + * @return 0 on success. + */ +int +ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg) +{ + ble_hci_uart_acl_pool.mpe_put_cb = cb; + ble_hci_uart_acl_pool.mpe_put_arg = arg; + return 0; +} + +/** + * Resets the HCI UART transport to a clean state. Frees all buffers and + * reconfigures the UART. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_reset(void) +{ + struct ble_hci_uart_pkt *pkt; + int rc; + + /* Close the UART to prevent race conditions as the buffers are freed. */ + rc = hal_uart_close(MYNEWT_VAL(BLE_HCI_UART_PORT)); + if (rc != 0) { + return BLE_ERR_HW_FAIL; + } + + ble_hci_uart_free_pkt(ble_hci_uart_state.rx_type, + ble_hci_uart_state.rx_cmd.data, + ble_hci_uart_state.rx_acl.buf); + ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE; + + ble_hci_uart_free_pkt(ble_hci_uart_state.tx_type, + ble_hci_uart_state.tx_cmd.data, + ble_hci_uart_state.tx_pkt.tx_acl); + ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE; + + while ((pkt = STAILQ_FIRST(&ble_hci_uart_state.tx_pkts)) != NULL) { + STAILQ_REMOVE(&ble_hci_uart_state.tx_pkts, pkt, ble_hci_uart_pkt, + next); + ble_hci_uart_free_pkt(pkt->type, pkt->data, pkt->data); + os_memblock_put(&ble_hci_uart_pkt_pool, pkt); + } + + /* Reopen the UART. */ + rc = ble_hci_uart_config(); + if (rc != 0) { + return rc; + } + + return 0; +} + +/** + * Initializes the UART HCI transport module. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +void +ble_hci_uart_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + rc = os_mempool_ext_init(&ble_hci_uart_acl_pool, + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE, + ble_hci_uart_acl_buf, + "ble_hci_uart_acl_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&ble_hci_uart_acl_mbuf_pool, + &ble_hci_uart_acl_pool.mpe_mp, + ACL_BLOCK_SIZE, + MYNEWT_VAL(BLE_ACL_BUF_COUNT)); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of HCI command buffers. NOTE: we currently dont + * allow this to be configured. The controller will only allow one + * outstanding command. We decided to keep this a pool in case we allow + * allow the controller to handle more than one outstanding command. + */ + rc = os_mempool_init(&ble_hci_uart_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + ble_hci_uart_cmd_buf, + "ble_hci_uart_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_uart_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_uart_evt_hi_buf, + "ble_hci_uart_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_uart_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_uart_evt_lo_buf, + "ble_hci_uart_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of packet list nodes. NOTE: the number of these + * buffers should be, at least, the total number of event buffers (hi + * and lo), the number of command buffers (currently 1) and the total + * number of buffers that the controller could possibly hand to the host. + */ + rc = os_mempool_init(&ble_hci_uart_pkt_pool, + BLE_HCI_UART_EVT_COUNT + 1 + + MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT), + sizeof (struct ble_hci_uart_pkt), + ble_hci_uart_pkt_buf, + "ble_hci_uart_pkt_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_hci_uart_config(); + SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failure configuring UART HCI"); + + memset(&ble_hci_uart_state, 0, sizeof ble_hci_uart_state); + STAILQ_INIT(&ble_hci_uart_state.tx_pkts); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml new file mode 100644 index 00000000..43486a8b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml @@ -0,0 +1,72 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_HCI_EVT_BUF_SIZE: + description: 'The size of the allocated event buffers' + value: 70 + BLE_HCI_EVT_HI_BUF_COUNT: + description: 'The number of high priority event buffers' + value: 8 + BLE_HCI_EVT_LO_BUF_COUNT: + description: 'The number of low priority event buffers' + value: 8 + BLE_ACL_BUF_COUNT: + description: 'The number of ACL data buffers' + value: 12 + BLE_ACL_BUF_SIZE: + description: > + This is the maximum size of the data portion of HCI ACL data + packets. It does not include the HCI data header (of 4 bytes). + value: 255 + + BLE_HCI_ACL_OUT_COUNT: + description: > + This count is used in creating a pool of elements used by the + code to enqueue various elements. In the case of the controller + only HCI, this number should be equal to the number of mbufs in + the msys pool. For host only, it is really dependent on the + number of ACL buffers that the controller tells the host it + has. + value: 12 + + BLE_HCI_UART_PORT: + description: 'The uart to use for the HCI uart interface' + value: 0 + BLE_HCI_UART_BAUD: + description: 'The baud rate of the HCI uart interface' + value: 1000000 + BLE_HCI_UART_DATA_BITS: + description: 'Number of data bits used for HCI uart interface' + value: 8 + BLE_HCI_UART_STOP_BITS: + description: 'Number of stop bits used for HCI uart interface' + value: 1 + BLE_HCI_UART_PARITY: + description: 'Parity used for HCI uart interface' + value: HAL_UART_PARITY_NONE + BLE_HCI_UART_FLOW_CTRL: + description: 'Flow control used for HCI uart interface' + value: HAL_UART_FLOW_CTL_RTS_CTS + BLE_TRANS_UART_SYSINIT_STAGE: + description: > + Sysinit stage for the UART BLE transport. + value: 500 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 257 |