diff --git a/connectivity/FEATURE_BLE/libraries/cordio_stack/ble-host/sources/hci/dual_chip/hci_evt.c b/connectivity/FEATURE_BLE/libraries/cordio_stack/ble-host/sources/hci/dual_chip/hci_evt.c index 6e507c693c9..fd708ea2ddd 100644 --- a/connectivity/FEATURE_BLE/libraries/cordio_stack/ble-host/sources/hci/dual_chip/hci_evt.c +++ b/connectivity/FEATURE_BLE/libraries/cordio_stack/ble-host/sources/hci/dual_chip/hci_evt.c @@ -367,6 +367,30 @@ static void hciEvtParseLeConnCmpl(hciEvt_t *pMsg, uint8_t *p, uint8_t len) pMsg->hdr.status = pMsg->leConnCmpl.status; } +#ifndef CORDIO_RPA_SWAP_WORKAROUND +#define CORDIO_RPA_SWAP_WORKAROUND 0 +#endif +#if CORDIO_RPA_SWAP_WORKAROUND +/*************************************************************************************************/ +/*! + * \brief Check if address bits are all 0s or all 1s. + * + * \param p Pointer to address. + * + * \return TRUE if address is invalid. + */ +/*************************************************************************************************/ +static bool_t isAddressInvalid(uint8_t *p) +{ + for (int i = 0; i < 6; i++) { + if ((p[i] != 0xff) && (p[i] != 0x00)) { + return FALSE; // meaning valid address + } + } + return TRUE; +} +#endif // CORDIO_RPA_SWAP_WORKAROUND + /*************************************************************************************************/ /*! * \brief Parse an HCI event. @@ -384,9 +408,21 @@ static void hciEvtParseLeEnhancedConnCmpl(hciEvt_t *pMsg, uint8_t *p, uint8_t le BSTREAM_TO_UINT16(pMsg->leConnCmpl.handle, p); BSTREAM_TO_UINT8(pMsg->leConnCmpl.role, p); BSTREAM_TO_UINT8(pMsg->leConnCmpl.addrType, p); - BSTREAM_TO_BDA(pMsg->leConnCmpl.peerAddr, p); - BSTREAM_TO_BDA(pMsg->leConnCmpl.localRpa, p); - BSTREAM_TO_BDA(pMsg->leConnCmpl.peerRpa, p); + +#if CORDIO_RPA_SWAP_WORKAROUND + if (isAddressInvalid(p)) { + memset(pMsg->leConnCmpl.peerRpa, 0x00, BDA_ADDR_LEN); + p += BDA_ADDR_LEN; + BSTREAM_TO_BDA(pMsg->leConnCmpl.localRpa, p); + BSTREAM_TO_BDA(pMsg->leConnCmpl.peerAddr, p); + } else +#endif // CORDIO_RPA_SWAP_WORKAROUND + { + BSTREAM_TO_BDA(pMsg->leConnCmpl.peerAddr, p); + BSTREAM_TO_BDA(pMsg->leConnCmpl.localRpa, p); + BSTREAM_TO_BDA(pMsg->leConnCmpl.peerRpa, p); + } + BSTREAM_TO_UINT16(pMsg->leConnCmpl.connInterval, p); BSTREAM_TO_UINT16(pMsg->leConnCmpl.connLatency, p); BSTREAM_TO_UINT16(pMsg->leConnCmpl.supTimeout, p); diff --git a/connectivity/FEATURE_BLE/source/cordio/mbed_lib.json b/connectivity/FEATURE_BLE/source/cordio/mbed_lib.json index 4b9234e7bd5..f6e5a31e950 100644 --- a/connectivity/FEATURE_BLE/source/cordio/mbed_lib.json +++ b/connectivity/FEATURE_BLE/source/cordio/mbed_lib.json @@ -76,6 +76,16 @@ "help": "Maximum number of EATT channels per DM connection.", "value": 1, "macro_name": "EATT_CONN_CHAN_MAX" + }, + "rpa-swap-workaround": { + "help": "Check connection complete event if it needs the addresses swapped due to bug in controller", + "value": null, + "macro_name": "CORDIO_RPA_SWAP_WORKAROUND" + } + }, + "target_overrides": { + "NUCLEO_WB55RG": { + "rpa-swap-workaround": true } } -} \ No newline at end of file +} diff --git a/connectivity/FEATURE_BLE/source/cordio/source/PalGapImpl.h b/connectivity/FEATURE_BLE/source/cordio/source/PalGapImpl.h index c361ac4d5fa..a78868e8397 100644 --- a/connectivity/FEATURE_BLE/source/cordio/source/PalGapImpl.h +++ b/connectivity/FEATURE_BLE/source/cordio/source/PalGapImpl.h @@ -401,30 +401,18 @@ class PalGap final : public ble::PalGap { static GapConnectionCompleteEvent convert(const hciLeConnCmplEvt_t *conn_evt) { - const bdAddr_t *peer_rpa = &conn_evt->peerRpa; - const bdAddr_t *peer_address = &conn_evt->peerAddr; - -#if defined(TARGET_MCU_STM32WB55xx) - const bdAddr_t invalidAddress = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - if (conn_evt->addrType == DM_ADDR_RANDOM && - memcmp(peer_address, invalidAddress, sizeof(invalidAddress)) == 0 && - memcmp(peer_rpa, invalidAddress, sizeof(invalidAddress) != 0) - ) { - std::swap(peer_rpa, peer_address); - } -#endif return GapConnectionCompleteEvent( conn_evt->status, // note the usage of the stack handle, not the HCI handle conn_evt->hdr.param, (connection_role_t::type) conn_evt->role, (peer_address_type_t::type) conn_evt->addrType, - *peer_address, + conn_evt->peerAddr, conn_evt->connInterval, conn_evt->connLatency, conn_evt->supTimeout, conn_evt->localRpa, - *peer_rpa + conn_evt->peerRpa ); } }; diff --git a/connectivity/drivers/ble/FEATURE_BLE/CMakeLists.txt b/connectivity/drivers/ble/FEATURE_BLE/CMakeLists.txt index 5c879757358..be0af09c091 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/CMakeLists.txt +++ b/connectivity/drivers/ble/FEATURE_BLE/CMakeLists.txt @@ -1,6 +1,10 @@ # Copyright (c) 2020 ARM Limited. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +if("BlueNRG_2" IN_LIST MBED_TARGET_LABELS) + add_subdirectory(COMPONENT_BlueNRG_2) +endif() + if("BlueNRG_MS" IN_LIST MBED_TARGET_LABELS) add_subdirectory(COMPONENT_BlueNRG_MS) endif() diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/BlueNrg2HCIDriver.cpp b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/BlueNrg2HCIDriver.cpp new file mode 100644 index 00000000000..2068ff86d09 --- /dev/null +++ b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/BlueNrg2HCIDriver.cpp @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2017-2020 ARM Limited + * Copyright (c) 2017-2020 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +// drivers +#include "drivers/DigitalOut.h" +#include "drivers/SPI.h" +#include "drivers/InterruptIn.h" +#include "drivers/Timer.h" + +// platform +#include "platform/mbed_wait_api.h" + +// FEATURE_BLE/targets/TARGET_CORDIO +#include "CordioHCIDriver.h" +#include "CordioHCITransportDriver.h" +#include "hci_api.h" +#include "hci_cmd.h" +#include "hci_core.h" +#include "dm_api.h" +#include "bstream.h" +#include "hci_mbed_os_adaptation.h" + +// rtos +#include "rtos/Thread.h" +#include "rtos/Semaphore.h" +#include "rtos/Mutex.h" + +#define HCI_RESET_RAND_CNT 4 + +#define VENDOR_SPECIFIC_EVENT 0xFF +#define EVT_BLUENRG_2_INITIALIZED 0x0001 +#define ACI_READ_CONFIG_DATA_OPCODE 0xFC0D +#define ACI_WRITE_CONFIG_DATA_OPCODE 0xFC0C +#define ACI_GATT_INIT_OPCODE 0xFD01 +#define ACI_GAP_INIT_OPCODE 0xFC8A + +#define PUBLIC_ADDRESS_OFFSET 0x00 +#define RANDOM_STATIC_ADDRESS_OFFSET 0x80 +#define LL_WITHOUT_HOST_OFFSET 0x2C + +#define SPI_STACK_SIZE 1024 + +#define IRQ_TIMEOUT_DURATION 100 //ms + +namespace ble { +namespace vendor { +namespace bluenrg_2 { + +/** + * BlueNRG_2 HCI driver implementation. + * @see CordioHCIDriver + */ +class HCIDriver : public CordioHCIDriver { +public: + /** + * Construction of the BlueNRG_2 HCIDriver. + * @param transport: Transport of the HCI commands. + * @param rst: Name of the reset pin + */ + HCIDriver(CordioHCITransportDriver &transport_driver, PinName rst) : + CordioHCIDriver(transport_driver), rst(rst) { } + + /** + * @see CordioHCIDriver::do_initialize + */ + virtual void do_initialize() + { + bluenrg_2_reset(); + } + + /** + * @see CordioHCIDriver::get_buffer_pool_description + */ + ble::buf_pool_desc_t get_buffer_pool_description() + { + // Use default buffer pool + return ble::CordioHCIDriver::get_default_buffer_pool_description(); + } + + /** + * @see CordioHCIDriver::start_reset_sequence + */ + virtual void start_reset_sequence() + { + reset_received = false; + bluenrg_2_initialized = false; + enable_link_layer_mode_ongoing = false; + /* send an HCI Reset command to start the sequence */ + HciResetCmd(); + } + + /** + * @see CordioHCIDriver::do_terminate + */ + virtual void do_terminate() + { + + } + + /** + * @see CordioHCIDriver::handle_reset_sequence + */ + virtual void handle_reset_sequence(uint8_t *pMsg) + { + uint16_t opcode; + static uint8_t randCnt; + + /* if event is a command complete event */ + if (*pMsg == HCI_CMD_CMPL_EVT) { + /* parse parameters */ + pMsg += HCI_EVT_HDR_LEN; + pMsg++; /* skip num packets */ + BSTREAM_TO_UINT16(opcode, pMsg); + pMsg++; /* skip status */ + + /* decode opcode */ + switch (opcode) { + case HCI_OPCODE_RESET: { + /* initialize rand command count */ + randCnt = 0; + reset_received = true; + // bluenrg_2_initialized event has to come after the hci reset event + bluenrg_2_initialized = false; + } + break; + + // ACL packet + case ACI_WRITE_CONFIG_DATA_OPCODE: + if (enable_link_layer_mode_ongoing) { + enable_link_layer_mode_ongoing = false; + aciGattInit(); + } + break; + + case ACI_GATT_INIT_OPCODE: + aciGapInit(); + break; + + case ACI_GAP_INIT_OPCODE: + aciReadConfigParameter(RANDOM_STATIC_ADDRESS_OFFSET); + break; + + case ACI_READ_CONFIG_DATA_OPCODE: + // note: will send the HCI command to send the random address + pMsg++; /* skip address length */ + set_random_static_address(pMsg); + break; + + case HCI_OPCODE_LE_SET_RAND_ADDR: + HciSetEventMaskCmd((uint8_t *) hciEventMask); + break; + + case HCI_OPCODE_SET_EVENT_MASK: + /* send next command in sequence */ + HciLeSetEventMaskCmd((uint8_t *) hciLeEventMask); + break; + + case HCI_OPCODE_LE_SET_EVENT_MASK: +// Note: the public address is not read because there is no valid public address provisioned by default on the target +// You can enable it thanks to json "valid-public-bd-address" config value +#if MBED_CONF_BLUENRG_2_VALID_PUBLIC_BD_ADDRESS == 1 + /* send next command in sequence */ + HciReadBdAddrCmd(); + break; + + case HCI_OPCODE_READ_BD_ADDR: + /* parse and store event parameters */ + BdaCpy(hciCoreCb.bdAddr, pMsg); + + /* send next command in sequence */ +#endif + HciLeReadBufSizeCmd(); + break; + + case HCI_OPCODE_LE_READ_BUF_SIZE: + /* parse and store event parameters */ + BSTREAM_TO_UINT16(hciCoreCb.bufSize, pMsg); + BSTREAM_TO_UINT8(hciCoreCb.numBufs, pMsg); + + /* initialize ACL buffer accounting */ + hciCoreCb.availBufs = hciCoreCb.numBufs; + + /* send next command in sequence */ + HciLeReadSupStatesCmd(); + break; + + case HCI_OPCODE_LE_READ_SUP_STATES: + /* parse and store event parameters */ + memcpy(hciCoreCb.leStates, pMsg, HCI_LE_STATES_LEN); + + /* send next command in sequence */ + HciLeReadWhiteListSizeCmd(); + break; + + case HCI_OPCODE_LE_READ_WHITE_LIST_SIZE: + /* parse and store event parameters */ + BSTREAM_TO_UINT8(hciCoreCb.whiteListSize, pMsg); + + /* send next command in sequence */ + HciLeReadLocalSupFeatCmd(); + break; + + case HCI_OPCODE_LE_READ_LOCAL_SUP_FEAT: + /* parse and store event parameters */ + BSTREAM_TO_UINT16(hciCoreCb.leSupFeat, pMsg); + + /* send next command in sequence */ + hciCoreReadResolvingListSize(); + break; + + case HCI_OPCODE_LE_READ_RES_LIST_SIZE: + /* parse and store event parameters */ + BSTREAM_TO_UINT8(hciCoreCb.resListSize, pMsg); + + /* send next command in sequence */ + hciCoreReadMaxDataLen(); + break; + + case HCI_OPCODE_LE_READ_MAX_DATA_LEN: { + uint16_t maxTxOctets; + uint16_t maxTxTime; + + BSTREAM_TO_UINT16(maxTxOctets, pMsg); + BSTREAM_TO_UINT16(maxTxTime, pMsg); + + /* use Controller's maximum supported payload octets and packet duration times + * for transmission as Host's suggested values for maximum transmission number + * of payload octets and maximum packet transmission time for new connections. + */ + HciLeWriteDefDataLen(maxTxOctets, maxTxTime); + } + break; + + case HCI_OPCODE_LE_WRITE_DEF_DATA_LEN: + if (hciCoreCb.extResetSeq) { + /* send first extended command */ + (*hciCoreCb.extResetSeq)(pMsg, opcode); + } else { + /* initialize extended parameters */ + hciCoreCb.maxAdvDataLen = 0; + hciCoreCb.numSupAdvSets = 0; + hciCoreCb.perAdvListSize = 0; + + /* send next command in sequence */ + HciLeRandCmd(); + } + break; + + case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN: + case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS: + case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE: + if (hciCoreCb.extResetSeq) { + /* send next extended command in sequence */ + (*hciCoreCb.extResetSeq)(pMsg, opcode); + } + break; + + case HCI_OPCODE_LE_RAND: + /* check if need to send second rand command */ + if (randCnt < (HCI_RESET_RAND_CNT - 1)) { + randCnt++; + HciLeRandCmd(); + } else { + signal_reset_sequence_done(); + } + break; + + default: + break; + } + } else { + /** + * vendor specific event + */ + if (pMsg[0] == VENDOR_SPECIFIC_EVENT) { + /* parse parameters */ + pMsg += HCI_EVT_HDR_LEN; + BSTREAM_TO_UINT16(opcode, pMsg); + + if (opcode == EVT_BLUENRG_2_INITIALIZED) { + if (bluenrg_2_initialized) { + return; + } + bluenrg_2_initialized = true; + if (reset_received) { + aciEnableLinkLayerModeOnly(); + } + } + + } + } + } + +private: + void aciEnableLinkLayerModeOnly() + { + uint8_t data[1] = { 0x01 }; + enable_link_layer_mode_ongoing = true; + aciWriteConfigData(LL_WITHOUT_HOST_OFFSET, data); + } + + void aciGattInit() + { + uint8_t *pBuf = hciCmdAlloc(ACI_GATT_INIT_OPCODE, 0); + if (!pBuf) { + return; + } + hciCmdSend(pBuf); + } + + void aciGapInit() + { + uint8_t *pBuf = hciCmdAlloc(ACI_GAP_INIT_OPCODE, 3); + if (!pBuf) { + return; + } + pBuf[3] = 0xF; + pBuf[4] = 0; + pBuf[5] = 0; + hciCmdSend(pBuf); + } + + void aciReadConfigParameter(uint8_t offset) + { + uint8_t *pBuf = hciCmdAlloc(ACI_READ_CONFIG_DATA_OPCODE, 1); + if (!pBuf) { + return; + } + + pBuf[3] = offset; + hciCmdSend(pBuf); + } + + template + void aciWriteConfigData(uint8_t offset, uint8_t (&buf)[N]) + { + uint8_t *pBuf = hciCmdAlloc(ACI_WRITE_CONFIG_DATA_OPCODE, 2 + N); + if (!pBuf) { + return; + } + + pBuf[3] = offset; + pBuf[4] = N; + memcpy(pBuf + 5, buf, N); + hciCmdSend(pBuf); + } + + void hciCoreReadResolvingListSize(void) + { + /* if LL Privacy is supported by Controller and included */ + if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_PRIVACY) && + (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_PRIVACY)) { + /* send next command in sequence */ + HciLeReadResolvingListSize(); + } else { + hciCoreCb.resListSize = 0; + + /* send next command in sequence */ + hciCoreReadMaxDataLen(); + } + } + + void hciCoreReadMaxDataLen(void) + { + /* if LE Data Packet Length Extensions is supported by Controller and included */ + if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_DATA_LEN_EXT) && + (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_DATA_LEN_EXT)) { + /* send next command in sequence */ + HciLeReadMaxDataLen(); + } else { + /* send next command in sequence */ + HciLeRandCmd(); + } + } + + void bluenrg_2_reset() + { + /* Reset BlueNRG_2 SPI interface. Hold reset line to 0 for 1500us */ + rst = 0; + wait_us(1500); + rst = 1; + + /* Wait for the radio to come back up */ + wait_us(100000); + } + + mbed::DigitalOut rst; + bool reset_received; + bool bluenrg_2_initialized; + bool enable_link_layer_mode_ongoing; +}; + +/** + * Transport driver of the ST BlueNRG_2 shield. + * @important: With that driver, it is assumed that the SPI bus used is not shared + * with other SPI peripherals. The reasons behind this choice are simplicity and + * performance: + * - Reading from the peripheral SPI can be challenging especially if other + * threads access the same SPI bus. Indeed it is common that the function + * spiRead yield nothings even if the chip has signaled data with the irq + * line. Sharing would make the situation worse and increase the risk of + * timeout of HCI commands / response. + * - This driver can be used even if the RTOS is disabled or not present it may + * may be usefull for some targets. + * + * If The SPI is shared with other peripherals then the best option would be to + * handle SPI read in a real time thread woken up by an event flag. + * + * Other mechanisms might also be added in the future to handle data read as an + * event from the stack. This might not be the best solution for all BLE chip; + * especially this one. + */ +class TransportDriver : public CordioHCITransportDriver { +public: + /** + * Construct the transport driver required by a BlueNRG_2 module. + * @param mosi Pin of the SPI mosi + * @param miso Pin of the SPI miso + * @param sclk Pin of the SPI clock + * @param irq Pin used by the module to signal data are available. + */ + TransportDriver(PinName mosi, PinName miso, PinName sclk, PinName ncs, PinName irq) + : spi(mosi, miso, sclk), nCS(ncs), irq(irq), _spi_thread(osPriorityNormal, SPI_STACK_SIZE, _spi_thread_stack) + { + _spi_thread.start(mbed::callback(this, &TransportDriver::spi_read_cb)); + } + + virtual ~TransportDriver() { } + + /** + * @see CordioHCITransportDriver::initialize + */ + virtual void initialize() + { + // Setup the spi for 8 bit data, low clock polarity, + // 2-edge phase, with an 1MHz clock rate + spi.format(8, 1); + spi.frequency(1000000); + + // Deselect the BlueNRG_2 chip by keeping its nCS signal high + nCS = 1; + + wait_us(500); + + // Set the interrupt handler for the device + irq.mode(PullDown); // set irq mode + irq.rise(mbed::callback(this, &TransportDriver::HCI_Isr)); + } + + /** + * @see CordioHCITransportDriver::terminate + */ + virtual void terminate() { } + + /** + * @see CordioHCITransportDriver::write + */ + virtual uint16_t write(uint8_t type, uint16_t len, uint8_t *pData) + { + // repeat write until successfull. A number of attempt or timeout might + // be useful + while (spiWrite(type, pData, len) == 0) { } + return len; + } + +private: + uint16_t spiWrite(uint8_t type, const uint8_t *data, uint16_t data_length) + { + static const uint8_t header_master[] = { 0x0a, 0x00, 0x00, 0x00, 0x00 }; + uint8_t header_slave[5]; + uint16_t data_written = 0; + uint16_t write_buffer_size = 0; + + _spi_mutex.lock(); + + irq.disable_irq(); + + /* CS reset */ + nCS = 0; + + // Wait until BlueNRG_2 is ready. + // When ready it will raise the IRQ pin. + _irq_timer.start(); + while (!dataPresent()) { + auto us = _irq_timer.elapsed_time().count(); + if (us > IRQ_TIMEOUT_DURATION * 1000) { + goto exit; + } + } + + /* Exchange header */ + for (uint8_t i = 0; i < sizeof(header_master); ++i) { + header_slave[i] = spi.write(header_master[i]); + } + + write_buffer_size = header_slave[2] << 8 | header_slave[1]; + + if (write_buffer_size == 0 || write_buffer_size < (data_length + 1)) { + goto exit; + } + + spi.write(type); + + data_written = data_length; + for (uint16_t i = 0; i < data_length; ++i) { + spi.write(data[i]); + } + +exit: + nCS = 1; + + irq.enable_irq(); + + _irq_timer.stop(); + + _spi_mutex.unlock(); + + return data_written; + } + + bool dataPresent() + { + return (irq == 1); + } + + uint16_t spiRead(uint8_t *data_buffer, const uint16_t buffer_size) + { + static const uint8_t header_master[] = {0x0b, 0x00, 0x00, 0x00, 0x00}; + uint8_t header_slave[5]; + uint16_t read_length = 0; + uint16_t data_available = 0; + + irq.disable_irq(); + + nCS = 0; + + /* Read the header */ + for (size_t i = 0; i < sizeof(header_master); i++) { + header_slave[i] = spi.write(header_master[i]); + } + + data_available = (header_slave[4] << 8) | header_slave[3]; + read_length = data_available > buffer_size ? buffer_size : data_available; + + for (uint16_t i = 0; i < read_length; ++i) { + data_buffer[i] = spi.write(0x00); + } + + nCS = 1; + + irq.enable_irq(); + + return read_length; + } + + /* + * might be split into two parts: the IRQ signaling a real time thread and + * the real time thread reading data from the SPI. + */ + void HCI_Isr(void) + { + _spi_read_sem.release(); + } + + void spi_read_cb() + { + uint8_t data_buffer[256]; + while (true) { + _spi_read_sem.acquire(); + + _spi_mutex.lock(); + while (irq == 1) { + uint16_t data_read = spiRead(data_buffer, sizeof(data_buffer)); + on_data_received(data_buffer, data_read); + } + _spi_mutex.unlock(); + } + } + + /** + * Unsafe SPI, does not lock when SPI access happens. + */ + ::mbed::SPI spi; + mbed::DigitalOut nCS; + mbed::InterruptIn irq; + rtos::Thread _spi_thread; + uint8_t _spi_thread_stack[SPI_STACK_SIZE]; + rtos::Semaphore _spi_read_sem; + rtos::Mutex _spi_mutex; + mbed::Timer _irq_timer; +}; + +} // namespace bluenrg_2 +} // namespace vendor +} // namespace ble + +/** + * Cordio HCI driver factory + */ +ble::CordioHCIDriver &ble_cordio_get_hci_driver() +{ + static ble::vendor::bluenrg_2::TransportDriver transport_driver( + MBED_CONF_BLUENRG_2_SPI_MOSI, + MBED_CONF_BLUENRG_2_SPI_MISO, + MBED_CONF_BLUENRG_2_SPI_SCK, + MBED_CONF_BLUENRG_2_SPI_NCS, + MBED_CONF_BLUENRG_2_SPI_IRQ + ); + static ble::vendor::bluenrg_2::HCIDriver hci_driver( + transport_driver, + MBED_CONF_BLUENRG_2_SPI_RESET + ); + return hci_driver; +} diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/CMakeLists.txt b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/CMakeLists.txt new file mode 100644 index 00000000000..ddb8e6ca282 --- /dev/null +++ b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2020 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +target_sources(mbed-ble-bluenrg2 + INTERFACE + BlueNrg2HCIDriver.cpp +) + +target_link_libraries(mbed-ble-bluenrg2 + INTERFACE + mbed-ble +) diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/README.md b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/README.md new file mode 100644 index 00000000000..2cd955d1029 --- /dev/null +++ b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/README.md @@ -0,0 +1,160 @@ +# BlueNRG_2 + +HCI driver for BlueNRG_2 BLE component + +A simple table like this could help: + +|Module Name|Processor Name|Bluetooth compliance|Status|Used in shields & boards|Link| +|-------------|-----------|-----|-|-|-| +|SPBTLE-RF |BlueNRG-MS (network processor) |v4.1 |Not recommended for new designs |X-NUCLEO-IDB05A1, DISCO-L475VG-IOT01A, DISCO-L562QE | https://www.st.com/en/wireless-transceivers-mcus-and-modules/spbtle-rf.html | +|BlueNRG-M0 |BlueNRG-MS (network processor) |v4.2 |Active (included in ST's Longevity Program) |X-NUCLEO-IDB05A2 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m0.html | +|BlueNRG-M2SP |BlueNRG-2 (application processor) |v5.0 |Active (included in ST's Longevity Program) |X-NUCLEO-BNRG2A1 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m2.html | + + +The library uses ARM Cordio stack. It does not work with the stock firmware that is loaded in the BLUENRG-M2SP BLE module of the X-NUCLEO-BNRG2A1 expansion board. + +In order to use this library with X-NUCLEO-BNRG2A1, you need to update the firmware of the BLUENRG-M2SP BLE module mounted on that expansion. + +To this aim, attach the X-NUCLEO-BNRG2A1 to the STM32 NUCLEO board through the Arduino Connector. You can use the binary provided for both STM32 NUCLEO-F401RE and STM32 NUCLEO-L476RG. + +Download the binaries from the links below: + +[BLE2_StackUpdater_F401RE.bin](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_StackUpdater/BLE2_StackUpdater_F401RE.bin) + +[BLE2_StackUpdater_L476RG.bin](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_StackUpdater/BLE2_StackUpdater_L476RG.bin) + +Before flashing your target board with the respective binary, be careful to properly set the SPI reset on pin **D7** as reported in the picture below: + +![D7_SPI_rst](img/D7_SPI_rst.jpg) + +At the end of the updating procedure, the LED labeled as **LD2** on the STM32 NUCLEO board will be blinking. +You can also check the status of the updating procedure on the serial terminal (baud rate set to 115200). +The picture below shows the log upon success: + +![updLog](img/updLog.jpg) + +If, for some reason, the updating procedure does not succeed you can use a standard ST-Link V2 with 5 jumper wires female-female together with the +[BlueNRG2_FlasherUtility](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_FlasherUtility/en.STSW_BNRGFLASHER.zip) software tool (currently available only for Windows PC). + +You need to connect the J12 pins of the X-NUCLEO-BNRG2A1 to the pins of the ST-Link V2 as shown in the picture below: + +![ST-LINK](img/ST-LINK.jpg) + +In particular, we have the following connections: + +|Pin number on J12|Pin number on ST/Link V2| +|--------|----------| +|1 |1 | +|2 |9 | +|3 |12 | +|4 |7 | +|5 |15 | + +Install the ST BlueNRG-1_2 Flasher Utility and open it, then select the SWD tab: + +![Flasher_1](img/Flasher_1.jpg) + +Erase the flash memory of the BlueNRG-2 chip: + +![Flasher_2](img/Flasher_2.jpg) + +Download the Firmware for the BLE module from the following link: + +[DTM_SPI.hex](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_Firmware/DTM_SPI.hex) + +Load the Firmware in the ST BlueNRG-1_2 Flasher Utility and then press the "Flash" button: + +![Flasher_3](img/Flasher_3.jpg) + +If you should find some issues during the update process, you can try to repeat the procedure closing the J15 jumper on the X-NUCLEO-BNRG2A1 expansion board. + +## Expansion Board + +### X-NUCLEO-BNRG2A1 + +Bluetooth Low Energy Nucleo Expansion Board: + +https://os.mbed.com/components/X-NUCLEO-BNRG2A1/ + +## Driver configuration + +In order to use the BlueNRG-M2SP module together with other targets, +you may need to override default settings in your local `mbed_app.json` file + +### Arduino Connector Compatibility Warning + +Default Arduino connection is using: + +``` + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_nCS": "A1", + "SPI_RESET": "D7", + "SPI_IRQ": "A0", + "SPI_SCK": "D3", + "valid-public-bd-address": false +``` + +X-NUCLEO-BNRG2A1 is Arduino compatible with an exception: instead of using pin **D13** for the SPI clock, pin **D3** is used. +The default configuration for this library is having the SPI clock on pin **D3**. + +You should use the jumper labeled as J14 to change the SPI clock pin configuration. + +#### SPI clock jumper settings + +|Settings|Pin 1|Pin 2|Pin 3| +|--------|-----|-----|-----| +|**D3** |Open |Short|Short| +|**D13** |Short|Short|Open | + +In case you change the default configuration, then you also have to configure this library to use pin **D13** to drive the SPI clock. +To this aim you need to update your local mbed_app.json file with: + +``` + "target_overrides": { + "XXXX": { + "SPI_SCK": "D13" + }, +``` + +If you use pin **D13** for the SPI clock, please be aware that on some STM32 Nucleo boards you may **not** drive the LED, +otherwise you will get a conflict: the LED on STM32 Nucleo boards is connected to pin **D13**. + +To correctly set the SPI reset on pin **D7** resistor R117 should be mounted. +Alternatively, pin **D7** of the Arduino connector (**CN9**) and pin #5 of the jumper labeled as J12 should be bridged. + +### Target Configuration + +To use that library, the target requires some extra configuration in the application `mbed_app.json`. In the `target_overides` section: + +* BlueNRG module has to be enabled + +```json +"target.components_add": ["BlueNRG_2"] +``` + +* BLE feature has to be enabled + +```json +"target.features_add": ["BLE"] +``` + +* Extra labels have to be defined to include the cordio stack and this library: + +```json +"target.extra_labels_add": ["CORDIO"] +``` + +As an example, the target overide section for the `NUCLEO_F401RE` would be: + +```json + "NUCLEO_F401RE": { + "target.components_add": ["BlueNRG_2"], + "target.features_add": ["BLE"] + "target.extra_labels_add": ["CORDIO"] + } +``` + +### Known limitations + +Security does not work with privacy due to pairing failure. \ No newline at end of file diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/D7_SPI_rst.jpg b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/D7_SPI_rst.jpg new file mode 100644 index 00000000000..6aa8e3b2948 Binary files /dev/null and b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/D7_SPI_rst.jpg differ diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_1.jpg b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_1.jpg new file mode 100644 index 00000000000..47d793c6ce1 Binary files /dev/null and b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_1.jpg differ diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_2.jpg b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_2.jpg new file mode 100644 index 00000000000..2ec70416346 Binary files /dev/null and b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_2.jpg differ diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_3.jpg b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_3.jpg new file mode 100644 index 00000000000..1a457344583 Binary files /dev/null and b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/Flasher_3.jpg differ diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/ST-LINK.jpg b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/ST-LINK.jpg new file mode 100644 index 00000000000..21240e1f9f6 Binary files /dev/null and b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/ST-LINK.jpg differ diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/updLog.jpg b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/updLog.jpg new file mode 100644 index 00000000000..9ff3af1f080 Binary files /dev/null and b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/img/updLog.jpg differ diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/mbed_lib.json b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/mbed_lib.json new file mode 100644 index 00000000000..502efc2aa04 --- /dev/null +++ b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_2/mbed_lib.json @@ -0,0 +1,23 @@ +{ + "name": "bluenrg_2", + "config": { + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_nCS": "A1", + "SPI_RESET": "D7", + "SPI_IRQ": "A0", + "SPI_SCK": "D3", + "valid-public-bd-address": { + "help": "Read the BD public address at startup", + "value": false + } + }, + "target_overrides": { + "*": { + "target.macros_add": [ "CORDIO_RPA_SWAP_WORKAROUND=1" ] + }, + "NUCLEO_F446RE": { + "SPI_SCK": "D13" + } + } +} diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/BlueNrgMsHCIDriver.cpp b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/BlueNrgMsHCIDriver.cpp index e978b8379f3..118719e324d 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/BlueNrgMsHCIDriver.cpp +++ b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/BlueNrgMsHCIDriver.cpp @@ -37,9 +37,9 @@ #include "hci_mbed_os_adaptation.h" // rtos -#include "Thread.h" -#include "Semaphore.h" -#include "Mutex.h" +#include "rtos/Thread.h" +#include "rtos/Semaphore.h" +#include "rtos/Mutex.h" #define HCI_RESET_RAND_CNT 4 diff --git a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/README.md b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/README.md index 434a58ad619..abfe3199ba6 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/README.md +++ b/connectivity/drivers/ble/FEATURE_BLE/COMPONENT_BlueNRG_MS/README.md @@ -1,14 +1,14 @@ # BlueNRG_MS -BLE API wrapper Library for BlueNRG (Bluetooth Low Energy) +HCI driver for BlueNRG_MS BLE component -Maybe a simple table like this could help: +A simple table like this could help: -|Name|Type|Bluetooth compliance|Status|Used in shields & boards|Link| -|-----------|----------|-----|-|-|-| -|SPBTLE-RF |Module |v4.1 |Not recommended for new designs |X-NUCLEO-IDB05A1, DISCO-L475VG-IOT01A, DISCO-L562QE | https://www.st.com/en/wireless-transceivers-mcus-and-modules/spbtle-rf.html | -|BlueNRG-M0 |Module |v4.2 |Active (included in ST's Longevity Program) |No | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m0.html | -|BlueNRG-MS |Processor |v4.2 |Active (included in ST's Longevity Program) |X-NUCLEO-IDB05A2 (coming soon) | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-ms.html | +|Module Name|Processor Name|Bluetooth compliance|Status|Used in shields & boards|Link| +|-------------|-----------|-----|-|-|-| +|SPBTLE-RF |BlueNRG-MS (network processor) |v4.1 |Not recommended for new designs |X-NUCLEO-IDB05A1, DISCO-L475VG-IOT01A, DISCO-L562QE | https://www.st.com/en/wireless-transceivers-mcus-and-modules/spbtle-rf.html | +|BlueNRG-M0 |BlueNRG-MS (network processor) |v4.2 |Active (included in ST's Longevity Program) |X-NUCLEO-IDB05A2 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m0.html | +|BlueNRG-M2SP |BlueNRG-2 (application processor) |v5.0 |Active (included in ST's Longevity Program) |X-NUCLEO-BNRG2A1 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m2.html | It uses ARM Cordio stack instead of the ST BlueNRG stack.