feat(bluetooth/controller): optimize bt hci layer code

This commit is contained in:
zwl 2024-07-04 21:11:23 +08:00 committed by Zhao Wei Liang
parent 580b6fbd3c
commit 6089f84653
21 changed files with 2600 additions and 364 deletions

View File

@ -117,6 +117,7 @@ if(CONFIG_BT_ENABLED)
common/btc/include
common/include
porting/mem/
porting/include
)
list(APPEND include_dirs ${common_include_dirs})
@ -573,30 +574,52 @@ if(CONFIG_BT_ENABLED)
if(CONFIG_BT_LE_CONTROLLER_NPL_OS_PORTING_SUPPORT)
list(APPEND srcs
"porting/npl/freertos/src/npl_os_freertos.c"
"porting/mem/os_msys_init.c"
"porting/transport/src/hci_transport.c"
)
if(CONFIG_BT_CONTROLLER_DISABLED)
list(APPEND srcs
"porting/npl/freertos/src/npl_os_freertos.c"
"porting/mem/os_msys_init.c"
"host/nimble/nimble/porting/nimble/src/hal_uart.c"
)
if(CONFIG_BT_CONTROLLER_DISABLED)
elseif(CONFIG_BT_LE_HCI_INTERFACE_USE_RAM)
if(CONFIG_BT_NIMBLE_ENABLED)
list(APPEND srcs
"host/nimble/nimble/porting/nimble/src/hal_uart.c"
)
endif()
list(APPEND include_dirs
porting/include
porting/npl/freertos/include
porting/transport/include
)
if(CONFIG_BT_LE_HCI_INTERFACE_USE_UART)
"porting/transport/driver/vhci/hci_driver_nimble.c"
"host/nimble/nimble/nimble/transport/esp_ipc/src/hci_esp_ipc.c"
)
else()
list(APPEND srcs
"porting/transport/uart/hci_uart.c"
)
"porting/transport/driver/vhci/hci_driver_standard.c"
)
endif()
elseif(CONFIG_BT_LE_HCI_INTERFACE_USE_UART)
list(APPEND srcs
"porting/transport/driver/common/hci_driver_util.c"
"porting/transport/driver/common/hci_driver_h4.c"
"porting/transport/driver/common/hci_driver_mem.c"
"porting/transport/driver/uart/hci_driver_uart_config.c"
)
if(CONFIG_BT_LE_UART_HCI_DMA_MODE)
list(APPEND srcs
"porting/transport/driver/uart/hci_driver_uart_dma.c"
)
else()
list(APPEND srcs
"porting/transport/driver/uart/hci_driver_uart.c"
)
endif()
endif()
list(APPEND include_dirs
porting/include
porting/npl/freertos/include
porting/transport/include
)
endif()
if(NOT (CONFIG_BT_LE_CRYPTO_STACK_MBEDTLS OR CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS))
list(APPEND include_dirs
porting/ext/tinycrypt/include
@ -621,7 +644,6 @@ if(CONFIG_BT_ENABLED)
if(CONFIG_BT_NIMBLE_ENABLED)
list(APPEND include_dirs
host/nimble/nimble/nimble/host/include
host/nimble/nimble/nimble/include
host/nimble/nimble/nimble/host/services/ans/include
@ -725,10 +747,12 @@ if(CONFIG_BT_ENABLED)
"host/nimble/nimble/porting/npl/freertos/src/nimble_port_freertos.c"
"host/nimble/port/src/nvs_port.c"
)
list(APPEND include_dirs
host/nimble/nimble/porting/nimble/include
host/nimble/port/include
host/nimble/nimble/nimble/transport/include
host/nimble/nimble/nimble/include
)
if(CONFIG_BT_CONTROLLER_DISABLED)
@ -762,6 +786,7 @@ if(CONFIG_BT_ENABLED)
if(CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE AND CONFIG_BT_CONTROLLER_ENABLED)
list(APPEND srcs
"host/nimble/esp-hci/src/esp_nimble_hci.c"
"host/nimble/nimble/nimble/transport/esp_ipc_legacy/src/hci_esp_ipc_legacy.c"
)
list(APPEND include_dirs ${nimble_hci_include_dirs})
endif()

View File

@ -35,10 +35,6 @@
#include "esp_bluedroid_hci.h"
#include "stack/hcimsgs.h"
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
#include "ble_hci_trans.h"
#endif
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
#include "l2c_int.h"
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
@ -622,42 +618,7 @@ static int host_recv_pkt_cb(uint8_t *data, uint16_t len)
return 0;
}
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
int
ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg)
{
if(esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
ble_hci_trans_buf_free(hci_ev);
return 0;
}
uint16_t len = hci_ev[1] + 3;
uint8_t *data = (uint8_t *)malloc(len);
assert(data != NULL);
data[0] = 0x04;
memcpy(&data[1], hci_ev, len - 1);
ble_hci_trans_buf_free(hci_ev);
host_recv_pkt_cb(data, len);
free(data);
return 0;
}
int
ble_hs_rx_data(struct os_mbuf *om, void *arg)
{
uint16_t len = OS_MBUF_PKTHDR(om)->omp_len + 1;
uint8_t *data = (uint8_t *)malloc(len);
assert(data != NULL);
data[0] = 0x02;
os_mbuf_copydata(om, 0, len - 1, &data[1]);
host_recv_pkt_cb(data, len);
free(data);
os_mbuf_free_chain(om);
return 0;
}
#endif
static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb = {
.notify_host_send_available = host_send_pkt_available_cb,
.notify_host_recv = host_recv_pkt_cb,

View File

@ -0,0 +1,356 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* 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 <stdint.h>
#include <string.h>
#include <os/os.h>
#include <os/os_mbuf.h>
#include "common/hci_driver_h4.h"
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#define HCI_H4_SM_W4_PKT_TYPE 0
#define HCI_H4_SM_W4_HEADER 1
#define HCI_H4_SM_W4_PAYLOAD 2
#define HCI_H4_SM_COMPLETED 3
struct hci_h4_input_buffer {
const uint8_t *buf;
uint16_t len;
};
static int
hci_h4_frame_start(struct hci_h4_sm *rxs, uint8_t pkt_type)
{
rxs->pkt_type = pkt_type;
rxs->len = 0;
rxs->exp_len = 0;
switch (rxs->pkt_type) {
case HCI_H4_CMD:
rxs->min_len = 3;
break;
case HCI_H4_ACL:
case HCI_H4_ISO:
rxs->min_len = 4;
break;
case HCI_H4_EVT:
rxs->min_len = 2;
break;
default:
/* !TODO: Sync loss. Need to wait for reset. */
return -1;
}
return 0;
}
static int
hci_h4_ib_consume(struct hci_h4_input_buffer *ib, uint16_t len)
{
assert(ib->len >= len);
ib->buf += len;
ib->len -= len;
return len;
}
static int
hci_h4_ib_pull_min_len(struct hci_h4_sm *rxs,
struct hci_h4_input_buffer *ib)
{
uint16_t len;
len = min(ib->len, rxs->min_len - rxs->len);
memcpy(&rxs->hdr[rxs->len], ib->buf, len);
rxs->len += len;
hci_h4_ib_consume(ib, len);
return rxs->len != rxs->min_len;
}
static int
hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib)
{
int rc;
rc = hci_h4_ib_pull_min_len(h4sm, ib);
if (rc) {
/* need more data */
return 1;
}
switch (h4sm->pkt_type) {
case HCI_H4_CMD:
assert(h4sm->allocs && h4sm->allocs->cmd);
h4sm->buf = h4sm->allocs->cmd();
if (!h4sm->buf) {
return -1;
}
memcpy(h4sm->buf, h4sm->hdr, h4sm->len);
h4sm->exp_len = h4sm->hdr[2] + 3;
break;
case HCI_H4_ACL:
assert(h4sm->allocs && h4sm->allocs->acl);
h4sm->om = h4sm->allocs->acl();
if (!h4sm->om) {
return -1;
}
os_mbuf_append(h4sm->om, h4sm->hdr, h4sm->len);
h4sm->exp_len = get_le16(&h4sm->hdr[2]) + 4;
break;
#if !CONFIG_BT_CONTROLLER_ENABLED
case HCI_H4_EVT:
if (h4sm->hdr[0] == BLE_HCI_EVCODE_LE_META) {
/* For LE Meta event we need 3 bytes to parse header */
h4sm->min_len = 3;
rc = hci_h4_ib_pull_min_len(h4sm, ib);
if (rc) {
/* need more data */
return 1;
}
}
assert(h4sm->allocs && h4sm->allocs->evt);
/* We can drop legacy advertising events if there's no free buffer in
* discardable pool.
*/
if (h4sm->hdr[2] == BLE_HCI_LE_SUBEV_ADV_RPT) {
h4sm->buf = h4sm->allocs->evt(1);
} else {
h4sm->buf = h4sm->allocs->evt(0);
if (!h4sm->buf) {
return -1;
}
}
if (h4sm->buf) {
memcpy(h4sm->buf, h4sm->hdr, h4sm->len);
}
h4sm->exp_len = h4sm->hdr[1] + 2;
break;
#endif // !CONFIG_BT_CONTROLLER_ENABLED
case HCI_H4_ISO:
assert(h4sm->allocs && h4sm->allocs->iso);
h4sm->om = h4sm->allocs->iso();
if (!h4sm->om) {
return -1;
}
os_mbuf_append(h4sm->om, h4sm->hdr, h4sm->len);
h4sm->exp_len = (get_le16(&h4sm->hdr[2]) & 0x7fff) + 4;
break;
default:
assert(0);
break;
}
return 0;
}
static int
hci_h4_sm_w4_payload(struct hci_h4_sm *h4sm,
struct hci_h4_input_buffer *ib)
{
uint16_t mbuf_len;
uint16_t len;
int rc;
len = min(ib->len, h4sm->exp_len - h4sm->len);
switch (h4sm->pkt_type) {
case HCI_H4_CMD:
case HCI_H4_EVT:
if (h4sm->buf) {
memcpy(&h4sm->buf[h4sm->len], ib->buf, len);
}
break;
case HCI_H4_ACL:
case HCI_H4_ISO:
assert(h4sm->om);
mbuf_len = OS_MBUF_PKTLEN(h4sm->om);
rc = os_mbuf_append(h4sm->om, ib->buf, len);
if (rc) {
/* Some data may already be appended so need to adjust h4sm only by
* the size of appended data.
*/
len = OS_MBUF_PKTLEN(h4sm->om) - mbuf_len;
h4sm->len += len;
hci_h4_ib_consume(ib, len);
return -1;
}
break;
default:
assert(0);
break;
}
h4sm->len += len;
hci_h4_ib_consume(ib, len);
/* return 1 if need more data */
return h4sm->len != h4sm->exp_len;
}
static void
hci_h4_sm_completed(struct hci_h4_sm *h4sm)
{
int rc;
switch (h4sm->pkt_type) {
#if CONFIG_BT_CONTROLLER_ENABLED
case HCI_H4_CMD:
if (h4sm->buf) {
assert(h4sm->frame_cb);
rc = h4sm->frame_cb(h4sm->pkt_type, h4sm->buf);
assert(rc == 0);
h4sm->buf = NULL;
}
break;
case HCI_H4_ACL:
case HCI_H4_ISO:
if (h4sm->om) {
assert(h4sm->frame_cb);
rc = h4sm->frame_cb(h4sm->pkt_type, h4sm->om);
assert(rc == 0);
h4sm->om = NULL;
}
break;
#else
case HCI_H4_CMD:
case HCI_H4_EVT:
if (h4sm->buf) {
assert(h4sm->frame_cb);
rc = h4sm->frame_cb(h4sm->pkt_type, h4sm->buf);
if (rc != 0) {
ble_transport_free(h4sm->buf);
}
h4sm->buf = NULL;
}
break;
case HCI_H4_ACL:
case HCI_H4_ISO:
if (h4sm->om) {
assert(h4sm->frame_cb);
rc = h4sm->frame_cb(h4sm->pkt_type, h4sm->om);
if (rc != 0) {
os_mbuf_free_chain(h4sm->om);
}
h4sm->om = NULL;
}
break;
#endif // CONFIG_BT_CONTROLLER_ENABLED
default:
assert(0);
break;
}
}
int
hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len)
{
struct hci_h4_input_buffer ib = {
.buf = buf,
.len = len,
};
int rc = 0;
while (ib.len && (rc >= 0)) {
rc = 0;
switch (h4sm->state) {
case HCI_H4_SM_W4_PKT_TYPE:
if (hci_h4_frame_start(h4sm, ib.buf[0]) < 0) {
return -1;
}
hci_h4_ib_consume(&ib, 1);
h4sm->state = HCI_H4_SM_W4_HEADER;
/* no break */
case HCI_H4_SM_W4_HEADER:
rc = hci_h4_sm_w4_header(h4sm, &ib);
assert(rc >= 0);
if (rc) {
break;
}
h4sm->state = HCI_H4_SM_W4_PAYLOAD;
/* no break */
case HCI_H4_SM_W4_PAYLOAD:
rc = hci_h4_sm_w4_payload(h4sm, &ib);
assert(rc >= 0);
if (rc) {
break;
}
h4sm->state = HCI_H4_SM_COMPLETED;
/* no break */
case HCI_H4_SM_COMPLETED:
hci_h4_sm_completed(h4sm);
h4sm->state = HCI_H4_SM_W4_PKT_TYPE;
break;
default:
return -1;
}
}
/* Calculate consumed bytes
*
* Note: we should always consume some bytes unless there is an oom error.
* It's also possible that we have an oom error but already consumed some
* data, in such case just return success and error will be returned on next
* pass.
*/
len = len - ib.len;
if (len == 0) {
assert(rc < 0);
return -1;
}
return len;
}
void
hci_h4_sm_init(struct hci_h4_sm *h4sm, const struct hci_h4_allocators *allocs,
hci_h4_frame_cb *frame_cb)
{
memset(h4sm, 0, sizeof(*h4sm));
h4sm->allocs = allocs;
h4sm->frame_cb = frame_cb;
}

