Merge bluetooth branch to master
A smaller than usual merge, but with a couple of important fixes: - Fix UART TX stall condition (easily reproducible on Arduino 101) - Add missing BT_SECURITY_NONE security level - Fix missing ATT responses due to missing buffers - Consolidate ATT buffers into a smaller set of pools (2 instead of 4) - Fix missing error response for LE Connection oriented Channels ---------------------------------------------------------------- Carles Cufi (1): Bluetooth: uart: Fix UART TX stall condition Johan Hedberg (6): Bluetooth: samples/hci-uart: Remove unneeded hci_driver.h include Bluetooth: Move hci_driver.h to include/drivers/bluetooth Bluetooth: Add handling for security level 0 Bluetooth: samples/hci-uart: Fix up configuration files Bluetooth: samples/hci-uart: Fix minor style issues Bluetooth: Rename BT_SECURITY_ZERO to BT_SECURITY_NONE Luiz Augusto von Dentz (5): Bluetooth: ATT: Fix not responding when there is a request in parallel Bluetooth: ATT: Rework buffer pools to minimize extra data Bluetooth: GATT: Fix calling read handler twice Bluetooth: L2CAP: Add security check for LE CoC Bluetooth: shell: Add support for setting security level to l2cap_register drivers/bluetooth/hci/h4.c | 3 +- drivers/bluetooth/hci/h5.c | 3 +- include/bluetooth/att.h | 11 +- include/bluetooth/conn.h | 2 + include/bluetooth/l2cap.h | 3 + .../bluetooth/hci_driver.h | 0 net/bluetooth/Kconfig | 6 +- net/bluetooth/att.c | 72 +++++----- net/bluetooth/gatt.c | 9 ++ net/bluetooth/l2cap.c | 23 ++- net/bluetooth/l2cap_br.c | 24 +++- .../bluetooth/hci-uart/Makefile | 2 +- .../{prj.conf => generic.conf} | 4 - .../hci-uart/src/main.c | 18 +-- tests/bluetooth/shell/src/main.c | 9 +- .../src/bluetooth.c | 2 +- 16 files changed, 119 insertions(+), 72 deletions(-) rename include/{ => drivers}/bluetooth/hci_driver.h (100%) rename samples/bluetooth/hci-uart/{prj.conf => generic.conf} (62%) Change-Id: Idc225b46206d4d3f0a85918e5cf9e527fb407a1e Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
commit
03c71748f1
|
@ -144,7 +144,8 @@ static void bt_uart_isr(struct device *unused)
|
|||
} else {
|
||||
BT_DBG("spurious interrupt");
|
||||
}
|
||||
continue;
|
||||
/* Only the UART RX path is interrupt-enabled */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Beginning of a new packet */
|
||||
|
|
|
@ -438,7 +438,8 @@ static void bt_uart_isr(struct device *unused)
|
|||
} else {
|
||||
BT_DBG("spurious interrupt");
|
||||
}
|
||||
continue;
|
||||
/* Only the UART RX path is interrupt-enabled */
|
||||
break;
|
||||
}
|
||||
|
||||
ret = uart_fifo_read(h5_dev, &byte, sizeof(byte));
|
||||
|
|
|
@ -57,12 +57,13 @@ typedef void (*bt_att_destroy_t)(void *user_data);
|
|||
|
||||
/* ATT request context */
|
||||
struct bt_att_req {
|
||||
sys_snode_t node;
|
||||
bt_att_func_t func;
|
||||
bt_att_destroy_t destroy;
|
||||
struct net_buf *buf;
|
||||
sys_snode_t node;
|
||||
bt_att_func_t func;
|
||||
bt_att_destroy_t destroy;
|
||||
struct net_buf_simple_state state;
|
||||
struct net_buf *buf;
|
||||
#if defined(CONFIG_BLUETOOTH_SMP)
|
||||
bool retrying;
|
||||
bool retrying;
|
||||
#endif /* CONFIG_BLUETOOTH_SMP */
|
||||
};
|
||||
|
||||
|
|
|
@ -241,6 +241,8 @@ struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer,
|
|||
|
||||
/** Security level. */
|
||||
typedef enum __packed {
|
||||
/** Only for BR/EDR special cases, like SDP */
|
||||
BT_SECURITY_NONE,
|
||||
/** No encryption and no authentication. */
|
||||
BT_SECURITY_LOW,
|
||||
/** Encryption and no authentication (no MITM). */
|
||||
|
|
|
@ -206,6 +206,9 @@ struct bt_l2cap_server {
|
|||
/** Server PSM */
|
||||
uint16_t psm;
|
||||
|
||||
/** Required minimim security level */
|
||||
bt_security_t sec_level;
|
||||
|
||||
/** Server accept callback
|
||||
*
|
||||
* This callback is called whenever a new incoming connection requires
|
||||
|
|
|
@ -190,11 +190,11 @@ config BLUETOOTH_ATT_PREPARE_COUNT
|
|||
|
||||
config BLUETOOTH_ATT_REQ_COUNT
|
||||
int "Number of ATT request buffers"
|
||||
default 1
|
||||
default BLUETOOTH_MAX_CONN
|
||||
range 1 64
|
||||
help
|
||||
Number of outgoing buffers available for ATT requests per connection,
|
||||
this controls how many requests can be queued without blocking.
|
||||
Number of outgoing buffers available for ATT requests, this controls
|
||||
how many requests can be queued without blocking.
|
||||
|
||||
config BLUETOOTH_SMP
|
||||
bool "Security Manager Protocol support"
|
||||
|
|
|
@ -92,30 +92,20 @@ static struct bt_att bt_req_pool[CONFIG_BLUETOOTH_MAX_CONN];
|
|||
* Pool for outgoing ATT requests packets.
|
||||
*/
|
||||
static struct nano_fifo req_data;
|
||||
static NET_BUF_POOL(req_pool,
|
||||
CONFIG_BLUETOOTH_ATT_REQ_COUNT * CONFIG_BLUETOOTH_MAX_CONN,
|
||||
static NET_BUF_POOL(req_pool, CONFIG_BLUETOOTH_ATT_REQ_COUNT,
|
||||
BT_L2CAP_BUF_SIZE(CONFIG_BLUETOOTH_ATT_MTU),
|
||||
&req_data, NULL, BT_BUF_USER_DATA_MIN);
|
||||
|
||||
/*
|
||||
* Pool for ATT indications packets. This is required since indication can be
|
||||
* sent in parallel to requests.
|
||||
* Pool for ougoing ATT responses packets. This is necessary in order not to
|
||||
* block the RX fiber since otherwise req_pool would have be used but buffers
|
||||
* may only be freed after a response is received which would never happen if
|
||||
* the RX fiber is waiting a buffer causing a deadlock.
|
||||
*/
|
||||
static struct nano_fifo ind_data;
|
||||
static NET_BUF_POOL(ind_pool,
|
||||
CONFIG_BLUETOOTH_ATT_REQ_COUNT * CONFIG_BLUETOOTH_MAX_CONN,
|
||||
static struct nano_fifo rsp_data;
|
||||
static NET_BUF_POOL(rsp_pool, 1,
|
||||
BT_L2CAP_BUF_SIZE(CONFIG_BLUETOOTH_ATT_MTU),
|
||||
&ind_data, NULL, BT_BUF_USER_DATA_MIN);
|
||||
|
||||
/*
|
||||
* Pool for outstanding ATT request, this is required for resending in case
|
||||
* there is a recoverable error since the original buffer is changed while
|
||||
* sending.
|
||||
*/
|
||||
static struct nano_fifo clone_data;
|
||||
static NET_BUF_POOL(clone_pool, 1 * CONFIG_BLUETOOTH_MAX_CONN,
|
||||
BT_L2CAP_BUF_SIZE(CONFIG_BLUETOOTH_ATT_MTU),
|
||||
&clone_data, NULL, BT_BUF_USER_DATA_MIN);
|
||||
&rsp_data, NULL, BT_BUF_USER_DATA_MIN);
|
||||
|
||||
static void att_req_destroy(struct bt_att_req *req)
|
||||
{
|
||||
|
@ -199,32 +189,21 @@ static uint8_t att_mtu_req(struct bt_att *att, struct net_buf *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct net_buf *att_req_clone(struct net_buf *buf)
|
||||
{
|
||||
struct net_buf *clone;
|
||||
|
||||
clone = net_buf_get(&clone_data, net_buf_headroom(buf));
|
||||
if (!clone) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(net_buf_add(clone, buf->len), buf->data, buf->len);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
static int att_send_req(struct bt_att *att, struct bt_att_req *req)
|
||||
{
|
||||
BT_DBG("req %p", req);
|
||||
|
||||
att->req = req;
|
||||
|
||||
/* Save request state so it can be resent */
|
||||
net_buf_simple_save(&req->buf->b, &req->state);
|
||||
|
||||
/* Start timeout work */
|
||||
nano_delayed_work_submit(&att->timeout_work, ATT_TIMEOUT);
|
||||
|
||||
/* Send a clone to keep the original buffer intact */
|
||||
/* Keep a reference for resending in case of an error */
|
||||
bt_l2cap_send(att->chan.chan.conn, BT_L2CAP_CID_ATT,
|
||||
att_req_clone(req->buf));
|
||||
net_buf_ref(req->buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1530,6 +1509,9 @@ static uint8_t att_error_rsp(struct bt_att *att, struct net_buf *buf)
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* Restore state to be resent */
|
||||
net_buf_simple_restore(&att->req->buf->b, &att->req->state);
|
||||
|
||||
hdr = (void *)att->req->buf->data;
|
||||
|
||||
err = rsp->request == hdr->code ? rsp->error : BT_ATT_ERR_UNLIKELY;
|
||||
|
@ -1796,12 +1778,23 @@ struct net_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
|
|||
}
|
||||
|
||||
switch (op) {
|
||||
case BT_ATT_OP_INDICATE:
|
||||
case BT_ATT_OP_CONFIRM:
|
||||
/* Use a different buffer pool for indication/confirmations
|
||||
* since they can be sent in parallel.
|
||||
case BT_ATT_OP_ERROR_RSP:
|
||||
case BT_ATT_OP_MTU_RSP:
|
||||
case BT_ATT_OP_FIND_INFO_RSP:
|
||||
case BT_ATT_OP_FIND_TYPE_RSP:
|
||||
case BT_ATT_OP_READ_TYPE_RSP:
|
||||
case BT_ATT_OP_READ_RSP:
|
||||
case BT_ATT_OP_READ_BLOB_RSP:
|
||||
case BT_ATT_OP_READ_MULT_RSP:
|
||||
case BT_ATT_OP_READ_GROUP_RSP:
|
||||
case BT_ATT_OP_WRITE_RSP:
|
||||
case BT_ATT_OP_PREPARE_WRITE_RSP:
|
||||
case BT_ATT_OP_EXEC_WRITE_RSP:
|
||||
/* Use a different buffer pool for responses as this is
|
||||
* usually sent from RX fiber it shall never block.
|
||||
*/
|
||||
buf = bt_l2cap_create_pdu(&ind_data, 0);
|
||||
buf = bt_l2cap_create_pdu(&rsp_data, 0);
|
||||
break;
|
||||
default:
|
||||
buf = bt_l2cap_create_pdu(&req_data, 0);
|
||||
|
@ -1983,9 +1976,8 @@ void bt_att_init(void)
|
|||
.accept = bt_att_accept,
|
||||
};
|
||||
|
||||
net_buf_pool_init(ind_pool);
|
||||
net_buf_pool_init(req_pool);
|
||||
net_buf_pool_init(clone_pool);
|
||||
net_buf_pool_init(rsp_pool);
|
||||
#if CONFIG_BLUETOOTH_ATT_PREPARE_COUNT > 0
|
||||
net_buf_pool_init(prep_pool);
|
||||
#endif
|
||||
|
|
|
@ -1355,6 +1355,11 @@ static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Stop if no data left */
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Core Spec 4.2, Vol. 3, Part G, 4.8.1
|
||||
* If the Characteristic Value is greater than (ATT_MTU - 1) octets
|
||||
|
@ -1410,6 +1415,10 @@ static void att_read_multiple_rsp(struct bt_conn *conn, uint8_t err,
|
|||
|
||||
params->func(conn, 0, params, pdu, length);
|
||||
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* mark read as complete since read multiple is single response */
|
||||
params->func(conn, 0, params, NULL, 0);
|
||||
}
|
||||
|
|
|
@ -474,6 +474,13 @@ int bt_l2cap_server_register(struct bt_l2cap_server *server)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (server->sec_level > BT_SECURITY_FIPS) {
|
||||
return -EINVAL;
|
||||
} else if (server->sec_level < BT_SECURITY_LOW) {
|
||||
/* Level 0 is only applicable for BR/EDR */
|
||||
server->sec_level = BT_SECURITY_LOW;
|
||||
}
|
||||
|
||||
/* Check if given PSM is already in use */
|
||||
if (l2cap_server_lookup_psm(server->psm)) {
|
||||
BT_DBG("PSM already registered");
|
||||
|
@ -601,7 +608,13 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
|
|||
goto rsp;
|
||||
}
|
||||
|
||||
/* TODO: Add security check */
|
||||
#if defined(CONFIG_BLUETOOTH_SMP)
|
||||
/* Check if connection has minimum required security level */
|
||||
if (conn->sec_level < server->sec_level) {
|
||||
rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_AUTHENTICATION);
|
||||
goto rsp;
|
||||
}
|
||||
#endif /* CONFIG_BLUETOOTH_SMP */
|
||||
|
||||
if (!L2CAP_LE_CID_IS_DYN(scid)) {
|
||||
rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_INVALID_SCID);
|
||||
|
@ -624,6 +637,8 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
|
|||
goto rsp;
|
||||
}
|
||||
|
||||
chan->required_sec_level = server->sec_level;
|
||||
|
||||
if (l2cap_chan_add(conn, chan, l2cap_chan_destroy)) {
|
||||
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
|
||||
|
||||
|
@ -1348,6 +1363,12 @@ int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
|||
}
|
||||
#endif /* CONFIG_BLUETOOTH_BREDR */
|
||||
|
||||
if (chan->required_sec_level > BT_SECURITY_FIPS) {
|
||||
return -EINVAL;
|
||||
} else if (chan->required_sec_level == BT_SECURITY_NONE) {
|
||||
chan->required_sec_level = BT_SECURITY_LOW;
|
||||
}
|
||||
|
||||
return l2cap_le_connect(conn, BT_L2CAP_LE_CHAN(chan), psm);
|
||||
}
|
||||
|
||||
|
|
|
@ -708,9 +708,9 @@ l2cap_br_conn_security(struct bt_l2cap_chan *chan, const uint16_t psm)
|
|||
int check;
|
||||
|
||||
/* For SDP PSM there's no need to change existing security on link */
|
||||
if (psm == L2CAP_BR_PSM_SDP) {
|
||||
if (chan->required_sec_level == BT_SECURITY_NONE) {
|
||||
return L2CAP_CONN_SECURITY_PASSED;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* No link key needed for legacy devices (pre 2.1) and when low security
|
||||
|
@ -839,8 +839,8 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
|
|||
* Report security violation for non SDP channel without encryption when
|
||||
* remote supports SSP.
|
||||
*/
|
||||
if (psm != L2CAP_BR_PSM_SDP && BT_FEAT_HOST_SSP(conn->br.features) &&
|
||||
!conn->encrypt) {
|
||||
if (server->sec_level != BT_SECURITY_NONE &&
|
||||
BT_FEAT_HOST_SSP(conn->br.features) && !conn->encrypt) {
|
||||
result = BT_L2CAP_BR_ERR_SEC_BLOCK;
|
||||
goto done;
|
||||
}
|
||||
|
@ -865,6 +865,8 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
|
|||
goto done;
|
||||
}
|
||||
|
||||
chan->required_sec_level = server->sec_level;
|
||||
|
||||
l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy);
|
||||
BR_CHAN(chan)->tx.cid = scid;
|
||||
dcid = BR_CHAN(chan)->rx.cid;
|
||||
|
@ -973,6 +975,13 @@ int bt_l2cap_br_server_register(struct bt_l2cap_server *server)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (server->sec_level > BT_SECURITY_FIPS) {
|
||||
return -EINVAL;
|
||||
} else if (server->sec_level == BT_SECURITY_NONE &&
|
||||
server->psm != L2CAP_BR_PSM_SDP) {
|
||||
server->sec_level = BT_SECURITY_LOW;
|
||||
}
|
||||
|
||||
/* Check if given PSM is already in use */
|
||||
if (l2cap_br_server_lookup_psm(server->psm)) {
|
||||
BT_DBG("PSM already registered");
|
||||
|
@ -1361,6 +1370,13 @@ int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->required_sec_level > BT_SECURITY_FIPS) {
|
||||
return -EINVAL;
|
||||
} else if (chan->required_sec_level == BT_SECURITY_NONE &&
|
||||
psm != L2CAP_BR_PSM_SDP) {
|
||||
chan->required_sec_level = BT_SECURITY_LOW;
|
||||
}
|
||||
|
||||
switch (chan->state) {
|
||||
case BT_L2CAP_CONNECTED:
|
||||
/* Already connected */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
KERNEL_TYPE = unified
|
||||
CONF_FILE ?= prj.conf
|
||||
CONF_FILE ?= nrf5.conf
|
||||
BOARD ?= nrf52_pca10040
|
||||
|
||||
include $(ZEPHYR_BASE)/Makefile.inc
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
CONFIG_CONSOLE=n
|
||||
CONFIG_STDOUT_CONSOLE=n
|
||||
CONFIG_UART_CONSOLE=n
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||
CONFIG_BLUETOOTH=y
|
||||
CONFIG_BLUETOOTH_LE=y
|
||||
CONFIG_BLUETOOTH_STACK_HCI_RAW=y
|
||||
CONFIG_BLUETOOTH_DEBUG_LOG=n
|
||||
CONFIG_BLUETOOTH_DEBUG_MONITOR=n
|
|
@ -33,14 +33,13 @@
|
|||
#include <net/buf.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_driver.h>
|
||||
#include <bluetooth/buf.h>
|
||||
#include <bluetooth/hci_raw.h>
|
||||
|
||||
static struct device *hci_uart_dev;
|
||||
|
||||
#define STACK_SIZE 1024
|
||||
uint8_t tx_fiber_stack[STACK_SIZE];
|
||||
#define STACK_SIZE 1024
|
||||
static uint8_t tx_fiber_stack[STACK_SIZE];
|
||||
|
||||
/* HCI command buffers */
|
||||
#define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_SEND_RESERVE + \
|
||||
|
@ -64,10 +63,10 @@ static NET_BUF_POOL(acl_tx_pool, CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS,
|
|||
|
||||
static struct nano_fifo tx_queue;
|
||||
|
||||
#define H4_CMD 0x01
|
||||
#define H4_ACL 0x02
|
||||
#define H4_SCO 0x03
|
||||
#define H4_EVT 0x04
|
||||
#define H4_CMD 0x01
|
||||
#define H4_ACL 0x02
|
||||
#define H4_SCO 0x03
|
||||
#define H4_EVT 0x04
|
||||
|
||||
/* Length of a discard/flush buffer.
|
||||
* This is sized to align with a BLE HCI packet:
|
||||
|
@ -76,7 +75,7 @@ static struct nano_fifo tx_queue;
|
|||
* variable, smaller ones will force the caller to call into discard more
|
||||
* often.
|
||||
*/
|
||||
#define H4_DISCARD_LEN 33
|
||||
#define H4_DISCARD_LEN 33
|
||||
|
||||
static int h4_read(struct device *uart, uint8_t *buf,
|
||||
size_t len, size_t min)
|
||||
|
@ -174,7 +173,8 @@ static void bt_uart_isr(struct device *unused)
|
|||
} else {
|
||||
SYS_LOG_DBG("spurious interrupt");
|
||||
}
|
||||
continue;
|
||||
/* Only the UART RX path is interrupt-enabled */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Beginning of a new packet */
|
||||
|
|
|
@ -1809,11 +1809,16 @@ static int cmd_l2cap_register(int argc, char *argv[])
|
|||
|
||||
server.psm = strtoul(argv[1], NULL, 16);
|
||||
|
||||
if (argc > 2) {
|
||||
server.sec_level = strtoul(argv[2], NULL, 10);
|
||||
}
|
||||
|
||||
if (bt_l2cap_server_register(&server) < 0) {
|
||||
printk("Unable to register psm\n");
|
||||
server.psm = 0;
|
||||
} else {
|
||||
printk("L2CAP psm %u registered\n", server.psm);
|
||||
printk("L2CAP psm %u sec_level %u registered\n", server.psm,
|
||||
server.sec_level);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2189,7 +2194,7 @@ static const struct shell_cmd commands[] = {
|
|||
{ "hrs-simulate", cmd_hrs_simulate,
|
||||
"register and simulate Heart Rate Service <value: on, off>" },
|
||||
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
|
||||
{ "l2cap-register", cmd_l2cap_register, "<psm>" },
|
||||
{ "l2cap-register", cmd_l2cap_register, "<psm> [sec_level]" },
|
||||
{ "l2cap-connect", cmd_l2cap_connect, "<psm>" },
|
||||
{ "l2cap-disconnect", cmd_l2cap_disconnect, HELP_NONE },
|
||||
{ "l2cap-send", cmd_l2cap_send, "<number of packets>" },
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <tc_util.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci_driver.h>
|
||||
#include <drivers/bluetooth/hci_driver.h>
|
||||
|
||||
#define EXPECTED_ERROR -ENOSYS
|
||||
|
||||
|
|
Loading…
Reference in a new issue