zephyr/subsys/bluetooth/host/hci_raw.c
Joakim Andersson 6483e12a8a Bluetooth: Refactor bluetooth buffer configuration for simplification
Refactor and simplify the bluetooth buffer configurations to improve the
easy of configurations and eliminate invalid ones.
By moving configurations out of host and controller specific
configurations and into a common one it becomes easier to configure
the host and controller separately as the same configurations can be
used as would be for a combined build.

All HCI configurations are now given exluding the matching HCI header,
which eases the configuration as the application don't have to know the
different header sizes.
The BT_RX_BUF_LEN is split into ACL and Event, as well as the suprising
use of Command size.
BT_L2CAP_RX_MTU is removed as the stack does not support reassembling of
HCI ACL data to larger L2CAP PDUs. The application will have to set
ACL RX size and account for the L2CAP PDU header itself.
BT_EATT_RX_MTU was removed as it is only used for setting a different
default value for another option which leads to the stuck kconfig symbol
problem.

The configurations can be updated according to the table below:

** New configuration         | ** Old configuration
All configurations
BT_BUF_ACL_RX_SIZE           | BT_L2CAP_RX_MTU + 4
BT_BUF_ACL_RX_SIZE           | BT_RX_BUF_LEN - 4
BT_BUF_EVT_RX_SIZE           | BT_RX_BUF_LEN - 2
BT_BUF_CMD_TX_SIZE           | BT_RX_BUF_LEN - 3
BT_BUF_CMD_TX_COUNT          | BT_HCI_CMD_COUNT
BT_BUF_EVT_RX_COUNT          | BT_RX_BUF_COUNT
BT_BUF_ACL_RX_COUNT          | BT_RX_BUF_COUNT
BT_BUF_ACL_RX_COUNT          | BT_ACL_RX_COUNT
BT_BUF_EVT_DISCARDABLE_SIZE  | BT_DISCARDABLE_BUF_SIZE - 2
BT_BUF_EVT_DISCARDABLE_COUNT | BT_DISCARDABLE_BUF_COUNT
Controller-build
BT_BUF_ACL_TX_SIZE           | BT_CTLR_TX_BUFFERS_SIZE
BT_BUF_ACL_TX_COUNT          | BT_CTLR_TX_BUFFER
HCI-bridge
BT_BUF_ACL_TX_SIZE           | BT_HCI_ACL_DATA_SIZE
BT_BUF_ACL_TX_COUNT          | 6

Fixed invalid configurations setting either BT_L2CAP_RX_MTU or
BT_CTLR_DATA_LENGTH_MAX larger than BT_RX_BUF_LEN could lead to buffer
overruns.

Fix advertising report max data length calculation.
This always used the BT_DISCARDABLE_BUF_SIZE macro but this feature
can be turned off and advertising reports will be allocated from the RX
buffer in that case. Also controller-build does not have this buffer
(in hci_raw.c). Also the wrong HCI header was used in the calculation,
HCI event header should have been used instead of HCI ACL header.

Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2021-05-06 14:56:18 +02:00

380 lines
7.3 KiB
C