View File

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "common/hci_driver_mem.h"
#include "common/hci_driver_h4.h"
#include "esp_hci_internal.h"
void *
hci_driver_mem_cmd_alloc(void)
{
return r_ble_hci_trans_buf_alloc(ESP_HCI_INTERNAL_BUF_CMD);
}
void *
hci_driver_mem_evt_alloc(int discardable)
{
/* The controller shouldn't invoke this. */
assert(0);
return NULL;
}
struct os_mbuf *
hci_driver_mem_acl_alloc(void)
{
return os_msys_get_pkthdr(0, ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE);
}
struct os_mbuf *
hci_driver_mem_acl_len_alloc(uint32_t len)
{
return os_msys_get_pkthdr(len, ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE);
}
struct os_mbuf *
hci_driver_mem_iso_alloc(void)
{
return os_msys_get_pkthdr(0, ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE);
}
struct os_mbuf *
hci_driver_mem_iso_len_alloc(uint32_t len)
{
return os_msys_get_pkthdr(len, ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE);
}
const struct hci_h4_allocators s_hci_driver_mem_alloc = {
.cmd = hci_driver_mem_cmd_alloc,
.evt = hci_driver_mem_evt_alloc,
.acl = hci_driver_mem_acl_alloc,
.iso = hci_driver_mem_iso_alloc,
};

View File

@ -0,0 +1,225 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "os/os.h"
#include "os/os_mempool.h"
#include "esp_hci_driver.h"
#include "esp_hci_internal.h"
#include "common/hci_driver_util.h"
#define TAG "HCI_UTIL"
#define HCI_DRIVER_UTIL_TX_POOL_NUM \
(CONFIG_BT_LE_ACL_BUF_COUNT + CONFIG_BT_LE_HCI_EVT_HI_BUF_COUNT + CONFIG_BT_LE_HCI_EVT_LO_BUF_COUNT)
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/**
* @brief Structure representing HCI TX data.
*/
typedef struct hci_driver_util_tx_entry {
hci_driver_data_type_t data_type; ///< Type of the HCI TX data.
uint8_t *data; ///< Pointer to the TX data.
uint32_t length; ///< Length of the TX data.
STAILQ_ENTRY(hci_driver_util_tx_entry) next; ///< Next element in the linked list.
} hci_driver_util_tx_entry_t;
/* The list for hci_driver_util_tx_entry */
STAILQ_HEAD(hci_driver_util_tx_list, hci_driver_util_tx_entry);
typedef struct {
struct hci_driver_util_tx_list tx_head;
struct hci_driver_util_tx_entry *cur_tx_entry;
uint32_t cur_tx_off;
struct os_mempool *tx_entry_pool;
uint8_t *tx_entry_mem;
} hci_driver_util_env_t;
static hci_driver_util_env_t s_hci_driver_util_env;
static void
hci_driver_util_memory_deinit(void)
{
if (s_hci_driver_util_env.tx_entry_pool) {
free(s_hci_driver_util_env.tx_entry_pool);
s_hci_driver_util_env.tx_entry_pool = NULL;
}
if (s_hci_driver_util_env.tx_entry_mem) {
free(s_hci_driver_util_env.tx_entry_mem);
s_hci_driver_util_env.tx_entry_mem = NULL;
}
}
static int
hci_driver_util_memory_init(void)
{
int rc;
s_hci_driver_util_env.tx_entry_pool = (struct os_mempool *)malloc(sizeof(struct os_mempool));
if (!s_hci_driver_util_env.tx_entry_pool) {
ESP_LOGE(TAG, "No memory for tx pool");
goto init_err;
}
s_hci_driver_util_env.tx_entry_mem = malloc(OS_MEMPOOL_SIZE(HCI_DRIVER_UTIL_TX_POOL_NUM,
sizeof(hci_driver_util_tx_entry_t)) * sizeof(os_membuf_t));
if (!s_hci_driver_util_env.tx_entry_mem) {
ESP_LOGE(TAG, "No memory for tx pool buffer");
goto init_err;
}
rc = os_mempool_init(s_hci_driver_util_env.tx_entry_pool, HCI_DRIVER_UTIL_TX_POOL_NUM,
sizeof(hci_driver_util_tx_entry_t), s_hci_driver_util_env.tx_entry_mem,
"hci_tx_entry_pool");
if (rc) {
ESP_LOGE(TAG, "Failed to initialize tx pool");
goto init_err;
}
return 0;
init_err:
hci_driver_util_memory_deinit();
return -1;
}
void
hci_driver_util_tx_list_enqueue(hci_driver_data_type_t type, uint8_t *data, uint32_t len)
{
os_sr_t sr;
hci_driver_util_tx_entry_t *tx_entry;
tx_entry = os_memblock_get(s_hci_driver_util_env.tx_entry_pool);
assert(tx_entry != NULL);
tx_entry->data_type = type;
tx_entry->data = data;
tx_entry->length = len;
/* If the txbuf is command status event or command complete event, we should send firstly.
* The tx list maybe used in the controller task and hci task. Therefore, enter critical area.
*/
if ((type == HCI_DRIVER_TYPE_EVT) && ((data[0] == 0x0E) || (data[0] == 0x0F))) {
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_HEAD(&s_hci_driver_util_env.tx_head, tx_entry, next);
OS_EXIT_CRITICAL(sr);
} else {
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_TAIL(&s_hci_driver_util_env.tx_head, tx_entry, next);
OS_EXIT_CRITICAL(sr);
}
}
uint32_t
hci_driver_util_tx_list_dequeue(uint32_t max_tx_len, void **tx_data, bool *last_frame)
{
os_sr_t sr;
uint32_t tx_len;
uint32_t data_len;
uint16_t out_off;
struct os_mbuf *om;
hci_driver_util_tx_entry_t *tx_entry;
/* Check if there is any remaining data that hasn't been sent completely. If it has been completed,
* free the corresponding memory. Therefore, the HCI TX entry needs to be sent one by one; multiple
* entries cannot be sent together.
*/
tx_len = 0;
tx_entry = s_hci_driver_util_env.cur_tx_entry;
if (tx_entry) {
data_len = tx_entry->length;
if (tx_entry->data_type == HCI_DRIVER_TYPE_ACL) {
om = (struct os_mbuf *)tx_entry->data;
if (s_hci_driver_util_env.cur_tx_off >= data_len) {
os_mbuf_free_chain(om);
} else {
om = os_mbuf_off(om, s_hci_driver_util_env.cur_tx_off, &out_off);
tx_len = min(max_tx_len, om->om_len - out_off);
*tx_data = (void *)&om->om_data[out_off];
}
} else if (tx_entry->data_type == HCI_DRIVER_TYPE_EVT) {
if (s_hci_driver_util_env.cur_tx_off >= data_len) {
r_ble_hci_trans_buf_free(tx_entry->data);
} else {
tx_len = min(max_tx_len, data_len - s_hci_driver_util_env.cur_tx_off);
*tx_data = &tx_entry->data[s_hci_driver_util_env.cur_tx_off];
}
} else {
assert(0);
}
/* If this is the last frame, inform the invoker not to call this API until the current data
* has been completely sent.
*/
if (tx_len) {
s_hci_driver_util_env.cur_tx_off += tx_len;
if (s_hci_driver_util_env.cur_tx_off >= data_len) {
*last_frame = true;
} else {
*last_frame = false;
}
} else {
os_memblock_put(s_hci_driver_util_env.tx_entry_pool, (void *)tx_entry);
s_hci_driver_util_env.cur_tx_entry = NULL;
}
}
/* Find a new entry. */
if (!tx_len && !STAILQ_EMPTY(&s_hci_driver_util_env.tx_head)) {
OS_ENTER_CRITICAL(sr);
tx_entry = STAILQ_FIRST(&s_hci_driver_util_env.tx_head);
STAILQ_REMOVE_HEAD(&s_hci_driver_util_env.tx_head, next);
OS_EXIT_CRITICAL(sr);
*tx_data = &tx_entry->data_type;
s_hci_driver_util_env.cur_tx_entry = tx_entry;
s_hci_driver_util_env.cur_tx_off = 0;
tx_len = 1;
*last_frame = false;
}
return tx_len;
}
int
hci_driver_util_init(void)
{
memset(&s_hci_driver_util_env, 0, sizeof(hci_driver_util_env_t));
if (hci_driver_util_memory_init()) {
return -1;
}
STAILQ_INIT(&s_hci_driver_util_env.tx_head);
return 0;
}
void
hci_driver_util_deinit(void)
{
hci_driver_util_tx_entry_t *tx_entry;
hci_driver_util_tx_entry_t *next_entry;
/* Free all of controller buffers which haven't been sent yet. The whole mempool will be freed.
* Therefore, it's unnecessary to put the tx_entry into mempool.
*/
tx_entry = STAILQ_FIRST(&s_hci_driver_util_env.tx_head);
while (tx_entry) {
next_entry = STAILQ_NEXT(tx_entry, next);
if (tx_entry->data_type == HCI_DRIVER_TYPE_ACL) {
os_mbuf_free_chain((struct os_mbuf *)tx_entry->data);
} else if (tx_entry->data_type == HCI_DRIVER_TYPE_EVT) {
r_ble_hci_trans_buf_free(tx_entry->data);
}
tx_entry = next_entry;
}
hci_driver_util_memory_deinit();
memset(&s_hci_driver_util_env, 0, sizeof(hci_driver_util_env_t));
}

View File

