1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c
2022-08-24 09:05:25 +02:00

228 lines
6.2 KiB
C

/*
* Copyright (C) 2022 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32_esp_ble_nimble
* @{
*
* @file
* @brief Implementation of the Bluetooth LE Host Controller Interface
*
* ESP32x SoC Bluetooth LE controller uses the uses UART H4 transport protocol.
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include "log.h"
#include "esp_bt.h"
#include "mutex.h"
#include "od.h"
#include "host/ble_hs.h"
#include "nimble/hci_common.h"
#include "nimble/nimble_port.h"
#include "nimble/transport/hci_h4.h"
#include "sysinit/sysinit.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* The size of an HCI command packet is defined as POOL_CMD_SIZE
* in `./nimble/transport/src/transport.c.`. Since there is no defined
* symbol in the header files, we have to define it here. According to
* the BLE specification, controllers must accept HCI command packets
* with up to 255 bytes of data excluding the header. */
#define BLE_HCI_CMD_SIZE (255 + BLE_HCI_CMD_HDR_LEN)
#define BLE_HCI_CMD_HDR_LEN (3)
#define BLE_HCI_EVT_HDR_LEN (2)
#define BLE_VHCI_TIMEOUT_MS 2000
/* Definition of UART H4 packet types */
enum {
BLE_HCI_UART_H4_NONE = 0x00,
BLE_HCI_UART_H4_CMD = 0x01,
BLE_HCI_UART_H4_ACL = 0x02,
BLE_HCI_UART_H4_SCO = 0x03,
BLE_HCI_UART_H4_EVT = 0x04
};
static const char *LOG_TAG = "esp_nimble";
static mutex_t _esp_vhci_semaphore = MUTEX_INIT;
static struct hci_h4_sm _esp_h4sm;
static void _ble_vhci_controller_ready_cb(void)
{
mutex_unlock(&_esp_vhci_semaphore);
}
static int _ble_vhci_packet_received_cb(uint8_t *data, uint16_t len)
{
/* process the HCI H4 formatted packet and call ble_transport_to_hs_* */
len = hci_h4_sm_rx(&_esp_h4sm, data, len);
return 0;
}
static const esp_vhci_host_callback_t vhci_host_cb = {
.notify_host_send_available = _ble_vhci_controller_ready_cb,
.notify_host_recv = _ble_vhci_packet_received_cb,
};
static inline int _ble_transport_to_ll(uint8_t *packet, uint16_t len)
{
uint8_t rc = 0;
/* check whether the controller is ready to accept packets */
if (!esp_vhci_host_check_send_available()) {
LOG_TAG_DEBUG(LOG_TAG, "Controller not ready to accept packets");
}
/* take the semaphore with timeout and send the packet to the controller */
if (ztimer_mutex_lock_timeout(ZTIMER_MSEC, &_esp_vhci_semaphore,
BLE_VHCI_TIMEOUT_MS) == 0) {
esp_vhci_host_send_packet(packet, len);
}
else {
rc = BLE_HS_ETIMEOUT_HCI;
}
return rc;
}
int ble_transport_to_ll_cmd_impl(void *buf)
{
uint16_t len;
uint8_t rc = 0;
uint8_t packet[BLE_HCI_CMD_SIZE + 1];
uint8_t* cmd = buf;
assert(cmd != NULL);
/* Prepare the HCI H4 formatted packet. HCI H4 uses one byte HCI packet
* indicator in front of the HCI command packet. */
len = BLE_HCI_CMD_HDR_LEN + cmd[2] + 1; /* overall length */
packet[0] = BLE_HCI_UART_H4_CMD; /* first byte is the packet indicator */
memcpy(packet + 1, cmd, len - 1);
if (ENABLE_DEBUG && IS_USED(MODULE_OD)) {
printf("CMD host to ctrl:\n");
od_hex_dump(packet + 1, len - 1, OD_WIDTH_DEFAULT);
}
/* send the packet */
rc = _ble_transport_to_ll(packet, len);
/* release the packet buffer */
ble_transport_free(buf);
return rc;
}
int ble_transport_to_ll_acl_impl(struct os_mbuf *om)
{
uint16_t len = 0;
uint8_t rc = 0;
uint8_t packet[MYNEWT_VAL(BLE_TRANSPORT_ACL_SIZE) + 1];
assert(om != NULL);
/* If this packet is zero length, just free it */
if (OS_MBUF_PKTLEN(om) == 0) {
os_mbuf_free_chain(om);
return 0;
}
/* Prepare the HCI H4 formatted packet. HCI H4 uses one byte HCI packet
indicator in front of the HCI command packet. */
packet[0] = BLE_HCI_UART_H4_ACL;
len++;
if (ENABLE_DEBUG && IS_USED(MODULE_OD)) {
printf("ACL host to ctrl:\n");
od_hex_dump(packet + 1,
(om->om_len < 32) ? om->om_len : 32, OD_WIDTH_DEFAULT);
}
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &packet[1]);
len += OS_MBUF_PKTLEN(om);
/* send the packet */
rc = _ble_transport_to_ll(packet, len);
/* release the mbuf */
os_mbuf_free_chain(om);
return rc;
}
static int _esp_hci_h4_frame_cb(uint8_t pkt_type, void *data)
{
int rc = 0;
switch (pkt_type) {
case HCI_H4_ACL:
if (ENABLE_DEBUG && IS_USED(MODULE_OD)) {
printf("ACL ctrl to host:\n");
od_hex_dump((uint8_t *)data, 4, OD_WIDTH_DEFAULT);
}
rc = ble_transport_to_hs_acl(data);
break;
case HCI_H4_EVT:
if (ENABLE_DEBUG && IS_USED(MODULE_OD)) {
printf("EVT ctrl to host:\n");
od_hex_dump((uint8_t *)data, ((uint8_t *)data)[1] + 2, OD_WIDTH_DEFAULT);
}
rc = ble_transport_to_hs_evt(data);
break;
default:
assert(0);
break;
}
return rc;
}
void esp_ble_nimble_init(void)
{
esp_err_t ret;
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
/* TODO: BLE mode only used, the memory for BT Classic could be released
if ((ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);) != ESP_OK) {
LOG_TAG_ERROR(LOG_TAG,
"Bluetooth controller release classic bt memory failed: %s",
esp_err_to_name(ret));
assert(0);
}
*/
/* init and enable the Bluetooth LE controller */
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
LOG_TAG_ERROR(LOG_TAG, "Bluetooth controller initialize failed: %d", ret);
assert(0);
}
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
LOG_TAG_ERROR(LOG_TAG, "Bluetooth controller enable failed: %d", ret);
assert(0);
}
/* register callbacks from Bluetooth LE controller */
if ((ret = esp_vhci_host_register_callback(&vhci_host_cb)) != ESP_OK) {
assert(0);
}
/* init HCI H4 processing */
hci_h4_sm_init(&_esp_h4sm, &hci_h4_allocs_from_ll, _esp_hci_h4_frame_cb);
}