/* hci_userchan.c - HCI user channel Bluetooth handling */
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <sys/atomic.h>
#include <sys/byteorder.h>
#include <drivers/bluetooth/hci_driver.h>
#include <bluetooth/buf.h>
#include <bluetooth/hci_raw.h>
#include <bluetooth/l2cap.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
#define LOG_MODULE_NAME bt_hci_raw
#include "common/log.h"
#include "hci_ecc.h"
#include "monitor.h"
#include "hci_raw_internal.h"
#define H4_CMD 0x01
#define H4_ACL 0x02
#define H4_SCO 0x03
#define H4_EVT 0x04
#define H4_ISO 0x05
static struct k_fifo *raw_rx;
#if defined(CONFIG_BT_HCI_RAW_H4_ENABLE)
static uint8_t raw_mode = BT_HCI_RAW_MODE_H4;
#else
static uint8_t raw_mode;
#endif
#define BT_BUF_RX_COUNT MAX(CONFIG_BT_BUF_EVT_RX_COUNT, CONFIG_BT_BUF_ACL_RX_COUNT)
NET_BUF_POOL_FIXED_DEFINE(hci_rx_pool, BT_BUF_RX_COUNT,
BT_BUF_RX_SIZE, NULL);
NET_BUF_POOL_FIXED_DEFINE(hci_cmd_pool, CONFIG_BT_BUF_CMD_TX_COUNT,
BT_BUF_CMD_SIZE(CONFIG_BT_BUF_CMD_TX_SIZE), NULL);
NET_BUF_POOL_FIXED_DEFINE(hci_acl_pool, CONFIG_BT_BUF_ACL_TX_COUNT,
BT_BUF_ACL_SIZE(CONFIG_BT_BUF_ACL_TX_SIZE), NULL);
#if defined(CONFIG_BT_ISO)
NET_BUF_POOL_FIXED_DEFINE(hci_iso_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
CONFIG_BT_ISO_TX_MTU, NULL);
#endif /* CONFIG_BT_ISO */
struct bt_dev_raw bt_dev;
struct bt_hci_raw_cmd_ext *cmd_ext;
static size_t cmd_ext_size;
int bt_hci_driver_register(const struct bt_hci_driver *drv)
{
if (bt_dev.drv) {
return -EALREADY;
}
if (!drv->open || !drv->send) {
return -EINVAL;
}
bt_dev.drv = drv;
BT_DBG("Registered %s", drv->name ? drv->name : "");
bt_monitor_new_index(BT_MONITOR_TYPE_PRIMARY, drv->bus,
BT_ADDR_ANY, drv->name ? drv->name : "bt0");
return 0;
}
struct net_buf *bt_buf_get_rx(enum bt_buf_type type, k_timeout_t timeout)
{
struct net_buf *buf;
switch (type) {
case BT_BUF_EVT:
case BT_BUF_ACL_IN:
case BT_BUF_ISO_IN:
break;
default:
BT_ERR("Invalid rx type: %u", type);
return NULL;
}
buf = net_buf_alloc(&hci_rx_pool, timeout);
if (!buf) {
return buf;
}
net_buf_reserve(buf, BT_BUF_RESERVE);
bt_buf_set_type(buf, type);
return buf;
}
struct net_buf *bt_buf_get_tx(enum bt_buf_type type, k_timeout_t timeout,
const void *data, size_t size)
{
struct net_buf_pool *pool;
struct net_buf *buf;
switch (type) {
case BT_BUF_CMD:
pool = &hci_cmd_pool;
break;
case BT_BUF_ACL_OUT:
pool = &hci_acl_pool;
break;
#if defined(CONFIG_BT_ISO)
case BT_BUF_ISO_OUT:
pool = &hci_iso_pool;
break;
#endif /* CONFIG_BT_ISO */
case BT_BUF_H4:
if (IS_ENABLED(CONFIG_BT_HCI_RAW_H4) &&
raw_mode == BT_HCI_RAW_MODE_H4) {
switch (((uint8_t *)data)[0]) {
case H4_CMD:
type = BT_BUF_CMD;
pool = &hci_cmd_pool;
break;
case H4_ACL:
type = BT_BUF_ACL_OUT;
pool = &hci_acl_pool;
break;
#if defined(CONFIG_BT_ISO)
case H4_ISO:
type = BT_BUF_ISO_OUT;
pool = &hci_iso_pool;
break;
#endif /* CONFIG_BT_ISO */
default:
LOG_ERR("Unknown H4 type %u", type);
return NULL;
}
/* Adjust data pointer to discard the header */
data = (uint8_t *)data + 1;
size--;
break;
}
__fallthrough;
default:
BT_ERR("Invalid tx type: %u", type);
return NULL;
}
buf = net_buf_alloc(pool, timeout);
if (!buf) {
return buf;
}
net_buf_reserve(buf, BT_BUF_RESERVE);
bt_buf_set_type(buf, type);
if (data && size) {
net_buf_add_mem(buf, data, size);
}
return buf;
}
struct net_buf *bt_buf_get_cmd_complete(k_timeout_t timeout)
{
return bt_buf_get_rx(BT_BUF_EVT, timeout);
}
struct net_buf *bt_buf_get_evt(uint8_t evt, bool discardable, k_timeout_t timeout)
{
return bt_buf_get_rx(BT_BUF_EVT, timeout);
}
int bt_recv(struct net_buf *buf)
{
BT_DBG("buf %p len %u", buf, buf->len);
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
if (IS_ENABLED(CONFIG_BT_HCI_RAW_H4) &&
raw_mode == BT_HCI_RAW_MODE_H4) {
switch (bt_buf_get_type(buf)) {
case BT_BUF_EVT:
net_buf_push_u8(buf, H4_EVT);
break;
case BT_BUF_ACL_IN:
net_buf_push_u8(buf, H4_ACL);
break;
case BT_BUF_ISO_IN:
if (IS_ENABLED(CONFIG_BT_ISO)) {
net_buf_push_u8(buf, H4_ISO);
break;
}
__fallthrough;
default:
BT_ERR("Unknown type %u", bt_buf_get_type(buf));
return -EINVAL;
}
}
/* Queue to RAW rx queue */
net_buf_put(raw_rx, buf);
return 0;
}
int bt_recv_prio(struct net_buf *buf)
{
if (bt_buf_get_type(buf) == BT_BUF_EVT) {
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
uint8_t evt_flags = bt_hci_evt_get_flags(hdr->evt);
if ((evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO) &&
(evt_flags & BT_HCI_EVT_FLAG_RECV)) {
/* Avoid queuing the event twice */
return 0;
}
}
return bt_recv(buf);
}
static void bt_cmd_complete_ext(uint16_t op, uint8_t status)
{
struct net_buf *buf;
struct bt_hci_evt_cc_status *cc;
if (status == BT_HCI_ERR_EXT_HANDLED) {
return;
}
buf = bt_hci_cmd_complete_create(op, sizeof(*cc));
cc = net_buf_add(buf, sizeof(*cc));
cc->status = status;
bt_recv(buf);
}
static uint8_t bt_send_ext(struct net_buf *buf)
{
struct bt_hci_cmd_hdr *hdr;
struct net_buf_simple_state state;
int i;
uint16_t op;
uint8_t status;
status = BT_HCI_ERR_SUCCESS;
if (!cmd_ext) {
return status;
}
net_buf_simple_save(&buf->b, &state);
if (buf->len < sizeof(*hdr)) {
BT_ERR("No HCI Command header");
return BT_HCI_ERR_INVALID_PARAM;
}
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
if (buf->len < hdr->param_len) {
BT_ERR("Invalid HCI CMD packet length");
return BT_HCI_ERR_INVALID_PARAM;
}
op = sys_le16_to_cpu(hdr->opcode);
for (i = 0; i < cmd_ext_size; i++) {
struct bt_hci_raw_cmd_ext *cmd = &cmd_ext[i];
if (cmd->op == op) {
if (buf->len < cmd->min_len) {
status = BT_HCI_ERR_INVALID_PARAM;
} else {
status = cmd->func(buf);
}
break;
}
}
if (status) {
bt_cmd_complete_ext(op, status);
return status;
}
net_buf_simple_restore(&buf->b, &state);
return status;
}
int bt_send(struct net_buf *buf)
{
BT_DBG("buf %p len %u", buf, buf->len);
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
if (IS_ENABLED(CONFIG_BT_HCI_RAW_CMD_EXT) &&
bt_buf_get_type(buf) == BT_BUF_CMD) {
uint8_t status;
status = bt_send_ext(buf);
if (status) {
return status;
}
}
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
return bt_hci_ecc_send(buf);
}
return bt_dev.drv->send(buf);
}
int bt_hci_raw_set_mode(uint8_t mode)
{
BT_DBG("mode %u", mode);
if (IS_ENABLED(CONFIG_BT_HCI_RAW_H4)) {
switch (mode) {
case BT_HCI_RAW_MODE_PASSTHROUGH:
case BT_HCI_RAW_MODE_H4:
raw_mode = mode;
return 0;
}
}
return -EINVAL;
}
uint8_t bt_hci_raw_get_mode(void)
{
if (IS_ENABLED(CONFIG_BT_HCI_RAW_H4)) {
return raw_mode;
}
return BT_HCI_RAW_MODE_PASSTHROUGH;
}
void bt_hci_raw_cmd_ext_register(struct bt_hci_raw_cmd_ext *cmds, size_t size)
{
if (IS_ENABLED(CONFIG_BT_HCI_RAW_CMD_EXT)) {
cmd_ext = cmds;
cmd_ext_size = size;
}
}
int bt_enable_raw(struct k_fifo *rx_queue)
{
const struct bt_hci_driver *drv = bt_dev.drv;
int err;
BT_DBG("");
raw_rx = rx_queue;
if (!bt_dev.drv) {
BT_ERR("No HCI driver registered");
return -ENODEV;
}
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
bt_hci_ecc_init();
}
err = drv->open();
if (err) {
BT_ERR("HCI driver open failed (%d)", err);
return err;
}
BT_INFO("Bluetooth enabled in RAW mode");
return 0;
}