@ -0,0 +1,222 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "esp_hci_transport.h"
#include "esp_hci_internal.h"
#include "common/hci_driver_h4.h"
#include "common/hci_driver_util.h"
#include "common/hci_driver_mem.h"
#include "hci_driver_uart.h"
static const char *TAG = "hci_uart";
#define CONFIG_BT_LE_HCI_RX_PROC_DATA_LEN (256)
typedef struct {
TaskHandle_t tx_task_handler;
TaskHandle_t rx_task_handler;
hci_driver_uart_params_config_t *hci_uart_params;
SemaphoreHandle_t tx_sem;
QueueHandle_t rx_event_queue;
uint8_t *rx_data;
struct hci_h4_sm *h4_sm;
hci_driver_forward_fn *forward_cb;
} hci_driver_uart_env_t;
static hci_driver_uart_env_t s_hci_driver_uart_env;
static struct hci_h4_sm s_hci_driver_uart_h4_sm;
static uint8_t s_hci_driver_uart_rx_data[CONFIG_BT_LE_HCI_RX_PROC_DATA_LEN];
static hci_driver_uart_params_config_t hci_driver_uart_params = BT_HCI_DRIVER_UART_CONFIG_DEFAULT();
static int
hci_driver_uart_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir)
{
/* By now, this layer is only used by controller. */
assert(dir == HCI_DRIVER_DIR_C2H);
ESP_LOGD(TAG, "controller tx len:%d\n", length);
hci_driver_util_tx_list_enqueue(data_type, data, length);
xSemaphoreGive(s_hci_driver_uart_env.tx_sem);
return 0;
}
static int
hci_driver_uart_h4_frame_cb(uint8_t pkt_type, void *data)
{
hci_driver_forward_fn *forward_cb;
forward_cb = s_hci_driver_uart_env.forward_cb;
if (!forward_cb) {
return -1;
}
ESP_LOGD(TAG, "h4 frame\n");
return forward_cb(pkt_type, data, 0, HCI_DRIVER_DIR_H2C);
}
static void
hci_driver_uart_tx_task(void *p)
{
void *data;
bool last_frame;
uint32_t tx_len;
uart_port_t port;
port = s_hci_driver_uart_env.hci_uart_params->hci_uart_port;
while (true) {
xSemaphoreTake(s_hci_driver_uart_env.tx_sem, portMAX_DELAY);
while (true) {
tx_len = hci_driver_util_tx_list_dequeue(0xffffff, &data, &last_frame);
if (tx_len == 0) {
break;
}
ESP_LOGD(TAG, "uart tx");
ESP_LOG_BUFFER_HEXDUMP(TAG, data, tx_len, ESP_LOG_DEBUG);
uart_write_bytes(port, data, tx_len);
}
}
}
static void
hci_driver_uart_rx_task(void *p)
{
void *data;
int read_len;
int ret;
uart_port_t port;
uart_event_t uart_event;
port = s_hci_driver_uart_env.hci_uart_params->hci_uart_port;
while (true) {
xQueueReceive(s_hci_driver_uart_env.rx_event_queue, &uart_event, portMAX_DELAY);
data = s_hci_driver_uart_env.rx_data;
while (true) {
read_len = uart_read_bytes(port, data, CONFIG_BT_LE_HCI_RX_PROC_DATA_LEN, 0);
if (read_len == 0) {
break;
}
ESP_LOGD(TAG, "uart rx");
ESP_LOG_BUFFER_HEXDUMP(TAG, data, read_len, ESP_LOG_DEBUG);
ret = hci_h4_sm_rx(s_hci_driver_uart_env.h4_sm, data, read_len);
if (ret < 0) {
r_ble_ll_hci_ev_hw_err(ESP_HCI_SYNC_LOSS_ERR);
}
}
}
}
static int
hci_driver_uart_task_create(void)
{
/* !TODO: Set the core id by menuconfig */
xTaskCreatePinnedToCore(hci_driver_uart_tx_task, "hci_driver_uart_tx_task",
CONFIG_BT_LE_HCI_TRANS_TASK_STACK_SIZE, NULL,
ESP_TASK_BT_CONTROLLER_PRIO, &s_hci_driver_uart_env.tx_task_handler,
0);
assert(s_hci_driver_uart_env.tx_task_handler);
xTaskCreatePinnedToCore(hci_driver_uart_rx_task, "hci_driver_uart_rx_task",
CONFIG_BT_LE_HCI_TRANS_TASK_STACK_SIZE, NULL,
ESP_TASK_BT_CONTROLLER_PRIO, &s_hci_driver_uart_env.rx_task_handler,
0);
assert(s_hci_driver_uart_env.rx_task_handler);
ESP_LOGI(TAG, "hci transport task create successfully, prio:%d, stack size: %ld",
ESP_TASK_BT_CONTROLLER_PRIO, CONFIG_BT_LE_HCI_TRANS_TASK_STACK_SIZE);
return 0;
}
static void
hci_driver_uart_deinit(void)
{
if (s_hci_driver_uart_env.tx_task_handler) {
vTaskDelete(s_hci_driver_uart_env.tx_task_handler);
s_hci_driver_uart_env.tx_task_handler = NULL;
}
if (s_hci_driver_uart_env.rx_task_handler) {
vTaskDelete(s_hci_driver_uart_env.rx_task_handler);
s_hci_driver_uart_env.rx_task_handler = NULL;
}
ESP_ERROR_CHECK(uart_driver_delete(s_hci_driver_uart_env.hci_uart_params->hci_uart_port));
if (!s_hci_driver_uart_env.tx_sem) {
vSemaphoreDelete(s_hci_driver_uart_env.tx_sem);
}
hci_driver_util_deinit();
memset(&s_hci_driver_uart_env, 0, sizeof(hci_driver_uart_env_t));
}
static int
hci_driver_uart_init(hci_driver_forward_fn *cb)
{
int rc;
memset(&s_hci_driver_uart_env, 0, sizeof(hci_driver_uart_env_t));
s_hci_driver_uart_env.h4_sm = &s_hci_driver_uart_h4_sm;
hci_h4_sm_init(s_hci_driver_uart_env.h4_sm, &s_hci_driver_mem_alloc, hci_driver_uart_h4_frame_cb);
rc = hci_driver_util_init();
if (rc) {
goto error;
}
s_hci_driver_uart_env.tx_sem = xSemaphoreCreateBinary();
if (!s_hci_driver_uart_env.tx_sem) {
goto error;
}
s_hci_driver_uart_env.rx_data = s_hci_driver_uart_rx_data;
s_hci_driver_uart_env.forward_cb = cb;
s_hci_driver_uart_env.hci_uart_params = &hci_driver_uart_params;
hci_driver_uart_config(&hci_driver_uart_params);
/* Currently, the queue size is set to 1. It will be considered as semaphore. */
ESP_ERROR_CHECK(uart_driver_install(s_hci_driver_uart_env.hci_uart_params->hci_uart_port,
CONFIG_BT_LE_HCI_UART_RX_BUFFER_SIZE,
CONFIG_BT_LE_HCI_UART_TX_BUFFER_SIZE,
1, &s_hci_driver_uart_env.rx_event_queue,
0));
rc = hci_driver_uart_task_create();
if (rc) {
goto error;
}
return 0;
error:
hci_driver_uart_deinit();
return rc;
}
int
hci_driver_uart_reconfig_pin(int tx_pin, int rx_pin, int cts_pin, int rts_pin)
{
hci_driver_uart_params_config_t *uart_param = s_hci_driver_uart_env.hci_uart_params;
uart_param->hci_uart_tx_pin = tx_pin;
uart_param->hci_uart_rx_pin = rx_pin;
uart_param->hci_uart_rts_pin = rts_pin;
uart_param->hci_uart_cts_pin = cts_pin;
return hci_driver_uart_config(uart_param);
}
hci_driver_ops_t hci_driver_uart_ops = {
.hci_driver_tx = hci_driver_uart_tx,
.hci_driver_init = hci_driver_uart_init,
.hci_driver_deinit = hci_driver_uart_deinit,
};

View File

@ -0,0 +1,97 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include "driver/uart.h"
#include "os/os_mbuf.h"
#include "esp_bt.h"
#include "esp_hci_transport.h"
/**
* @brief UART configuration parameters for the HCI driver
*/
typedef struct hci_driver_uart_params_config
{
uint8_t hci_uart_port; /*!< Port of UART for HCI */
uint8_t hci_uart_data_bits; /*!< Data bits of UART for HCI */
uint8_t hci_uart_stop_bits; /*!< Stop bits of UART for HCI */
uint8_t hci_uart_flow_ctrl; /*!< Flow control of UART for HCI */
uint8_t hci_uart_parity; /*!< UART parity */
uint8_t hci_uart_driver_mode; /*!< UART driver mode */
uint32_t hci_uart_baud; /*!< Baudrate of UART for HCI */
int hci_uart_tx_pin; /*!< Tx Pin number of UART for HCI */
int hci_uart_rx_pin; /*!< Rx Pin number of UART for HCI */
int hci_uart_rts_pin; /*!< RTS Pin number of UART for HCI */
int hci_uart_cts_pin; /*!< CTS Pin number of UART for HCI */
} hci_driver_uart_params_config_t;
#define BT_HCI_DRIVER_UART_CONFIG_DEFAULT() { \
.hci_uart_port = DEFAULT_BT_LE_HCI_UART_PORT, \
.hci_uart_baud = DEFAULT_BT_LE_HCI_UART_BAUD, \
.hci_uart_tx_pin = DEFAULT_BT_LE_HCI_UART_TX_PIN , \
.hci_uart_rx_pin = DEFAULT_BT_LE_HCI_UART_RX_PIN, \
.hci_uart_cts_pin = DEFAULT_BT_LE_HCI_UART_CTS_PIN, \
.hci_uart_rts_pin = DEFAULT_BT_LE_HCI_UART_RTS_PIN, \
.hci_uart_data_bits = DEFAULT_BT_LE_HCI_UART_DATA_BITS, \
.hci_uart_stop_bits = DEFAULT_BT_LE_HCI_UART_STOP_BITS, \
.hci_uart_flow_ctrl = DEFAULT_BT_LE_HCI_UART_FLOW_CTRL, \
.hci_uart_parity = DEFAULT_BT_LE_HCI_UART_PARITY, \
}
/**
* @brief Configures the HCI driver UART parameters.
* This function sets up the UART interface according to the specified configuration parameters.
*
* @param uart_config A pointer to a structure containing the UART configuration parameters.
* The structure should include details such as baud rate, parity, stop bits, and flow control.
* Ensure that the uart_config structure is correctly initialized before calling this function.
*
* @return int Returns 0 on success, or a non-zero error code on failure.
*
* @note This function should be called before any UART communication is initiated.
*/
int hci_driver_uart_config(hci_driver_uart_params_config_t *uart_config);
#if CONFIG_BT_LE_UART_HCI_DMA_MODE
/**
* @brief Reconfigure the UART pins for the HCI driver.
*
* This function changes the UART pin configuration for the HCI driver.
*
* @param tx_pin The pin number for the UART TX (transmit) line.
* @param rx_pin The pin number for the UART RX (receive) line.
* @param cts_pin The pin number for the UART CTS (clear to send) line.
* @param rts_pin The pin number for the UART RTS (request to send) line.
*
* @return int Returns 0 on success, or a negative error code on failure.
*/
int hci_driver_uart_dma_reconfig_pin(int tx_pin, int rx_pin, int cts_pin, int rts_pin);
#define hci_uart_reconfig_pin hci_driver_uart_dma_reconfig_pin
#else
/**
* @brief Reconfigure the UART pins for the HCI driver.
*
* This function changes the UART pin configuration for the HCI driver.
*
* @param tx_pin The pin number for the UART TX (transmit) line.
* @param rx_pin The pin number for the UART RX (receive) line.
* @param cts_pin The pin number for the UART CTS (clear to send) line.
* @param rts_pin The pin number for the UART RTS (request to send) line.
*
* @return int Returns 0 on success, or a negative error code on failure.
*/
int hci_driver_uart_reconfig_pin(int tx_pin, int rx_pin, int cts_pin, int rts_pin);
#define hci_uart_reconfig_pin hci_driver_uart_reconfig_pin
#endif // CONFIG_BT_LE_UART_HCI_DMA_MODE
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "driver/uart.h"
#include "hci_driver_uart.h"
static const char *TAG = "hci_uart_config";
static uart_config_t s_uart_cfg;
int hci_driver_uart_config(hci_driver_uart_params_config_t *uart_config)
{
uart_config_t *uart_cfg;
uart_cfg = &s_uart_cfg;
uart_cfg->baud_rate = uart_config->hci_uart_baud;
uart_cfg->data_bits = uart_config->hci_uart_data_bits;
uart_cfg->stop_bits = uart_config->hci_uart_stop_bits;
uart_cfg->parity = uart_config->hci_uart_parity;
uart_cfg->flow_ctrl = uart_config->hci_uart_flow_ctrl;
uart_cfg->source_clk= UART_SCLK_DEFAULT;
uart_cfg->rx_flow_ctrl_thresh = UART_HW_FIFO_LEN(uart_config->hci_uart_port) - 1;
ESP_LOGI(TAG,"set uart pin tx:%d, rx:%d.\n", uart_config->hci_uart_tx_pin, uart_config->hci_uart_rx_pin);
ESP_LOGI(TAG,"set rts:%d, cts:%d.\n", uart_config->hci_uart_rts_pin, uart_config->hci_uart_cts_pin);
ESP_LOGI(TAG,"set baud_rate:%d.\n", uart_config->hci_uart_baud);
ESP_LOGI(TAG,"set flow_ctrl:%d.\n", uart_config->hci_uart_flow_ctrl);
ESP_ERROR_CHECK(uart_driver_delete(uart_config->hci_uart_port));
ESP_ERROR_CHECK(uart_param_config(uart_config->hci_uart_port, uart_cfg));
ESP_ERROR_CHECK(uart_set_pin(uart_config->hci_uart_port, uart_config->hci_uart_tx_pin, uart_config->hci_uart_rx_pin,
uart_config->hci_uart_rts_pin, uart_config->hci_uart_cts_pin));
return 0;
}

View File

@ -0,0 +1,670 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "esp_hci_transport.h"
#include "esp_hci_internal.h"
#include "common/hci_driver_h4.h"
#include "common/hci_driver_util.h"
#include "common/hci_driver_mem.h"
#include "hci_driver_uart.h"
#include "ble_hci_trans.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/gdma.h"
#include "hal/uhci_ll.h"
/*
* UART DMA Desc struct
*
* --------------------------------------------------------------
* | own | EoF | sub_sof | 5'b0 | length [11:0] | size [11:0] |
* --------------------------------------------------------------
* | buf_ptr [31:0] |
* --------------------------------------------------------------
* | next_desc_ptr [31:0] |
* --------------------------------------------------------------
*/
/* this bitfield is start from the LSB!!! */
typedef struct uhci_lldesc_s {
volatile uint32_t size : 12,
length: 12,
offset: 5, /* h/w reserved 5bit, s/w use it as offset in buffer */
sosf : 1, /* start of sub-frame */
eof : 1, /* end of frame */
owner : 1; /* hw or sw */
volatile const uint8_t *buf; /* point to buffer data */
union {
volatile uint32_t empty;
STAILQ_ENTRY(uhci_lldesc_s) qe; /* pointing to the next desc */
};
} uhci_lldesc_t;
/**
* @brief Enumeration of HCI transport transmission states.
*/
typedef enum {
HCI_TRANS_TX_IDLE, ///< HCI Transport TX is in idle state.
HCI_TRANS_TX_START, ///< HCI Transport TX is starting transmission.
HCI_TRANS_TX_END, ///< HCI Transport TX has completed transmission.
} hci_trans_tx_state_t;
typedef struct {
TaskHandle_t task_handler;
hci_driver_uart_params_config_t *hci_uart_params;
SemaphoreHandle_t process_sem;
struct hci_h4_sm *h4_sm;
hci_driver_forward_fn *forward_cb;
struct os_mempool *hci_rx_data_pool; /*!< Init a memory pool for rx_data cache */
uint8_t *hci_rx_data_buffer;
struct os_mempool *hci_rxinfo_pool; /*!< Init a memory pool for rxinfo cache */
os_membuf_t *hci_rxinfo_buffer;
volatile bool rxinfo_mem_exhausted; /*!< Indicate rxinfo memory does not exist */
volatile bool is_continue_rx; /*!< Continue to rx */
volatile hci_trans_tx_state_t hci_tx_state; /*!< HCI Tx State */
struct os_mempool lldesc_mem_pool;/*!< Init a memory pool for uhci_lldesc_t */
uhci_lldesc_t *lldesc_mem;
} hci_driver_uart_dma_env_t;
#define ESP_BT_HCI_TL_STATUS_OK (0) /*!< HCI_TL Tx/Rx operation status OK */
/* The number of lldescs pool */
#define HCI_LLDESCS_POOL_NUM (CONFIG_BT_LE_HCI_LLDESCS_POOL_NUM)
/* Default block size for HCI RX data */
#define HCI_RX_DATA_BLOCK_SIZE (DEFAULT_BT_LE_ACL_BUF_SIZE + BLE_HCI_TRANS_CMD_SZ)
#define HCI_RX_DATA_POOL_NUM (CONFIG_BT_LE_HCI_TRANS_RX_MEM_NUM)
#define HCI_RX_INFO_POOL_NUM (CONFIG_BT_LE_HCI_TRANS_RX_MEM_NUM + 1)
/**
* @brief callback function for HCI Transport Layer send/receive operations
*/
typedef void (* esp_bt_hci_tl_callback_t) (void *arg, uint8_t status);
struct uart_txrxchannel {
esp_bt_hci_tl_callback_t callback;
void *arg;
uhci_lldesc_t *link_head;
};
struct uart_env_tag {
struct uart_txrxchannel tx;
struct uart_txrxchannel rx;
};
typedef struct hci_message {
void *ptr; ///< Pointer to the message data.
uint32_t length; ///< Length of the message data.
STAILQ_ENTRY(hci_message) next; ///< Next element in the linked list.
} hci_message_t;
static void hci_driver_uart_dma_recv_async(uint8_t *buf, uint32_t size, esp_bt_hci_tl_callback_t callback, void *arg);
int hci_driver_uart_dma_rx_start(uint8_t *rx_data, uint32_t length);
int hci_driver_uart_dma_tx_start(esp_bt_hci_tl_callback_t callback, void *arg);
static const char *TAG = "uart_dma";
static hci_driver_uart_dma_env_t s_hci_driver_uart_dma_env;
static struct hci_h4_sm s_hci_driver_uart_h4_sm;
static hci_driver_uart_params_config_t hci_driver_uart_dma_params = BT_HCI_DRIVER_UART_CONFIG_DEFAULT();
/* The list for hci_rx_data */
STAILQ_HEAD(g_hci_rxinfo_list, hci_message);
DRAM_ATTR struct g_hci_rxinfo_list g_hci_rxinfo_head;
static DRAM_ATTR struct uart_env_tag uart_env;
static volatile uhci_dev_t *s_uhci_hw = &UHCI0;
static DRAM_ATTR gdma_channel_handle_t s_rx_channel;
static DRAM_ATTR gdma_channel_handle_t s_tx_channel;
static int hci_driver_uart_dma_memory_deinit(void)
{
if (s_hci_driver_uart_dma_env.hci_rxinfo_buffer) {
free(s_hci_driver_uart_dma_env.hci_rxinfo_buffer);
s_hci_driver_uart_dma_env.hci_rxinfo_buffer = NULL;
}
if (s_hci_driver_uart_dma_env.hci_rxinfo_pool) {
free(s_hci_driver_uart_dma_env.hci_rxinfo_pool);
s_hci_driver_uart_dma_env.hci_rxinfo_pool = NULL;
}
if (s_hci_driver_uart_dma_env.hci_rx_data_buffer) {
free(s_hci_driver_uart_dma_env.hci_rx_data_buffer);
s_hci_driver_uart_dma_env.hci_rx_data_buffer = NULL;
}
if (s_hci_driver_uart_dma_env.hci_rx_data_pool) {
free(s_hci_driver_uart_dma_env.hci_rx_data_pool);
s_hci_driver_uart_dma_env.hci_rx_data_pool = NULL;
}
if (s_hci_driver_uart_dma_env.lldesc_mem) {
free(s_hci_driver_uart_dma_env.lldesc_mem);
s_hci_driver_uart_dma_env.lldesc_mem = NULL;
}
return 0;
}
static int hci_driver_uart_dma_memory_init(void)
{
int rc = 0;
s_hci_driver_uart_dma_env.lldesc_mem = malloc(OS_MEMPOOL_SIZE(HCI_LLDESCS_POOL_NUM,
sizeof (uhci_lldesc_t)) * sizeof(os_membuf_t));
if (!s_hci_driver_uart_dma_env.lldesc_mem) {
return -1;
}
rc = os_mempool_init(&s_hci_driver_uart_dma_env.lldesc_mem_pool, HCI_LLDESCS_POOL_NUM,
sizeof (uhci_lldesc_t), s_hci_driver_uart_dma_env.lldesc_mem, "hci_lldesc_pool");
if (rc) {
goto init_err;
}
s_hci_driver_uart_dma_env.hci_rx_data_pool = (struct os_mempool *)malloc(sizeof(struct os_mempool));
if (!s_hci_driver_uart_dma_env.hci_rx_data_pool) {
goto init_err;
}
memset(s_hci_driver_uart_dma_env.hci_rx_data_pool, 0, sizeof(struct os_mempool));
s_hci_driver_uart_dma_env.hci_rx_data_buffer = malloc(OS_MEMPOOL_SIZE(HCI_RX_DATA_POOL_NUM,
HCI_RX_DATA_BLOCK_SIZE) * sizeof(os_membuf_t));
if (!s_hci_driver_uart_dma_env.hci_rx_data_buffer) {
goto init_err;
}
memset(s_hci_driver_uart_dma_env.hci_rx_data_buffer, 0, OS_MEMPOOL_SIZE(HCI_RX_DATA_POOL_NUM,
HCI_RX_DATA_BLOCK_SIZE) * sizeof(os_membuf_t));
rc = os_mempool_init(s_hci_driver_uart_dma_env.hci_rx_data_pool, HCI_RX_DATA_POOL_NUM,
HCI_RX_DATA_BLOCK_SIZE, s_hci_driver_uart_dma_env.hci_rx_data_buffer,
"hci_rx_data_pool");
if (rc) {
goto init_err;
}
/* Malloc hci rxinfo pool */
s_hci_driver_uart_dma_env.hci_rxinfo_pool = (struct os_mempool *)malloc(sizeof(struct os_mempool));
if (!s_hci_driver_uart_dma_env.hci_rxinfo_pool) {
goto init_err;
}
memset(s_hci_driver_uart_dma_env.hci_rxinfo_pool, 0, sizeof(struct os_mempool));
s_hci_driver_uart_dma_env.hci_rxinfo_buffer = malloc(OS_MEMPOOL_SIZE(HCI_RX_INFO_POOL_NUM,
sizeof(hci_message_t)) * sizeof(os_membuf_t));
if (!s_hci_driver_uart_dma_env.hci_rxinfo_buffer) {
goto init_err;
}
memset(s_hci_driver_uart_dma_env.hci_rxinfo_buffer, 0, OS_MEMPOOL_SIZE(HCI_RX_INFO_POOL_NUM,
sizeof(hci_message_t)) * sizeof(os_membuf_t));
rc = os_mempool_init(s_hci_driver_uart_dma_env.hci_rxinfo_pool, HCI_RX_INFO_POOL_NUM,
sizeof(hci_message_t), s_hci_driver_uart_dma_env.hci_rxinfo_buffer,
"hci_rxinfo_pool");
if (rc) {
goto init_err;
}
return rc;
init_err:
hci_driver_uart_dma_memory_deinit();
return rc;
}
static IRAM_ATTR bool hci_uart_tl_rx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
esp_bt_hci_tl_callback_t callback = uart_env.rx.callback;
void *arg = uart_env.rx.arg;
assert(dma_chan == s_rx_channel);
assert(uart_env.rx.callback != NULL);
// clear callback pointer
uart_env.rx.callback = NULL;
uart_env.rx.arg = NULL;
// call handler
callback(arg, ESP_BT_HCI_TL_STATUS_OK);
return true;
}
static IRAM_ATTR bool hci_uart_tl_tx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
esp_bt_hci_tl_callback_t callback = uart_env.tx.callback;
assert(dma_chan == s_tx_channel);
assert(uart_env.tx.callback != NULL);
// clear callback pointer
uart_env.tx.callback = NULL;
// call handler
callback(uart_env.tx.arg, ESP_BT_HCI_TL_STATUS_OK);
uart_env.tx.arg = NULL;
return true;
}
uint8_t * IRAM_ATTR hci_driver_uart_dma_rxdata_memory_get(void)
{
uint8_t *rx_data;
rx_data = os_memblock_get(s_hci_driver_uart_dma_env.hci_rx_data_pool);
return rx_data;
}
hci_message_t * IRAM_ATTR hci_driver_uart_dma_rxinfo_memory_get(void)
{
hci_message_t *rx_info;
rx_info = os_memblock_get(s_hci_driver_uart_dma_env.hci_rxinfo_pool);
return rx_info;
}
void IRAM_ATTR hci_driver_uart_dma_cache_rxinfo(hci_message_t *hci_rxinfo)
{
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_TAIL(&g_hci_rxinfo_head, hci_rxinfo, next);
OS_EXIT_CRITICAL(sr);
}
void IRAM_ATTR hci_driver_uart_dma_continue_rx_enable(bool enable)
{
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
s_hci_driver_uart_dma_env.is_continue_rx = enable;
OS_EXIT_CRITICAL(sr);
}
void IRAM_ATTR hci_driver_uart_dma_rxinfo_mem_exhausted_set(bool is_exhausted)
{
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
s_hci_driver_uart_dma_env.rxinfo_mem_exhausted = is_exhausted;
OS_EXIT_CRITICAL(sr);
}
void IRAM_ATTR hci_driver_uart_dma_recv_callback(void *arg, uint8_t status)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
hci_message_t *hci_rxinfo;
uint8_t *rx_data;
if (s_hci_driver_uart_dma_env.rxinfo_mem_exhausted) {
ESP_LOGE(TAG, "Will lost rx data, need adjust rxinfo memory count\n");
assert(0);
}
hci_rxinfo = hci_driver_uart_dma_rxinfo_memory_get();
if (!hci_rxinfo) {
ESP_LOGW(TAG, "set rxinfo mem exhausted flag\n");
hci_driver_uart_dma_rxinfo_mem_exhausted_set(true);
xSemaphoreGiveFromISR(s_hci_driver_uart_dma_env.process_sem, &xHigherPriorityTaskWoken);
return;
}
hci_rxinfo->ptr = (void *)uart_env.rx.link_head->buf;
hci_rxinfo->length = uart_env.rx.link_head->length;
hci_driver_uart_dma_cache_rxinfo(hci_rxinfo);
xSemaphoreGiveFromISR(s_hci_driver_uart_dma_env.process_sem, &xHigherPriorityTaskWoken);
rx_data = hci_driver_uart_dma_rxdata_memory_get();
if (!rx_data) {
hci_driver_uart_dma_continue_rx_enable(true);
}else {
hci_driver_uart_dma_rx_start(rx_data, HCI_RX_DATA_BLOCK_SIZE);
}
}
void IRAM_ATTR hci_driver_uart_dma_txstate_set(hci_trans_tx_state_t tx_state)
{
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
s_hci_driver_uart_dma_env.hci_tx_state = tx_state;
OS_EXIT_CRITICAL(sr);
}
void IRAM_ATTR hci_driver_uart_dma_send_callback(void *arg, uint8_t status)
{
uhci_lldesc_t *lldesc_head;
uhci_lldesc_t *lldesc_nxt;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
lldesc_head = uart_env.tx.link_head;
while (lldesc_head) {
lldesc_nxt = lldesc_head->qe.stqe_next;
os_memblock_put(&s_hci_driver_uart_dma_env.lldesc_mem_pool, lldesc_head);
lldesc_head = lldesc_nxt;
}
uart_env.tx.link_head = NULL;
hci_driver_uart_dma_txstate_set(HCI_TRANS_TX_IDLE);
xSemaphoreGiveFromISR(s_hci_driver_uart_dma_env.process_sem, &xHigherPriorityTaskWoken);
}
static IRAM_ATTR void hci_driver_uart_dma_recv_async(uint8_t *buf, uint32_t size, esp_bt_hci_tl_callback_t callback, void *arg)
{
uhci_lldesc_t *lldesc_head;
assert(buf != NULL);
assert(size != 0);
assert(callback != NULL);
uart_env.rx.callback = callback;
uart_env.rx.arg = arg;
lldesc_head = uart_env.rx.link_head;
while (lldesc_head) {
os_memblock_put(&s_hci_driver_uart_dma_env.lldesc_mem_pool, lldesc_head),
lldesc_head = lldesc_head->qe.stqe_next;
}
uart_env.rx.link_head = NULL;
lldesc_head = os_memblock_get(&s_hci_driver_uart_dma_env.lldesc_mem_pool);
assert(lldesc_head);
memset(lldesc_head, 0, sizeof(uhci_lldesc_t));
lldesc_head->buf = buf;
lldesc_head->size = size;
lldesc_head->eof = 0;
s_uhci_hw->pkt_thres.pkt_thrs = size;
uart_env.rx.link_head = lldesc_head;
gdma_start(s_rx_channel, (intptr_t)(uart_env.rx.link_head));
}
int IRAM_ATTR hci_driver_uart_dma_rx_start(uint8_t *rx_data, uint32_t length)
{
hci_driver_uart_dma_recv_async(rx_data, length, hci_driver_uart_dma_recv_callback, NULL);
return 0;
}
int hci_driver_uart_dma_tx_start(esp_bt_hci_tl_callback_t callback, void *arg)
{
void *data;
bool last_frame;
bool head_is_setted;
uint32_t tx_len;
uhci_lldesc_t *lldesc_data;
uhci_lldesc_t *lldesc_head;
uhci_lldesc_t *lldesc_tail;
lldesc_head = NULL;
lldesc_tail = NULL;
head_is_setted = false;
last_frame = false;
while (true) {
tx_len = hci_driver_util_tx_list_dequeue(0xffffff, &data, &last_frame);
if (!tx_len) {
break;
}
lldesc_data = os_memblock_get(&s_hci_driver_uart_dma_env.lldesc_mem_pool);
/* According to the current processing logic It should not be empty */
assert(lldesc_data);
memset(lldesc_data, 0, sizeof(uhci_lldesc_t));
lldesc_data->length = tx_len;
lldesc_data->buf = data;
lldesc_data->eof = 0;
if (!head_is_setted) {
lldesc_head = lldesc_data;
head_is_setted = true;
} else {
lldesc_tail->qe.stqe_next = lldesc_data;
}
lldesc_tail = lldesc_data;
if (last_frame) {
break;
}
}
if (lldesc_head) {
lldesc_tail->eof = 1;
uart_env.tx.link_head = lldesc_head;
uart_env.tx.callback = callback;
uart_env.tx.arg = arg;
gdma_start(s_tx_channel, (intptr_t)(uart_env.tx.link_head));
return 0;
} else {
return -1;
}
}
static void hci_driver_uart_dma_install(void)
{
periph_module_enable(PERIPH_UHCI0_MODULE);
periph_module_reset(PERIPH_UHCI0_MODULE);
// install DMA driver
gdma_channel_alloc_config_t tx_channel_config = {
.flags.reserve_sibling = 1,
.direction = GDMA_CHANNEL_DIRECTION_TX,
};
ESP_ERROR_CHECK(gdma_new_channel(&tx_channel_config, &s_tx_channel));
gdma_channel_alloc_config_t rx_channel_config = {
.direction = GDMA_CHANNEL_DIRECTION_RX,
.sibling_chan = s_tx_channel,
};
ESP_ERROR_CHECK(gdma_new_channel(&rx_channel_config, &s_rx_channel));
gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
gdma_strategy_config_t strategy_config = {
.auto_update_desc = false,
.owner_check = false
};
gdma_apply_strategy(s_tx_channel, &strategy_config);
gdma_apply_strategy(s_rx_channel, &strategy_config);
gdma_rx_event_callbacks_t rx_cbs = {
.on_recv_eof = hci_uart_tl_rx_eof_callback
};
gdma_register_rx_event_callbacks(s_rx_channel, &rx_cbs, NULL);
gdma_tx_event_callbacks_t tx_cbs = {
.on_trans_eof = hci_uart_tl_tx_eof_callback
};
gdma_register_tx_event_callbacks(s_tx_channel, &tx_cbs, NULL);
// configure UHCI
uhci_ll_init((uhci_dev_t *)s_uhci_hw);
// uhci_ll_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_LEN_EOF);
uhci_ll_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_IDLE_EOF);
// disable software flow control
s_uhci_hw->escape_conf.val = 0;
uhci_ll_attach_uart_port((uhci_dev_t *)s_uhci_hw, s_hci_driver_uart_dma_env.hci_uart_params->hci_uart_port);
}
static int
hci_driver_uart_dma_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir)
{
/* By now, this layer is only used by controller. */
assert(dir == HCI_DRIVER_DIR_C2H);
ESP_LOGD(TAG, "dma tx:");
ESP_LOG_BUFFER_HEXDUMP(TAG, data, length, ESP_LOG_DEBUG);
hci_driver_util_tx_list_enqueue(data_type, data, length);
xSemaphoreGive(s_hci_driver_uart_dma_env.process_sem);
return 0;
}
static int
hci_driver_uart_dma_h4_frame_cb(uint8_t pkt_type, void *data)
{
hci_driver_forward_fn *forward_cb;
forward_cb = s_hci_driver_uart_dma_env.forward_cb;
if (!forward_cb) {
return -1;
}
ESP_LOGD(TAG, "h4 frame\n");
return forward_cb(pkt_type, data, 0, HCI_DRIVER_DIR_H2C);
}
static void
hci_driver_uart_dma_process_task(void *p)
{
hci_message_t *rxinfo_container;
os_sr_t sr;
int ret;
uint8_t* rx_data;
uint32_t rx_len;
while (true) {
xSemaphoreTake(s_hci_driver_uart_dma_env.process_sem, portMAX_DELAY);
ESP_LOGD(TAG, "task run:%d\n",s_hci_driver_uart_dma_env.hci_tx_state);
if (s_hci_driver_uart_dma_env.rxinfo_mem_exhausted) {
rx_data = (void *)uart_env.rx.link_head->buf;
rx_len = uart_env.rx.link_head->length;
ESP_LOGD(TAG, "rxinfo exhausted:");
ESP_LOG_BUFFER_HEXDUMP(TAG, rx_data, rx_len, ESP_LOG_DEBUG);
ret = hci_h4_sm_rx(s_hci_driver_uart_dma_env.h4_sm, rx_data, rx_len);
hci_driver_uart_dma_rx_start(rx_data, HCI_RX_DATA_BLOCK_SIZE);
hci_driver_uart_dma_rxinfo_mem_exhausted_set(false);
if (ret < 0) {
ESP_LOGW(TAG, "parse rx data error!\n");
r_ble_ll_hci_ev_hw_err(ESP_HCI_SYNC_LOSS_ERR);
}
}
while (!STAILQ_EMPTY(&g_hci_rxinfo_head)) {
OS_ENTER_CRITICAL(sr);
rxinfo_container = STAILQ_FIRST(&g_hci_rxinfo_head);
STAILQ_REMOVE_HEAD(&g_hci_rxinfo_head, next);
OS_EXIT_CRITICAL(sr);
rx_data = rxinfo_container->ptr;
rx_len = rxinfo_container->length;
ESP_LOGD(TAG, "uart rx");
ESP_LOG_BUFFER_HEXDUMP(TAG, rx_data, rx_len, ESP_LOG_DEBUG);
ret = hci_h4_sm_rx(s_hci_driver_uart_dma_env.h4_sm, rx_data, rx_len);
if (ret < 0) {
ESP_LOGW(TAG, "parse rx data error!\n");
r_ble_ll_hci_ev_hw_err(ESP_HCI_SYNC_LOSS_ERR);
}
os_memblock_put(s_hci_driver_uart_dma_env.hci_rxinfo_pool, rxinfo_container);
/* No need to enter CRITICAL */
if (s_hci_driver_uart_dma_env.is_continue_rx) {
/* We should set continux rx flag first, RX interrupted may happened when rx start soon */
hci_driver_uart_dma_continue_rx_enable(false);
hci_driver_uart_dma_rx_start(rx_data, HCI_RX_DATA_BLOCK_SIZE);
} else {
os_memblock_put(s_hci_driver_uart_dma_env.hci_rx_data_pool, rx_data);
}
}
/* Process Tx data */
if (s_hci_driver_uart_dma_env.hci_tx_state == HCI_TRANS_TX_IDLE) {
ret = hci_driver_uart_dma_tx_start(hci_driver_uart_dma_send_callback, (void*)&uart_env);
if (!ret) {
s_hci_driver_uart_dma_env.hci_tx_state = HCI_TRANS_TX_START;
}
}
}
}
static int
hci_driver_uart_dma_task_create(void)
{
/* !TODO: Set the core id by menuconfig */
xTaskCreatePinnedToCore(hci_driver_uart_dma_process_task, "hci_driver_uart_dma_process_task",
CONFIG_BT_LE_HCI_TRANS_TASK_STACK_SIZE, NULL,
ESP_TASK_BT_CONTROLLER_PRIO, &s_hci_driver_uart_dma_env.task_handler,
0);
assert(s_hci_driver_uart_dma_env.task_handler);
ESP_LOGI(TAG, "hci transport task create successfully, prio:%d, stack size: %ld",
ESP_TASK_BT_CONTROLLER_PRIO, CONFIG_BT_LE_HCI_TRANS_TASK_STACK_SIZE);
return 0;
}
static void
hci_driver_uart_dma_deinit(void)
{
if (s_hci_driver_uart_dma_env.task_handler) {
vTaskDelete(s_hci_driver_uart_dma_env.task_handler);
s_hci_driver_uart_dma_env.task_handler = NULL;
}
ESP_ERROR_CHECK(uart_driver_delete(s_hci_driver_uart_dma_env.hci_uart_params->hci_uart_port));
hci_driver_uart_dma_memory_deinit();
if (!s_hci_driver_uart_dma_env.process_sem) {
vSemaphoreDelete(s_hci_driver_uart_dma_env.process_sem);
}
hci_driver_util_deinit();
memset(&s_hci_driver_uart_dma_env, 0, sizeof(hci_driver_uart_dma_env_t));
}
static int
hci_driver_uart_dma_init(hci_driver_forward_fn *cb)
{
int rc;
memset(&s_hci_driver_uart_dma_env, 0, sizeof(hci_driver_uart_dma_env_t));
s_hci_driver_uart_dma_env.h4_sm = &s_hci_driver_uart_h4_sm;
hci_h4_sm_init(s_hci_driver_uart_dma_env.h4_sm, &s_hci_driver_mem_alloc, hci_driver_uart_dma_h4_frame_cb);
rc = hci_driver_util_init();
if (rc) {
goto error;
}
s_hci_driver_uart_dma_env.process_sem = xSemaphoreCreateBinary();
if (!s_hci_driver_uart_dma_env.process_sem) {
goto error;
}
rc = hci_driver_uart_dma_memory_init();
if (rc) {
goto error;
}
s_hci_driver_uart_dma_env.forward_cb = cb;
s_hci_driver_uart_dma_env.hci_uart_params = &hci_driver_uart_dma_params;
hci_driver_uart_config(&hci_driver_uart_dma_params);
ESP_LOGI(TAG, "uart attach uhci!");
hci_driver_uart_dma_install();
STAILQ_INIT(&g_hci_rxinfo_head);
rc = hci_driver_uart_dma_task_create();
if (rc) {
goto error;
}
s_hci_driver_uart_dma_env.hci_tx_state = HCI_TRANS_TX_IDLE;
s_hci_driver_uart_dma_env.rxinfo_mem_exhausted = false;
s_hci_driver_uart_dma_env.is_continue_rx = false;
hci_driver_uart_dma_rx_start(os_memblock_get(s_hci_driver_uart_dma_env.hci_rx_data_pool),
HCI_RX_DATA_BLOCK_SIZE);
return 0;
error:
hci_driver_uart_dma_deinit();
return rc;
}
int
hci_driver_uart_dma_reconfig_pin(int tx_pin, int rx_pin, int cts_pin, int rts_pin)
{
hci_driver_uart_params_config_t *uart_param = s_hci_driver_uart_dma_env.hci_uart_params;
uart_param->hci_uart_tx_pin = tx_pin;
uart_param->hci_uart_rx_pin = rx_pin;
uart_param->hci_uart_rts_pin = rts_pin;
uart_param->hci_uart_cts_pin = cts_pin;
return hci_driver_uart_config(uart_param);
}
hci_driver_ops_t hci_driver_uart_dma_ops = {
.hci_driver_tx = hci_driver_uart_dma_tx,
.hci_driver_init = hci_driver_uart_dma_init,
.hci_driver_deinit = hci_driver_uart_dma_deinit,
};

View File

@ -0,0 +1,61 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.h>
#include "esp_hci_internal.h"
#include "esp_hci_driver.h"
typedef struct {
hci_driver_forward_fn *forward_cb;
} hci_driver_vhci_env_t;
static hci_driver_vhci_env_t s_hci_driver_vhci_env;
static int
hci_driver_vhci_controller_tx(hci_driver_data_type_t data_type, uint8_t *data)
{
/* The length is contained in the data. */
return s_hci_driver_vhci_env.forward_cb(data_type, data, 0, HCI_DRIVER_DIR_C2H);
}
static int
hci_driver_vhci_host_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length)
{
return s_hci_driver_vhci_env.forward_cb(data_type, data, length, HCI_DRIVER_DIR_H2C);
}
static int
hci_driver_vhci_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir)
{
int rc;
if (dir == HCI_DRIVER_DIR_C2H) {
rc = hci_driver_vhci_controller_tx(data_type, data);
} else {
rc = hci_driver_vhci_host_tx(data_type, data, length);
}
return rc;
}
static int
hci_driver_vhci_init(hci_driver_forward_fn *cb)
{
s_hci_driver_vhci_env.forward_cb = cb;
return 0;
}
static void
hci_driver_vhci_deinit(void)
{
memset(&s_hci_driver_vhci_env, 0, sizeof(hci_driver_vhci_env_t));
}
hci_driver_ops_t hci_driver_vhci_ops = {
.hci_driver_tx = hci_driver_vhci_tx,
.hci_driver_init = hci_driver_vhci_init,
.hci_driver_deinit = hci_driver_vhci_deinit,
};

View File

@ -0,0 +1,149 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.h>
#include "os/os_mbuf.h"
#include "esp_hci_transport.h"
#include "esp_hci_internal.h"
#include "esp_hci_driver.h"
#include "esp_bt.h"
typedef struct {
hci_driver_forward_fn *forward_cb;
const esp_vhci_host_callback_t *host_recv_cb;
} hci_driver_vhci_env_t;
static hci_driver_vhci_env_t s_hci_driver_vhci_env;
static int
hci_driver_vhci_controller_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length)
{
int rc;
uint16_t buf_len = 0;
uint8_t *buf = NULL;
struct os_mbuf *om;
if (data_type == HCI_DRIVER_TYPE_ACL) {
om = (struct os_mbuf *)data;
buf_len = length + 1;
buf = malloc(buf_len);
/* TODO: If there is no memory, should handle it in the controller. */
assert(buf);
buf[0] = HCI_DRIVER_TYPE_ACL;
os_mbuf_copydata(om, 0, length, &buf[1]);
os_mbuf_free_chain(om);
} else if (data_type == HCI_DRIVER_TYPE_EVT) {
buf_len = length + 1;
buf = malloc(buf_len);
/* TODO: If there is no memory, should handle it in the controller. */
assert(buf != NULL);
buf[0] = HCI_DRIVER_TYPE_EVT;
memcpy(&buf[1], data, length);
r_ble_hci_trans_buf_free(data);
}
rc = s_hci_driver_vhci_env.forward_cb(data_type, buf, buf_len, HCI_DRIVER_DIR_C2H);
free(buf);
return rc;
}
static int
hci_driver_vhci_host_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length)
{
uint8_t *cmd;
struct os_mbuf *om;
if (data_type == HCI_DRIVER_TYPE_ACL) {
om = os_msys_get_pkthdr(length, ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE);
assert(om);
assert(os_mbuf_append(om, &data[1], length - 1) == 0);
data = (uint8_t *)om;
} else if (data_type == HCI_DRIVER_TYPE_CMD) {
cmd = r_ble_hci_trans_buf_alloc(ESP_HCI_INTERNAL_BUF_CMD);
assert(cmd);
memcpy(cmd, data + 1, length - 1);
data = cmd;
}
return s_hci_driver_vhci_env.forward_cb(data_type, data, length, HCI_DRIVER_DIR_H2C);
}
static int
hci_driver_vhci_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir)
{
int rc;
if (dir == HCI_DRIVER_DIR_C2H) {
rc = hci_driver_vhci_controller_tx(data_type, data, length);
} else {
rc = hci_driver_vhci_host_tx(data_type, data, length);
}
return rc;
}
static int
hci_driver_vhci_init(hci_driver_forward_fn *cb)
{
memset(&s_hci_driver_vhci_env, 0, sizeof(hci_driver_vhci_env_t));
s_hci_driver_vhci_env.forward_cb = cb;
return 0;
}
static void
hci_driver_vhci_deinit(void)
{
memset(&s_hci_driver_vhci_env, 0, sizeof(hci_driver_vhci_env_t));
}
hci_driver_ops_t hci_driver_vhci_ops = {
.hci_driver_tx = hci_driver_vhci_tx,
.hci_driver_init = hci_driver_vhci_init,
.hci_driver_deinit = hci_driver_vhci_deinit,
};
/* Special APIs declared in the `esp_bt.h'. */
static int
hci_driver_vhci_host_recv_cb(hci_trans_pkt_ind_t type, uint8_t *data, uint16_t len)
{
static const esp_vhci_host_callback_t *host_recv_cb;
host_recv_cb = s_hci_driver_vhci_env.host_recv_cb;
if (host_recv_cb) {
return host_recv_cb->notify_host_recv(data, len);
}
return -1;
}
esp_err_t
esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback)
{
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
return ESP_FAIL;
}
s_hci_driver_vhci_env.host_recv_cb = callback;
if(hci_transport_host_callback_register(hci_driver_vhci_host_recv_cb)) {
s_hci_driver_vhci_env.host_recv_cb = NULL;
return ESP_FAIL;
}
return ESP_OK;
}
void
esp_vhci_host_send_packet(uint8_t *data, uint16_t len)
{
hci_driver_vhci_tx(data[0], data, len, HCI_DRIVER_DIR_H2C);
}
bool
esp_vhci_host_check_send_available(void)
{
return true;
}

View File

@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.h>
#include "os/os_mbuf.h"
#include "esp_hci_transport.h"
#include "esp_hci_internal.h"
#include "esp_hci_driver.h"
typedef struct {
hci_driver_forward_fn *forward_cb;
} hci_driver_vhci_env_t;
static hci_driver_vhci_env_t s_hci_driver_vhci_env;
static int
hci_driver_vhci_controller_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length)
{
int rc;
uint16_t len = 0;
uint8_t *buf = NULL;
struct os_mbuf *om;
if (data_type == HCI_DRIVER_TYPE_ACL) {
/* The ACL data will be packaged as structure of `os_mbuf`.
* 1. Allocate a buffer suitable for the host. Use the following method to copy the data
* from the os_mbuf to the newly allocated memory.
* ```c
* buf = malloc(length);
* os_mbuf_copydata(om, 0, length, buf);
* ```
* 2. Free the controller's os_mbuf
* ```c
* os_mbuf_free_chain(om);
* ```
*/
} else if (data_type == HCI_DRIVER_TYPE_EVT) {
/* The event data will be packaged as an array.
* 1. Allocate a buffer suitable for the host. Use the following method to copy the data
* from the controller buffer to the newly allocated memory.
* ```c
* buf = malloc(length);
* memcpy(buf, data, length);
* ```
* 2. Free the controller's buffer.
* ```c
* r_ble_hci_trans_buf_free(data);
* ```
*/
} else {
assert(0);
}
rc = s_hci_driver_vhci_env.forward_cb(data_type, buf, len, HCI_DRIVER_DIR_C2H);
free(buf);
return rc;
}
static int
hci_driver_vhci_host_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length)
{
uint8_t *hci_data;
struct os_mbuf *om;
if (data_type == HCI_DRIVER_TYPE_ACL) {
/* The ACL data needs to be packaged as structure of `os_mbuf`.
* 1. Get an os_mbuf in the following way.
* ```c
* om = os_msys_get_pkthdr(length, ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE);
* ```
* 2. Copy the host's data into this os_mbuf using the following method.
* ```c
* assert(os_mbuf_append(om, data, length) == 0);
* hci_data = (uint8_t *)om;
* ```
* 3. Free the host's buffer if needed.
*/
} else if (data_type == HCI_DRIVER_TYPE_CMD) {
/* The COMMAND data needs to be packaged as an array.
* 1. Get a command buffer from the controller.
* ```c
* hci_data = r_ble_hci_trans_buf_alloc(ESP_HCI_INTERNAL_BUF_CMD);
* ```
* 2. Copy the host's data into this buffer.
* ```c
* memcpy(hci_data, data, length);
* ```
* 3. Free the host's buffer if needed.
*/
} else {
assert(0);
}
return s_hci_driver_vhci_env.forward_cb(data_type, hci_data, length, HCI_DRIVER_DIR_H2C);
}
static int
hci_driver_vhci_tx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir)
{
int rc;
if (dir == HCI_DRIVER_DIR_C2H) {
rc = hci_driver_vhci_controller_tx(data_type, data, length);
} else {
rc = hci_driver_vhci_host_tx(data_type, data, length);
}
return rc;
}
static int
hci_driver_vhci_init(hci_driver_forward_fn *cb)
{
memset(&s_hci_driver_vhci_env, 0, sizeof(hci_driver_vhci_env_t));
s_hci_driver_vhci_env.forward_cb = cb;
return 0;
}
static void
hci_driver_vhci_deinit(void)
{
memset(&s_hci_driver_vhci_env, 0, sizeof(hci_driver_vhci_env_t));
}
hci_driver_ops_t hci_driver_vhci_ops = {
.hci_driver_tx = hci_driver_vhci_tx,
.hci_driver_init = hci_driver_vhci_init,
.hci_driver_deinit = hci_driver_vhci_deinit,
};

View File

@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* 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 _HCI_H4_H_
#define _HCI_H4_H_
#include <stdint.h>
#define HCI_H4_NONE 0x00
#define HCI_H4_CMD 0x01
#define HCI_H4_ACL 0x02
#define HCI_H4_EVT 0x04
#define HCI_H4_ISO 0x05
typedef void *(hci_h4_alloc_cmd)(void);
typedef void *(hci_h4_alloc_evt)(int);
typedef struct os_mbuf *(hci_h4_alloc_acl)(void);
typedef struct os_mbuf *(hci_h4_alloc_iso)(void);
struct hci_h4_allocators {
hci_h4_alloc_cmd *cmd;
hci_h4_alloc_acl *acl;
hci_h4_alloc_evt *evt;
hci_h4_alloc_iso *iso;
};
extern const struct hci_h4_allocators hci_h4_allocs_from_ll;
extern const struct hci_h4_allocators hci_h4_allocs_from_hs;
typedef int (hci_h4_frame_cb)(uint8_t pkt_type, void *data);
struct hci_h4_sm {
uint8_t state;
uint8_t pkt_type;
uint8_t min_len;
uint16_t len;
uint16_t exp_len;
uint8_t hdr[4];
union {
uint8_t *buf;
struct os_mbuf *om;
};
const struct hci_h4_allocators *allocs;
hci_h4_frame_cb *frame_cb;
};
void hci_h4_sm_init(struct hci_h4_sm *h4sm,
const struct hci_h4_allocators *allocs,
hci_h4_frame_cb *frame_cb);
int hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len);
#endif /* _HCI_H4_H_ */

View File

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _H_HCI_DRIVER_MEM_
#define _H_HCI_DRIVER_MEM_
#include <stdint.h>
#include "os/os_mbuf.h"
void *hci_driver_mem_cmd_alloc(void);
void *hci_driver_mem_evt_alloc(int discardable);
struct os_mbuf *hci_driver_mem_acl_alloc(void);
struct os_mbuf *hci_driver_mem_acl_len_alloc(uint32_t len);
struct os_mbuf *hci_driver_mem_iso_alloc(void);
struct os_mbuf *hci_driver_mem_iso_len_alloc(uint32_t len);
extern const struct hci_h4_allocators s_hci_driver_mem_alloc;
#endif // _H_HCI_DRIVER_MEM_

View File

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _H_HCI_DRIVER_UTIL_
#define _H_HCI_DRIVER_UTIL_
#include <stdint.h>
int hci_driver_util_init(void);
void hci_driver_util_deinit(void);
void hci_driver_util_tx_list_enqueue(hci_driver_data_type_t type, uint8_t *data, uint32_t len);
uint32_t hci_driver_util_tx_list_dequeue(uint32_t max_tx_len, void **tx_data, bool *last_frame);
#endif // _H_HCI_DRIVER_UTIL_

View File

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef H_ESP_HCI_DRIVER_
#define H_ESP_HCI_DRIVER_
#include <stdint.h>
/**
* @brief Enumeration of HCI transport direction.
*/
typedef enum {
HCI_DRIVER_DIR_C2H = 0x00, ///< From controller to host.
HCI_DRIVER_DIR_H2C, ///< From host to controller.
} hci_driver_direction_t;
typedef enum {
HCI_DRIVER_TYPE_CMD = 0x01, ///< HCI Command Indicator.
HCI_DRIVER_TYPE_ACL, ///< HCI ACL Data Indicator.
HCI_DRIVER_TYPE_SYNC, ///< HCI Synchronous Data Indicator.
HCI_DRIVER_TYPE_EVT, ///< HCI Event Indicator.
HCI_DRIVER_TYPE_ISO, ///< HCI Isochronous Data Indicator.
HCI_DRIVER_TYPE_VENDOR, ///< HCI Vendor data Indicator.
} hci_driver_data_type_t;
typedef int hci_driver_forward_fn(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir);
/**
* @brief Structure of HCI driver operations.
*/
typedef struct hci_driver_ops {
int (*hci_driver_tx)(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir);
int (*hci_driver_init)(hci_driver_forward_fn *cb);
void (*hci_driver_deinit)(void);
} hci_driver_ops_t;
#if CONFIG_BT_LE_HCI_INTERFACE_USE_RAM
extern hci_driver_ops_t hci_driver_vhci_ops;
#endif // CONFIG_BT_LE_HCI_INTERFACE_USE_RAM
#if CONFIG_BT_LE_HCI_INTERFACE_USE_UART
extern hci_driver_ops_t hci_driver_uart_ops;
#if CONFIG_BT_LE_UART_HCI_DMA_MODE
extern hci_driver_ops_t hci_driver_uart_dma_ops;
#endif // CONFIG_BT_LE_UART_HCI_DMA_MODE
#endif // CONFIG_BT_LE_HCI_INTERFACE_USE_UART
#endif // H_ESP_HCI_DRIVER_

View File

@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef H_ESP_HCI_INTERNAL_
#define H_ESP_HCI_INTERNAL_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "os/os_mbuf.h"
/* The leadingspace in user info header for ACL data */
#define ESP_HCI_INTERNAL_ACL_MBUF_LEADINGSPCAE (4)
#define ESP_HCI_INTERNAL_BUF_CMD (3)
/**
* @brief Define the HCI hardware error code for synchronization loss.
* This error code is used to indicate a loss of synchronization between the controller and the host.
*/
#define ESP_HCI_SYNC_LOSS_ERR (0x1)
/** Callback function types; executed when HCI packets are received. */
typedef int esp_hci_internal_rx_cmd_fn(uint8_t *cmd, void *arg);
typedef int esp_hci_internal_rx_acl_fn(struct os_mbuf *om, void *arg);
/**
* Configures the HCI transport to operate with a host. The transport will
* execute specified callbacks upon receiving HCI packets from the controller.
*
* @param evt_cb The callback to execute upon receiving an HCI
* event.
* @param evt_arg Optional argument to pass to the event
* callback.
* @param acl_cb The callback to execute upon receiving ACL
* data.
* @param acl_arg Optional argument to pass to the ACL
* callback.
*/
void r_ble_hci_trans_cfg_hs(esp_hci_internal_rx_cmd_fn *evt_cb, void *evt_arg,
esp_hci_internal_rx_acl_fn *acl_cb, void *acl_arg);
/**
* 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 r_ble_hci_trans_hs_acl_tx(struct os_mbuf *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 r_ble_hci_trans_hs_cmd_tx(uint8_t *cmd);
/**
* 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 * r_ble_hci_trans_buf_alloc(int type);
/**
* Frees the specified flat buffer. The buffer must have been allocated via
* ble_hci_trans_buf_alloc().
*
* @param buf The buffer to free.
*/
void r_ble_hci_trans_buf_free(uint8_t *buf);
/**
* Configures a callback to get executed whenever an ACL data packet is freed.
* The function is called immediately before the free occurs.
*
* @param cb The callback to configure.
* @param arg An optional argument to pass to the callback.
*
* @return 0 on success;
* BLE_ERR_UNSUPPORTED if the transport does not
* support this operation.
*/
int r_ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg);
/**
* @brief Handle an HCI hardware error event.
* This function processes a hardware error code and generates the appropriate HCI hardware error event.
*
* @param hw_err The hardware error code that needs to be processed. The specific meaning of the error code
* depends on the implementation and the hardware.
*
* @return int Returns 0 on success, or a non-zero error code on failure.
*
* @note This function should be called whenever a hardware error is detected in the HCI Layer.
*/
int r_ble_ll_hci_ev_hw_err(uint8_t hw_err);
//!TODO: Check what this API is used for
int r_ble_hci_trans_reset(void);
//!TODO: Should we initialize the hci layer in IDF ?
void esp_ble_hci_trans_init(uint8_t);
#ifdef __cplusplus
}
#endif
#endif /* H_ESP_HCI_INTERNAL_ */

View File

@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef H_ESP_HCI_TRANSPORT_
#define H_ESP_HCI_TRANSPORT_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "os/os_mbuf.h"
#include "esp_hci_driver.h"
/**
* @brief Enumeration of HCI packet indicators
*/
typedef enum {
HCI_CMD_IND = 0x01, /*!< HCI Command Indicator */
HCI_ACL_IND, /*!< HCI ACL Data Indicator */
HCI_SYNC_IND, /*!< HCI Synchronous Data Indicator */
HCI_EVT_IND, /*!< HCI Event Indicator */
HCI_ISO_IND, /*!< HCI Isochronous Data Indicator */
HCI_VENDOR_IND, /*!< HCI Vendor data Indicator */
} hci_trans_pkt_ind_t;
/**
* @brief Enumeration of HCI Transport Mode
*/
typedef enum {
HCI_TRANSPORT_VHCI, /*!< VHCI Transport Mode */
HCI_TRANSPORT_UART_NO_DMA, /*!< UART_NO_DMA Transport Mode */
HCI_TRANSPORT_UART_UHCI, /*!< UART_UHCI Transport Mode */
HCI_TRANSPORT_SDIO, /*!< SDIO Transport Mode */
HCI_TRANSPORT_USB, /*!< USB Transport Mode */
} hci_trans_mode_t;
typedef int hci_transport_host_recv_fn(hci_trans_pkt_ind_t type, uint8_t *data, uint16_t len);
/**
* @brief Initialize the HCI transport layer.
* It should be called before using any other functions in the transport layer.
*
* @param hci_transport_mode The mode in which the HCI transport should operate.
*
* @return int Returns 0 on success, or a non-zero error code on failure.
*/
int hci_transport_init(uint8_t hci_transport_mode);
/**
* @brief Deinitialize the HCI transport layer for releasing any allocated resources.
*/
void hci_transport_deinit(void);
/**
* @brief Set the host's HCI callback which will be invoked when receiving ACL/Events from controller.
* @param callback hci_transport_host_recv_fn type variable
* @return int 0 on success, non-zero error code on failure.
*/
int hci_transport_host_callback_register(hci_transport_host_recv_fn *callback);
/**
* @brief Called to send HCI commands form host to controller.
* @param data Point to the commands data
* @param length Length of data
* @return int 0 on success, non-zero error code on failure.
*/
int hci_transport_host_cmd_tx(uint8_t *data, uint32_t length);
/**
* @brief Called to send HCI ACL form host to controller.
* @param data Point to the ACL data
* @param length Length of data
* @return int 0 on success, non-zero error code on failure.
*/
int hci_transport_host_acl_tx(uint8_t *data, uint32_t length);
#ifdef __cplusplus
}
#endif
#endif /* H_ESP_HCI_TRANSPORT_ */

View File

@ -1,100 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include "driver/uart.h"
/**
* Function prototype for UART driver to ask for more data to send.
* Returns -1 if no more data is available for TX.
* Driver must call this with interrupts disabled.
*/
typedef int (*hci_uart_tx_char)(void *arg);
/**
* Function prototype for UART driver to report that transmission is
* complete. This should be called when transmission of last byte is
* finished.
* Driver must call this with interrupts disabled.
*/
typedef void (*hci_uart_tx_done)(void *arg);
/**
* Function prototype for UART driver to report incoming byte of data.
* Returns -1 if data was dropped.
* Driver must call this with interrupts disabled.
*/
typedef int (*hci_uart_rx_char)(void *arg, uint8_t byte);
/**
* Initializes given uart. Mapping of logical UART number to physical
* UART/GPIO pins is in BSP.
*/
int hci_uart_init_cbs(int uart, hci_uart_tx_char tx_func,
hci_uart_tx_done tx_done, hci_uart_rx_char rx_func, void *arg);
/**
* Applies given configuration to UART.
*
* @param port_num The UART number to configure
* @param speed The baudrate in bps to configure
* @param databits The number of databits to send per byte
* @param stopbits The number of stop bits to send
* @param parity The UART parity
* @param flow_ctl Flow control settings on the UART
*
* @return 0 on success, non-zero error code on failure
*/
int hci_uart_config(int port_num, int32_t baud_rate, uint8_t data_bits, uint8_t stop_bits,
uart_parity_t parity, uart_hw_flowcontrol_t flow_ctl);
/**
* Close UART port. Can call hal_uart_config() with different settings after
* calling this.
*
* @param port_num The UART number to close
*/
int hci_uart_close(int port_num);
/**
* More data queued for transmission. UART driver will start asking for that
* data.
*
* @param port_num The UART number to start TX on
*/
void hci_uart_start_tx(int port_num);
/**
* Upper layers have consumed some data, and are now ready to receive more.
* This is meaningful after uart_rx_char callback has returned -1 telling
* that no more data can be accepted.
*
* @param port_num The UART number to begin RX on
*/
void hci_uart_start_rx(int port_num);
/**
* @brief reconfig hci uart pin
*
* @param tx_pin The Tx pin
* @param rx_pin The Rx pin
* @param cts_pin The CTS pin
* @param rts_pin The RTS pin
* @return int 0 on success, non-zero error code on failure
*/
int hci_uart_reconfig_pin(int tx_pin, int rx_pin, int cts_pin, int rts_pin);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,175 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_hci_transport.h"
#include "esp_hci_internal.h"
#include "esp_bt.h"
typedef struct hci_transport_env
{
hci_transport_host_recv_fn *host_recv_cb;
hci_driver_ops_t *driver_ops;
} hci_transport_env_t;
static hci_transport_env_t s_hci_transport_env;
/* Functions for packets Rx. */
static int
hci_transport_controller_packet_rx(hci_driver_data_type_t data_type, uint8_t *data)
{
if (data_type == HCI_DRIVER_TYPE_CMD) {
r_ble_hci_trans_hs_cmd_tx(data);
}
if (data_type == HCI_DRIVER_TYPE_ACL) {
r_ble_hci_trans_hs_acl_tx((struct os_mbuf *) data);
}
return 0;
}
static int
hci_transport_host_packet_rx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length)
{
if (!s_hci_transport_env.host_recv_cb) {
return -1;
}
return s_hci_transport_env.host_recv_cb((hci_trans_pkt_ind_t)data_type, data, length);
}
static int
hci_transport_packet_rx(hci_driver_data_type_t data_type, uint8_t *data, uint32_t length,
hci_driver_direction_t dir)
{
int rc;
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
return -1;
}
if (dir == HCI_DRIVER_DIR_C2H) {
rc = hci_transport_host_packet_rx(data_type, data, length);
} else {
rc = hci_transport_controller_packet_rx(data_type, data);
}
return rc;
}
/* Functions for controller Tx. */
static int
hci_transport_controller_tx_dummy(void *data, void *arg)
{
return -1;
}
static int
hci_transport_controller_evt_tx(uint8_t *hci_ev, void *arg)
{
uint32_t len;
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
return -1;
}
len = hci_ev[1] + 2;
return s_hci_transport_env.driver_ops->hci_driver_tx(HCI_DRIVER_TYPE_EVT, hci_ev, len,
HCI_DRIVER_DIR_C2H);
}
static int
hci_transport_controller_acl_tx(struct os_mbuf *om, void *arg)
{
uint16_t len;
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
return -1;
}
len = OS_MBUF_PKTHDR(om)->omp_len;
return s_hci_transport_env.driver_ops->hci_driver_tx(HCI_DRIVER_TYPE_ACL, (uint8_t *)om, len,
HCI_DRIVER_DIR_C2H);
}
/* Functions for host Tx. */
int
hci_transport_host_cmd_tx(uint8_t *data, uint32_t length)
{
return s_hci_transport_env.driver_ops->hci_driver_tx(HCI_DRIVER_TYPE_CMD, data, length,
HCI_DRIVER_DIR_H2C);
}
int
hci_transport_host_acl_tx(uint8_t *data, uint32_t length)
{
return s_hci_transport_env.driver_ops->hci_driver_tx(HCI_DRIVER_TYPE_ACL, data, length,
HCI_DRIVER_DIR_H2C);
}
int
hci_transport_host_callback_register(hci_transport_host_recv_fn *callback)
{
s_hci_transport_env.host_recv_cb = callback;
return 0;
}
int
hci_transport_init(uint8_t hci_transport_mode)
{
int rc;
hci_driver_ops_t *ops;
memset(&s_hci_transport_env, 0, sizeof(hci_transport_env_t));
switch(hci_transport_mode) {
#if CONFIG_BT_LE_HCI_INTERFACE_USE_RAM
case HCI_TRANSPORT_VHCI:
ops = &hci_driver_vhci_ops;
break;
#endif // CONFIG_BT_LE_HCI_INTERFACE_USE_RAM
#if CONFIG_BT_LE_HCI_INTERFACE_USE_UART
#if CONFIG_BT_LE_UART_HCI_DMA_MODE
case HCI_TRANSPORT_UART_UHCI:
ops = &hci_driver_uart_dma_ops;
break;
#else
case HCI_TRANSPORT_UART_NO_DMA:
ops = &hci_driver_uart_ops;
break;
#endif // CONFIG_BT_LE_UART_HCI_DMA_MODE
#endif // CONFIG_BT_LE_HCI_INTERFACE_USE_UART
default:
assert(0);
}
rc = ops->hci_driver_init(hci_transport_packet_rx);
if (rc) {
goto error;
}
s_hci_transport_env.driver_ops = ops;
r_ble_hci_trans_cfg_hs(hci_transport_controller_evt_tx, NULL, hci_transport_controller_acl_tx, NULL);
return 0;
error:
hci_transport_deinit();
return rc;
}
void
hci_transport_deinit(void)
{
hci_driver_ops_t *ops;
r_ble_hci_trans_cfg_hs((esp_hci_internal_rx_cmd_fn *)hci_transport_controller_tx_dummy, NULL,
(esp_hci_internal_rx_acl_fn *)hci_transport_controller_tx_dummy, NULL);
ops = s_hci_transport_env.driver_ops;
if (ops) {
ops->hci_driver_deinit();
}
memset(&s_hci_transport_env, 0, sizeof(hci_transport_env_t));
}

View File

@ -1,207 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "hci_uart.h"
#include "esp_log.h"
#include "esp_attr.h"
#ifdef CONFIG_BT_LE_HCI_INTERFACE_USE_UART
static const char *TAG = "hci_uart";
#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE)
#define HCI_UART_TX_PIN CONFIG_BT_LE_HCI_UART_TX_PIN
#define HCI_UART_RX_PIN CONFIG_BT_LE_HCI_UART_RX_PIN
#ifdef CONFIG_BT_LE_HCI_UART_FLOWCTRL
#define HCI_UART_FLOWCTRL UART_HW_FLOWCTRL_CTS_RTS
#define HCI_UART_RTS_PIN CONFIG_BT_LE_HCI_UART_RTS_PIN
#define HCI_UART_CTS_PIN CONFIG_BT_LE_HCI_UART_CTS_PIN
#else
#define HCI_UART_FLOWCTRL UART_HW_FLOWCTRL_DISABLE
#define HCI_UART_RTS_PIN (-1)
#define HCI_UART_CTS_PIN (-1)
#endif
typedef struct {
bool uart_opened;
uart_port_t port;
uart_config_t cfg;
QueueHandle_t evt_queue;
TaskHandle_t rx_task_handler;
hci_uart_tx_char tx_char;
hci_uart_tx_done tx_done;
hci_uart_rx_char rx_char;
void *u_func_arg;
} hci_uart_t;
static hci_uart_t hci_uart;
static void IRAM_ATTR hci_uart_rx_task(void *pvParameters)
{
uart_event_t event;
uint8_t *dtmp = (uint8_t *) malloc(RD_BUF_SIZE);
while (hci_uart.uart_opened) {
//Waiting for UART event.
if (xQueueReceive(hci_uart.evt_queue, (void * )&event, (TickType_t)portMAX_DELAY)) {
bzero(dtmp, RD_BUF_SIZE);
ESP_LOGD(TAG, "uart[%d] event:", hci_uart.port);
switch (event.type) {
//Event of UART receving data
/*We'd better handler data event fast, there would be much more data events than
other types of events. If we take too much time on data event, the queue might
be full.*/
case UART_DATA:
// ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
uart_read_bytes(hci_uart.port, dtmp, event.size, portMAX_DELAY);
for (int i = 0 ; i < event.size; i++) {
hci_uart.rx_char(hci_uart.u_func_arg, dtmp[i]);
}
break;
//Event of HW FIFO overflow detected
case UART_FIFO_OVF:
ESP_LOGI(TAG, "hw fifo overflow");
// If fifo overflow happened, you should consider adding flow control for your application.
// The ISR has already reset the rx FIFO,
uart_flush_input(hci_uart.port);
xQueueReset(hci_uart.evt_queue);
break;
//Event of UART ring buffer full
case UART_BUFFER_FULL:
ESP_LOGI(TAG, "ring buffer full");
// If buffer full happened, you should consider encreasing your buffer size
uart_flush_input(hci_uart.port);
xQueueReset(hci_uart.evt_queue);
break;
//Event of UART RX break detected
case UART_BREAK:
ESP_LOGI(TAG, "uart rx break");
break;
//Event of UART parity check error
case UART_PARITY_ERR:
ESP_LOGI(TAG, "uart parity error");
break;
//Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGI(TAG, "uart frame error");
break;
//Others
default:
ESP_LOGI(TAG, "uart event type: %d", event.type);
break;
}
}
}
free(dtmp);
dtmp = NULL;
hci_uart.rx_task_handler = NULL;
vTaskDelete(NULL);
}
int hci_uart_config(int port_num, int32_t baud_rate, uint8_t data_bits, uint8_t stop_bits,
uart_parity_t parity, uart_hw_flowcontrol_t flow_ctl)
{
uart_config_t uart_cfg = {
.baud_rate = baud_rate,
.data_bits = data_bits,
.parity = parity,
.stop_bits = stop_bits,
.flow_ctrl = HCI_UART_FLOWCTRL,
.source_clk = UART_SCLK_DEFAULT,
.rx_flow_ctrl_thresh = UART_HW_FIFO_LEN(port_num) - 1,
};
hci_uart.port = port_num;
hci_uart.cfg = uart_cfg;
int intr_alloc_flags = 0;
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
printf("set uart pin tx:%d, rx:%d.\n", HCI_UART_TX_PIN, HCI_UART_RX_PIN);
printf("set rts:%d, cts:%d.\n", HCI_UART_RTS_PIN, HCI_UART_CTS_PIN);
printf("set baud_rate:%d.\n", baud_rate);
ESP_ERROR_CHECK(uart_driver_delete(port_num));
ESP_ERROR_CHECK(uart_driver_install(port_num, BUF_SIZE * 2, BUF_SIZE * 2, 20, &hci_uart.evt_queue, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(port_num, &hci_uart.cfg));
ESP_ERROR_CHECK(uart_set_pin(port_num, HCI_UART_TX_PIN, HCI_UART_RX_PIN, HCI_UART_RTS_PIN, HCI_UART_CTS_PIN));
hci_uart.uart_opened = true;
//Create a task to handler UART event from ISR
xTaskCreate(hci_uart_rx_task, "hci_uart_rx_task", 2048, NULL, 12, &hci_uart.rx_task_handler);
return 0;
}
void IRAM_ATTR hci_uart_start_tx(int port_num)
{
int data;
uint8_t u8_data = 0;
while (1) {
data = hci_uart.tx_char(hci_uart.u_func_arg);
if (data >= 0) {
u8_data = data;
uart_write_bytes(port_num, (char *)&u8_data, 1);
} else {
break;
}
}
if (hci_uart.tx_done) {
hci_uart.tx_done(hci_uart.u_func_arg);
}
}
int hci_uart_init_cbs(int port_num, hci_uart_tx_char tx_func,
hci_uart_tx_done tx_done, hci_uart_rx_char rx_func, void *arg)
{
hci_uart.tx_char = tx_func;
hci_uart.rx_char = rx_func;
hci_uart.tx_done = tx_done;
hci_uart.u_func_arg = arg;
return 0;
}
int hci_uart_close(int port_num)
{
uart_event_t uart_event;
uart_event.type = UART_BREAK;
hci_uart.uart_opened = false;
// Stop uart rx task
if (hci_uart.rx_task_handler != NULL) {
xQueueSend(hci_uart.evt_queue, (void *)&uart_event, 1000);
ESP_LOGW(TAG, "Waiting for uart task finish...");
}
while (hci_uart.rx_task_handler != NULL);
uart_driver_delete(port_num);
ESP_LOGI(TAG, "hci uart close success.");
return 0;
}
int hci_uart_reconfig_pin(int tx_pin, int rx_pin, int cts_pin, int rts_pin)
{
int port_num = hci_uart.port;
int32_t baud_rate = hci_uart.cfg.baud_rate;
uint8_t data_bits = hci_uart.cfg.data_bits;
uint8_t stop_bits = hci_uart.cfg.stop_bits;
uart_parity_t parity = hci_uart.cfg.parity;
uart_hw_flowcontrol_t flow_ctl = hci_uart.cfg.flow_ctrl;
hci_uart_close(port_num);
hci_uart_config(port_num, baud_rate, data_bits, stop_bits, parity, flow_ctl);
ESP_ERROR_CHECK(uart_set_pin(port_num, tx_pin, rx_pin, rts_pin, cts_pin));
return 0;
}
#endif //CONFIG_BT_LE_HCI_INTERFACE_USE_UART