feat(ble_audio): Miscellaneous update for verifying with PTS cases

This commit is contained in:
Liu Linyan
2026-08-28 11:23:15 +08:00
parent 43e6da1796
commit de84e7398b
80 changed files with 2751 additions and 422 deletions
+2 -2
View File
@@ -80,7 +80,7 @@ if BT_ISO
config BT_ISO_MAX_CIG
int "Maximum number of Connected Isochronous Groups (CIGs) to support"
range 1 1 if BT_BLUEDROID_ENABLED
range 1 2 if BT_BLUEDROID_ENABLED
range 1 BT_NIMBLE_ISO_CIG if BT_NIMBLE_ENABLED
default 1 if BT_BLUEDROID_ENABLED
default BT_NIMBLE_ISO_CIG if BT_NIMBLE_ENABLED
@@ -94,7 +94,7 @@ if BT_ISO
config BT_ISO_MAX_BIG
int "Maximum number of Broadcast Isochronous Groups (BIGs) to support"
range 1 1 if BT_BLUEDROID_ENABLED
range 1 2 if BT_BLUEDROID_ENABLED
range 1 BT_NIMBLE_ISO_BIG if BT_NIMBLE_ENABLED
default 1 if BT_BLUEDROID_ENABLED
default BT_NIMBLE_ISO_BIG if BT_NIMBLE_ENABLED
@@ -41,8 +41,10 @@ esp_err_t esp_ble_iso_data_parse(const uint8_t ltv[], size_t size,
continue;
}
/* A callback returning false aborts parsing; report it as an error so
* callers detect it (bt_audio_data_parse() returns -ECANCELED here). */
if (func(type, &ltv[i + 2], data_len, user_data) == false) {
return ESP_OK;
return ESP_FAIL;
}
i += (size_t)len + 1;
@@ -233,7 +235,8 @@ esp_err_t esp_ble_iso_big_ext_adv_add(esp_ble_iso_ext_adv_info_t *info)
return ESP_ERR_INVALID_ARG;
}
err = bt_le_ext_adv_new_safe(info->adv_handle);
err = bt_le_ext_adv_new_safe(info->adv_handle, info->addr_type,
info->addr, info->sid);
if (err) {
return ESP_FAIL;
}
@@ -287,6 +287,9 @@ typedef struct bt_iso_tx_cb_info esp_ble_iso_tx_cb_info_t;
/** Extended advertising information structure */
typedef struct {
uint8_t adv_handle; /*!< Handle for the advertising set */
uint8_t addr_type; /*!< Advertising address type (0=public, 1=random) */
uint8_t addr[6]; /*!< Advertising address */
uint8_t sid; /*!< Advertising SID */
} esp_ble_iso_ext_adv_info_t;
/**
@@ -13,6 +13,7 @@
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/buf.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/common/bt_str.h>
#include <../host/hci_core.h>
#include <../host/iso_internal.h>
@@ -104,6 +105,46 @@ static void gap_app_cb(tBTA_DM_BLE_5_GAP_EVENT event, tBTA_DM_BLE_5_GAP_CB_PARAM
btc_ble_5_gap_callback(event, params);
}
/* Copy this peer's bonded LTK (16B) into out_ltk; true if found. Used on AUTH_CMPL as
* the CSIS SIRK key. The store exposes only PENC, but under LE SC PENC==LENC==link LTK.
* esp_ble_get_bond_device_list reads btc_storage synchronously — safe in this callback. */
static bool bd_read_bonded_ltk(const uint8_t *addr, uint8_t out_ltk[16])
{
esp_ble_bond_dev_t *list;
bool found = false;
int num;
num = esp_ble_get_bond_device_num();
if (num <= 0) {
return false;
}
list = calloc(num, sizeof(*list));
if (list == NULL) {
LOG_ERR("[B]LtkListAllocFail[%d]", num);
return false;
}
if (esp_ble_get_bond_device_list(&num, list) == ESP_OK) {
for (int i = 0; i < num; i++) {
if (memcmp(list[i].bd_addr, addr, sizeof(esp_bd_addr_t)) == 0) {
/* Identity-only bonds carry no LTK; penc_key would be zeroed. */
if (list[i].bond_key.key_mask & ESP_BLE_ENC_KEY_MASK) {
memcpy(out_ltk, list[i].bond_key.penc_key.ltk, 16);
found = true;
}
break;
}
}
}
if (list != NULL) {
free(list);
}
return found;
}
void bt_le_bluedroid_gap_post_event(uint16_t event, void *param)
{
const esp_ble_gap_cb_param_t *p = param;
@@ -157,7 +198,16 @@ void bt_le_bluedroid_gap_post_event(uint16_t event, void *param)
qev->security_change.sec_level = sec_level;
qev->security_change.bonded = (a->auth_mode & ESP_LE_AUTH_BOND) ? 1 : 0;
/* Attach the bonded LTK (CSIS SIRK-encryption key K). */
if (bd_read_bonded_ltk(a->bd_addr, qev->security_change.ltk)) {
qev->security_change.ltk_present = 1;
LOG_INF("[B]LtkFromStore[%u]", qev->security_change.conn_handle);
} else {
LOG_WRN("[B]NoLtkForEnc[%u]", qev->security_change.conn_handle);
}
}
break;
}
@@ -237,7 +287,8 @@ static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
LOG_WRN("[B]PaSyncEstabOverwrite[%04x->%04x]",
active_pa_sync_handle, e->sync_handle);
} else {
LOG_INF("[B]PaSyncEstab[%04x]", e->sync_handle);
LOG_INF("[B]PaSyncEstab[%04x][%s]",
e->sync_handle, bt_hex(e->adv_addr, BT_ADDR_SIZE));
}
active_pa_sync_handle = e->sync_handle;
}
@@ -236,16 +236,26 @@ static void gatts_notify_list_drain(struct gatt_conn *gatt_conn)
}
}
/* Rewrite a slot's conn_id to the GATTS/GATTC gatt_if (low byte); the BTA index
* (high byte) is shared on one ACL. Lets a dual-role link op on the right conn_id. */
static inline uint16_t to_gatts_conn_id(uint16_t conn_id)
{
return BTC_GATT_CREATE_CONN_ID(gatts_if, BTC_GATT_GET_CONN_ID(conn_id));
}
static inline uint16_t to_gattc_conn_id(uint16_t conn_id)
{
return BTC_GATT_CREATE_CONN_ID(gattc_if, BTC_GATT_GET_CONN_ID(conn_id));
}
static void gatts_indicate_dispatch(struct bt_conn *conn,
uint16_t conn_id,
struct gatts_list_node *n)
{
uint16_t conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn->handle);
LOG_DBG("[B]GattsIndDispatch[%u][%u]", conn->handle, n->value_handle);
BTA_GATTS_HandleValueIndication(conn_id, n->value_handle,
n->params_copy.len,
n->data_copy, true);
BTA_GATTS_HandleValueIndication(to_gatts_conn_id(conn_id), n->value_handle,
n->params_copy.len, n->data_copy, true);
}
/* Silent drain — used by reset_gatt_conn as a safety net. The disconnect
@@ -348,11 +358,58 @@ struct gatt_conn *bt_le_bluedroid_find_free_gatt_conn(void)
return NULL;
}
/* Exact slot lookup by BTA conn_id (full gatt_if + connection index). */
static struct gatt_conn *find_gatt_conn_with_conn_id(uint16_t conn_id)
{
for (size_t i = 0; i < ARRAY_SIZE(gatt_conns); i++) {
if (gatt_conns[i].used && gatt_conns[i].conn_id == conn_id) {
return &gatt_conns[i];
}
}
return NULL;
}
/* Slot lookup for a BTA request/completion: exact conn_id, else fall back to the
* connection index (high byte) so a dual-role link resolves either gatt_if. */
static struct gatt_conn *find_gatt_conn_by_conn_id_or_index(uint16_t conn_id)
{
struct gatt_conn *gatt_conn = find_gatt_conn_with_conn_id(conn_id);
if (gatt_conn == NULL) {
for (size_t i = 0; i < ARRAY_SIZE(gatt_conns); i++) {
if (gatt_conns[i].used &&
BTC_GATT_GET_CONN_ID(gatt_conns[i].conn_id) == BTC_GATT_GET_CONN_ID(conn_id)) {
gatt_conn = &gatt_conns[i];
break;
}
}
}
return gatt_conn;
}
/* Map an ACL conn_handle to the BTA conn_id for BTA_GATT[CS]_* calls. Returns
* false if the link is already torn down (slot gone) — caller skips the BTA op. */
static bool gatt_conn_id_lookup(uint16_t conn_handle, uint16_t *conn_id)
{
struct gatt_conn *gatt_conn;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(conn_handle);
if (gatt_conn == NULL) {
return false;
}
*conn_id = gatt_conn->conn_id;
return true;
}
static void reset_gatt_conn(struct gatt_conn *gatt_conn)
{
gattc_list_drain(gatt_conn);
gatts_list_drain(gatt_conn);
gatts_notify_list_drain(gatt_conn);
free(gatt_conn->prep_buf); /* free(NULL) is safe when no long write was in flight */
memset(gatt_conn, 0, sizeof(*gatt_conn));
gatt_conn->conn_handle = UINT16_MAX;
}
@@ -392,7 +449,8 @@ static void gattc_connect_event_handler(tBTA_GATTC_CONNECT *connect)
qev->type = BT_LE_GATTC_CONNECT_EVENT;
qev->gattc_connect.conn_handle = BTC_GATT_GET_CONN_ID(connect->conn_id);
qev->gattc_connect.conn_handle = connect->conn_handle;
qev->gattc_connect.conn_id = connect->conn_id;
qev->gattc_connect.role = connect->link_role;
qev->gattc_connect.peer.type = connect->ble_addr_type;
memcpy(qev->gattc_connect.peer.val, connect->remote_bda, BT_ADDR_SIZE);
@@ -414,7 +472,7 @@ static void gattc_disconnect_event_handler(tBTA_GATTC_DISCONNECT *disconnect)
qev->type = BT_LE_GATTC_DISCONNECT_EVENT;
qev->gattc_disconnect.conn_handle = BTC_GATT_GET_CONN_ID(disconnect->conn_id);
qev->gattc_disconnect.conn_id = disconnect->conn_id;
qev->gattc_disconnect.reason = bta_disconn_reason_to_hci(disconnect->reason);
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
@@ -435,7 +493,7 @@ static void gattc_open_event_handler(tBTA_GATTC_OPEN *open)
qev->type = BT_LE_GATTC_OPEN_EVENT;
qev->gattc_open.status = open->status;
qev->gattc_open.conn_handle = BTC_GATT_GET_CONN_ID(open->conn_id);
qev->gattc_open.conn_id = open->conn_id;
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
if (err) {
@@ -455,7 +513,7 @@ static void gattc_mtu_event_handler(tBTA_GATTC_CFG_MTU *cfg_mtu)
qev->type = BT_LE_GATTC_MTU_EVENT;
qev->gattc_mtu.status = cfg_mtu->status;
qev->gattc_mtu.conn_handle = BTC_GATT_GET_CONN_ID(cfg_mtu->conn_id);
qev->gattc_mtu.conn_id = cfg_mtu->conn_id;
qev->gattc_mtu.mtu = cfg_mtu->mtu;
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
@@ -476,7 +534,7 @@ static void gattc_disc_cmpl_event_handler(tBTA_GATTC_DIS_CMPL *disc_cmpl)
qev->type = BT_LE_GATTC_DISC_CMPL_EVENT;
qev->gattc_disc_cmpl.status = disc_cmpl->status;
qev->gattc_disc_cmpl.conn_handle = BTC_GATT_GET_CONN_ID(disc_cmpl->conn_id);
qev->gattc_disc_cmpl.conn_id = disc_cmpl->conn_id;
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
if (err) {
@@ -496,7 +554,7 @@ static void gattc_read_chrc_event_handler(tBTA_GATTC_READ *read)
qev->type = BT_LE_GATTC_READ_CHRC_EVENT;
qev->gattc_read_chrc.status = read->status;
qev->gattc_read_chrc.conn_handle = BTC_GATT_GET_CONN_ID(read->conn_id);
qev->gattc_read_chrc.conn_id = read->conn_id;
qev->gattc_read_chrc.attr_handle = read->handle;
if (read->p_value &&
@@ -531,7 +589,7 @@ static void gattc_write_chrc_event_handler(tBTA_GATTC_WRITE *write)
qev->type = BT_LE_GATTC_WRITE_CHRC_EVENT;
qev->gattc_write_chrc.status = write->status;
qev->gattc_write_chrc.conn_handle = BTC_GATT_GET_CONN_ID(write->conn_id);
qev->gattc_write_chrc.conn_id = write->conn_id;
qev->gattc_write_chrc.attr_handle = write->handle;
qev->gattc_write_chrc.offset = write->offset;
@@ -558,7 +616,7 @@ static void gatts_notify_tx_event_handler(tBTA_GATTS_REQ *req)
* list first (see gatts_notify_enqueue / handle_gatts_notify_tx_event),
* so is_notify here is left unused on this path. */
qev->gatts_notify_tx.is_notify = false;
qev->gatts_notify_tx.conn_handle = BTC_GATT_GET_CONN_ID(req->conn_id);
qev->gatts_notify_tx.conn_id = req->conn_id;
qev->gatts_notify_tx.attr_handle = req->handle;
qev->gatts_notify_tx.status = req->status;
@@ -580,7 +638,7 @@ static void gattc_notify_rx_event_handler(tBTA_GATTC_NOTIFY *notify)
qev->type = BT_LE_GATTC_NOTIFY_RX_EVENT;
qev->gattc_notify_rx.is_notify = notify->is_notify;
qev->gattc_notify_rx.conn_handle = BTC_GATT_GET_CONN_ID(notify->conn_id);
qev->gattc_notify_rx.conn_id = notify->conn_id;
qev->gattc_notify_rx.attr_handle = notify->handle;
if (notify->len) {
@@ -612,7 +670,8 @@ static void gatts_connect_event_handler(tBTA_GATTS_CONN *connect)
qev->type = BT_LE_GATTS_CONNECT_EVENT;
qev->gatts_connect.conn_handle = BTC_GATT_GET_CONN_ID(connect->conn_id);
qev->gatts_connect.conn_handle = connect->conn_handle;
qev->gatts_connect.conn_id = connect->conn_id;
qev->gatts_connect.role = connect->link_role;
qev->gatts_connect.peer.type = connect->ble_addr_type;
memcpy(qev->gatts_connect.peer.val, connect->remote_bda, BT_ADDR_SIZE);
@@ -634,7 +693,7 @@ static void gatts_disconnect_event_handler(tBTA_GATTS_CONN *disconnect)
qev->type = BT_LE_GATTS_DISCONNECT_EVENT;
qev->gatts_disconnect.conn_handle = BTC_GATT_GET_CONN_ID(disconnect->conn_id);
qev->gatts_disconnect.conn_id = disconnect->conn_id;
qev->gatts_disconnect.reason = bta_disconn_reason_to_hci(disconnect->reason);
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
@@ -655,7 +714,7 @@ static void gatts_mtu_event_handler(tBTA_GATTS_REQ *req)
qev->type = BT_LE_GATTS_MTU_EVENT;
qev->gatts_mtu.conn_handle = BTC_GATT_GET_CONN_ID(req->conn_id);
qev->gatts_mtu.conn_id = req->conn_id;
qev->gatts_mtu.mtu = req->p_data->mtu;
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
@@ -675,7 +734,7 @@ static void gatts_read_req_handler(tBTA_GATTS_REQ *req)
qev->type = BT_LE_GATTS_READ_EVENT;
qev->gatts_read.conn_handle = BTC_GATT_GET_CONN_ID(req->conn_id);
qev->gatts_read.conn_id = req->conn_id;
qev->gatts_read.trans_id = req->trans_id;
memcpy(qev->gatts_read.peer, req->remote_bda, BT_ADDR_SIZE);
qev->gatts_read.attr_handle = req->p_data->read_req.handle;
@@ -701,7 +760,7 @@ static void gatts_write_req_handler(tBTA_GATTS_REQ *req)
qev->type = BT_LE_GATTS_WRITE_EVENT;
qev->gatts_write.conn_handle = BTC_GATT_GET_CONN_ID(req->conn_id);
qev->gatts_write.conn_id = req->conn_id;
qev->gatts_write.trans_id = req->trans_id;
memcpy(qev->gatts_write.peer, req->remote_bda, BT_ADDR_SIZE);
qev->gatts_write.attr_handle = req->p_data->write_req.handle;
@@ -728,6 +787,27 @@ static void gatts_write_req_handler(tBTA_GATTS_REQ *req)
}
}
static void gatts_exec_write_req_handler(tBTA_GATTS_REQ *req)
{
struct bt_le_gatt_event_param *qev;
int err;
qev = calloc(1, sizeof(*qev));
assert(qev);
qev->type = BT_LE_GATTS_EXEC_WRITE_EVENT;
qev->gatts_exec_write.conn_id = req->conn_id;
qev->gatts_exec_write.trans_id = req->trans_id;
qev->gatts_exec_write.exec = req->p_data &&
req->p_data->exec_write == GATT_PREP_WRITE_EXEC;
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
if (err) {
LOG_ERR("[B]GattsExecPostFail[%d]", err);
free(qev);
}
}
static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
{
switch (event) {
@@ -774,12 +854,6 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
break;
}
if (BTC_GATT_GET_CONN_ID(p_data->conn.conn_id) != p_data->conn.conn_handle) {
LOG_ERR("[B]GattsConnHdlMismatch[%u][%u]",
BTC_GATT_GET_CONN_ID(p_data->conn.conn_id), p_data->conn.conn_handle);
break;
}
gatts_connect_event_handler(&p_data->conn);
break;
@@ -983,6 +1057,14 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
if (p_data->req_data.p_data) {
LOG_DBG("[B]GattsExecFlag[%u]", p_data->req_data.p_data->exec_write);
}
if (BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id) != gatts_if) {
LOG_ERR("[B]GattsExecUnknownIf[%u]",
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id));
break;
}
gatts_exec_write_req_handler(&p_data->req_data);
break;
case BTA_GATTS_CONF_EVT:
@@ -1059,12 +1141,6 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
break;
}
if (BTC_GATT_GET_CONN_ID(p_data->connect.conn_id) != p_data->connect.conn_handle) {
LOG_ERR("[B]GattcConnHdlMismatch[%u][%u]",
BTC_GATT_GET_CONN_ID(p_data->connect.conn_id), p_data->connect.conn_handle);
break;
}
gattc_connect_event_handler(&p_data->connect);
break;
@@ -1375,6 +1451,7 @@ static void handle_gattc_connect_event(struct bt_le_gattc_connect_event *event)
gatt_conn->status = 0x00;
gatt_conn->gatt_if = gattc_if;
gatt_conn->conn_handle = event->conn_handle;
gatt_conn->conn_id = event->conn_id;
gatt_conn->role = event->role;
gatt_conn->peer.type = event->peer.type;
memcpy(gatt_conn->peer.val, event->peer.val, BT_ADDR_SIZE);
@@ -1387,12 +1464,14 @@ static void handle_gattc_disconnect_event(struct bt_le_gattc_disconnect_event *e
struct bt_conn *conn;
sys_snode_t *snode;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
gatt_conn = find_gatt_conn_with_conn_id(event->conn_id);
if (gatt_conn == NULL) {
LOG_DBG("[B]GattcDisconnUnknownDev");
return;
}
event->conn_handle = gatt_conn->conn_handle;
if (gatt_conn->role == BTM_ROLE_MASTER) {
/* CENTRAL may also run a GATT server (e.g. CAP Initiator with PACS).
* Mirror handle_gatts_disconnect_event: fire func(err) for every
@@ -1419,12 +1498,14 @@ static void handle_gattc_open_event(struct bt_le_gattc_open_event *event)
struct gatt_conn *gatt_conn;
uint16_t conn_id;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_ERR("[B]GattcOpenUnknownDev");
return;
}
event->conn_handle = gatt_conn->conn_handle;
gatt_conn->status = event->status;
if (gatt_conn->role == BTM_ROLE_MASTER) {
@@ -1445,7 +1526,7 @@ static void handle_gattc_open_event(struct bt_le_gattc_open_event *event)
/* At this moment, the peer device may has not initiated MTU exchange */
if (gatt_conn->mtu == 0) {
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, gatt_conn->conn_handle);
conn_id = to_gattc_conn_id(gatt_conn->conn_id);
BTA_GATTC_ConfigureMTU(conn_id);
@@ -1465,12 +1546,14 @@ static void handle_gattc_mtu_event(struct bt_le_gattc_mtu_event *event)
{
struct gatt_conn *gatt_conn;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_ERR("[B]GattcMtuUnknownConn[%u]", event->conn_handle);
LOG_ERR("[B]GattcMtuUnknownConn[0x%04x]", event->conn_id);
return;
}
event->conn_handle = gatt_conn->conn_handle;
/* The device may works as GATT client or server or both, which is not
* related to the Link Layer role, hence we don't check the Link layer
* role here and only check if the MTU has already been exchanged here.
@@ -1499,15 +1582,41 @@ static void handle_gattc_disc_cmpl_event(struct bt_le_gattc_disc_cmpl_event *eve
{
struct gatt_conn *gatt_conn;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_ERR("[B]GattcDiscSvcUnknownConn[%u]", event->conn_handle);
LOG_ERR("[B]GattcDiscSvcUnknownConn[0x%04x]", event->conn_id);
return;
}
event->conn_handle = gatt_conn->conn_handle;
bt_le_gattc_app_disc_cmpl_event(event);
}
/* Map a BTA/Bluedroid GATT status to a standard ATT error. Bluedroid reuses the
* 0x80-0x92 range for internal failures (e.g. 0x85 on link loss) overlapping ATT
* app errors, so only codes the profiles receive OTA pass; the rest -> "unlikely". */
static uint8_t bluedroid_gattc_att_err(uint8_t status)
{
/* Success (0) and ATT spec error codes (0x01-0x1F) match the wire 1:1. */
if (status <= 0x1F) {
return status;
}
/* ATT application error codes the profiles receive as a client. 0x85+ here is
* Bluedroid-internal (GATT_ERROR etc.), not an over-the-air error. */
if (status >= 0x80 && status <= 0x84) {
return status;
}
/* Common Profile and Service error codes (0xFC-0xFF). */
if (status >= 0xFC) {
return status;
}
return BT_ATT_ERR_UNLIKELY;
}
static void handle_gattc_read_chrc_event(struct bt_le_gattc_read_chrc_event *event)
{
struct bt_gatt_read_params read_copy;
@@ -1521,15 +1630,17 @@ static void handle_gattc_read_chrc_event(struct bt_le_gattc_read_chrc_event *eve
uint16_t off;
uint8_t ret;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattcRdCharNotConn");
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattcRdCharUnknownConn[0x%04x]", event->conn_id);
goto end;
}
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattcRdCharUnknownConn[%u]", event->conn_handle);
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattcRdCharNotConn");
goto end;
}
@@ -1582,7 +1693,13 @@ static void handle_gattc_read_chrc_event(struct bt_le_gattc_read_chrc_event *eve
vlen = (off < event->len) ? (event->len - off) : 0;
}
ret = read_copy.func(conn, event->status, params, val, vlen);
/* By-UUID read: report the matched handle in params->by_uuid.start_handle (like
* Zephyr); callers (has_client active-index read) use it as the value handle. */
if (read_copy.handle_count == 0 && event->status == 0) {
params->by_uuid.start_handle = event->attr_handle;
}
ret = read_copy.func(conn, bluedroid_gattc_att_err(event->status), params, val, vlen);
if (ret == BT_GATT_ITER_CONTINUE && event->status == 0) {
read_copy.func(conn, 0, params, NULL, 0);
@@ -1602,15 +1719,17 @@ static void handle_gattc_write_chrc_event(struct bt_le_gattc_write_chrc_event *e
struct bt_conn *conn;
sys_snode_t *snode;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattcWrCharNotConn");
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattcWrCharUnknownConn[0x%04x]", event->conn_id);
return;
}
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattcWrCharUnknownConn[%u]", event->conn_handle);
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattcWrCharNotConn");
return;
}
@@ -1647,7 +1766,7 @@ static void handle_gattc_write_chrc_event(struct bt_le_gattc_write_chrc_event *e
return;
}
params->func(conn, event->status, params);
params->func(conn, bluedroid_gattc_att_err(event->status), params);
}
static void handle_gattc_notify_event(struct bt_le_gattc_notify_rx_event *event)
@@ -1658,15 +1777,17 @@ static void handle_gattc_notify_event(struct bt_le_gattc_notify_rx_event *event)
struct gattc_sub *sub;
struct bt_conn *conn;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattcNotifNotConn");
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattcNotifUnknownConn[0x%04x]", event->conn_id);
goto end;
}
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattcNotifUnknownConn[%u]", event->conn_handle);
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattcNotifNotConn");
goto end;
}
@@ -1730,10 +1851,20 @@ static void handle_gatts_connect_event(struct bt_le_gatts_connect_event *event)
gatt_conn->status = 0x00;
gatt_conn->gatt_if = gatts_if;
gatt_conn->conn_handle = event->conn_handle;
gatt_conn->conn_id = event->conn_id;
gatt_conn->role = event->role;
gatt_conn->peer.type = event->peer.type;
memcpy(gatt_conn->peer.val, event->peer.val, BT_ADDR_SIZE);
/* conn->le.dst is the connect-event address verbatim — no RPA->identity resolution.
* A peer using an RPA makes bt_le_bond_exists()/CCC cfg miss on reconnect (fresh RPA
* each time). Identity / static-random / public addresses are stable. Warn to surface it. */
if (event->peer.type == BT_ADDR_LE_RANDOM && BT_ADDR_IS_RPA(&event->peer)) {
LOG_WRN("[B]GattsConnRpaDst[%02x:%02x:%02x:%02x:%02x:%02x]",
event->peer.val[5], event->peer.val[4], event->peer.val[3],
event->peer.val[2], event->peer.val[1], event->peer.val[0]);
}
post_acl_connect_app_event(gatt_conn);
}
@@ -1744,12 +1875,14 @@ static void handle_gatts_disconnect_event(struct bt_le_gatts_disconnect_event *e
struct bt_conn *conn;
sys_snode_t *snode;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
gatt_conn = find_gatt_conn_with_conn_id(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattsDisconnUnknownDev");
return;
}
event->conn_handle = gatt_conn->conn_handle;
/* Mirror handle_gatts_connect_event: SLAVE-side teardown happens here;
* MASTER teardown is in handle_gattc_disconnect_event. */
if (gatt_conn->role != BTM_ROLE_SLAVE) {
@@ -1783,12 +1916,14 @@ static void handle_gatts_mtu_event(struct bt_le_gatts_mtu_event *event)
{
struct gatt_conn *gatt_conn;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_ERR("[B]GattsMtuUnknownConn[%u]", event->conn_handle);
LOG_ERR("[B]GattsMtuUnknownConn[0x%04x]", event->conn_id);
return;
}
event->conn_handle = gatt_conn->conn_handle;
/* The device may works as GATT client or server or both, which is not
* related to the Link Layer role, hence we don't check the Link layer
* role here and only check if the MTU has already been exchanged here.
@@ -1812,6 +1947,7 @@ static void handle_gatts_mtu_event(struct bt_le_gatts_mtu_event *event)
static void handle_gatts_read_event(struct bt_le_gatts_read_event *event)
{
struct gatt_conn *gatt_conn;
struct bt_gatt_attr *attr;
tBTA_GATT_STATUS status;
struct bt_conn *conn;
@@ -1822,6 +1958,14 @@ static void handle_gatts_read_event(struct bt_le_gatts_read_event *event)
status = BTA_GATT_OK;
rsp = NULL;
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattsRdUnknownConn[0x%04x]", event->conn_id);
return;
}
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattsRdEvtNotConn");
@@ -1844,24 +1988,23 @@ static void handle_gatts_read_event(struct bt_le_gatts_read_event *event)
rsp->attr_value.handle = event->attr_handle;
if (attr->read) {
if (event->offset) {
LOG_WRN("[B]GattsRdEvtNotSupp");
/* Pass the request offset through so GATT Read Blob (long read) works: the read
* cb returns value[offset..], BTA caps each response at ATT_MTU-1. Needed for
* values larger than one PDU (e.g. a BASS Broadcast Receive State). */
ret = attr->read(conn, attr, (void *)rsp, GATT_MAX_ATTR_LEN, event->offset);
if (ret < 0) {
LOG_DBG("[B]GattsRdEvtErr[%u][%d]", event->attr_handle, ret);
status = GATT_REQ_NOT_SUPPORTED;
} else {
ret = attr->read(conn, attr, (void *)rsp, GATT_MAX_ATTR_LEN, 0);
if (ret < 0) {
LOG_DBG("[B]GattsRdEvtErr[%u][%d]", event->attr_handle, ret);
status = BT_GATT_ERR(ret);
}
status = BT_GATT_ERR(ret);
}
} else {
status = GATT_READ_NOT_PERMIT;
}
end:
conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn->handle);
/* Respond on the conn_id the request arrived on (event->conn_id): for a central-side
* server the slot's canonical conn_id is the GATTC one, which would be wrong here. */
conn_id = event->conn_id;
BTA_GATTS_SendRsp(conn_id, event->trans_id, status, rsp);
@@ -1870,9 +2013,92 @@ end:
}
}
/* Buffer one ATT prepare-write chunk and echo it; the reassembled value is applied on
* the execute event. Per-connection state (struct gatt_conn) so concurrent long writes
* don't collide. Used for control-point PDUs > ATT_MTU-3 (e.g. BASS Add Source). */
static void handle_gatts_prepare_write(struct bt_le_gatts_write_event *event)
{
struct gatt_conn *gatt_conn;
tBTA_GATT_STATUS status = BTA_GATT_OK;
uint16_t conn_id;
tBTA_GATTS_RSP *rsp;
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_ERR("[B]GattsPrepNoConn[0x%04x]", event->conn_id);
return;
}
if (event->offset == 0) {
/* A new long write always restarts at offset 0; this also discards any
* sequence abandoned by a prior disconnect. */
gatt_conn->prep_active = true;
gatt_conn->prep_error = false;
gatt_conn->prep_attr_handle = event->attr_handle;
gatt_conn->prep_len = 0;
} else if (!gatt_conn->prep_active ||
gatt_conn->prep_attr_handle != event->attr_handle ||
gatt_conn->prep_len != event->offset) {
LOG_WRN("[B]GattsPrepBadOft[%u][%u]", event->offset, gatt_conn->prep_len);
gatt_conn->prep_error = true;
status = GATT_INVALID_OFFSET;
}
if (status == BTA_GATT_OK) {
uint32_t total = (uint32_t)event->offset + event->len;
if (total > GATTS_PREP_MAX_LEN) {
LOG_WRN("[B]GattsPrepQFull[%u]", (unsigned)total);
gatt_conn->prep_error = true;
status = GATT_PREPARE_Q_FULL;
} else if (total == 0) {
/* realloc(_,0) frees prep_buf and returns NULL → dangling ptr that
execute/disconnect would double-free. Release it explicitly. */
free(gatt_conn->prep_buf);
gatt_conn->prep_buf = NULL;
gatt_conn->prep_len = 0;
} else {
/* Grow the reassembly buffer to the write size on demand; realloc leaves
* the old buffer intact on failure. */
uint8_t *nb = realloc(gatt_conn->prep_buf, total);
if (nb == NULL) {
LOG_ERR("[B]GattsPrepNoMem[%u]", (unsigned)total);
gatt_conn->prep_error = true;
status = GATT_INSUF_RESOURCE;
} else {
gatt_conn->prep_buf = nb;
memcpy(gatt_conn->prep_buf + event->offset, event->value, event->len);
gatt_conn->prep_len = (uint16_t)total;
}
}
}
conn_id = event->conn_id;
if (status == BTA_GATT_OK) {
/* The prepare-write response must echo handle/offset/value. */
rsp = calloc(1, sizeof(*rsp));
assert(rsp);
rsp->attr_value.handle = event->attr_handle;
rsp->attr_value.offset = event->offset;
rsp->attr_value.len = event->len;
if (event->len) {
memcpy(rsp->attr_value.value, event->value, event->len);
}
BTA_GATTS_SendRsp(conn_id, event->trans_id, status, rsp);
free(rsp);
} else {
BTA_GATTS_SendRsp(conn_id, event->trans_id, status, NULL);
}
}
static void handle_gatts_write_event(struct bt_le_gatts_write_event *event)
{
struct bt_le_gatts_subscribe_event sub_event;
struct gatt_conn *gatt_conn;
struct bt_gatt_attr *attr;
tBTA_GATT_STATUS status;
struct bt_conn *conn;
@@ -1881,6 +2107,17 @@ static void handle_gatts_write_event(struct bt_le_gatts_write_event *event)
status = BTA_GATT_OK;
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattsWrUnknownConn[0x%04x]", event->conn_id);
if (event->value) {
free(event->value);
}
return;
}
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattsWrEvtNotConn");
@@ -1890,6 +2127,15 @@ static void handle_gatts_write_event(struct bt_le_gatts_write_event *event)
return;
}
/* Long write: queue the chunk and defer the attr->write to the execute event. */
if (event->is_prep) {
handle_gatts_prepare_write(event);
if (event->value) {
free(event->value);
}
return;
}
attr = bt_gatts_find_attr_by_handle(event->attr_handle);
if (attr == NULL) {
LOG_ERR("[B]GattsWrAttrNotFound[%u]", event->attr_handle);
@@ -1953,7 +2199,7 @@ static void handle_gatts_write_event(struct bt_le_gatts_write_event *event)
end:
if (event->need_rsp) {
conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn->handle);
conn_id = event->conn_id;
BTA_GATTS_SendRsp(conn_id, event->trans_id, status, NULL);
}
@@ -1963,6 +2209,61 @@ end:
}
}
static void handle_gatts_exec_write_event(struct bt_le_gatts_exec_write_event *event)
{
tBTA_GATT_STATUS status = BTA_GATT_OK;
struct gatt_conn *gatt_conn;
struct bt_gatt_attr *attr;
struct bt_conn *conn;
uint16_t conn_id;
ssize_t ret;
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]GattsExecUnknownConn[0x%04x]", event->conn_id);
return;
}
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_WRN("[B]GattsExecNotConn");
return;
}
if (!gatt_conn->prep_active) {
LOG_DBG("[B]GattsExecNoPrep");
} else {
if (!event->exec) {
LOG_DBG("[B]GattsExecCancel");
} else if (gatt_conn->prep_error) {
LOG_WRN("[B]GattsExecPrepErr");
status = GATT_INSUF_RESOURCE;
} else {
attr = bt_gatts_find_attr_by_handle(gatt_conn->prep_attr_handle);
if (attr == NULL) {
status = GATT_INVALID_HANDLE;
} else if (attr->write == NULL) {
status = GATT_WRITE_NOT_PERMIT;
} else {
ret = attr->write(conn, attr, gatt_conn->prep_buf, gatt_conn->prep_len, 0, 0);
if (ret < 0) {
LOG_DBG("[B]GattsExecWrErr[%u][%d]", gatt_conn->prep_attr_handle, (int)ret);
status = BT_GATT_ERR(ret);
}
}
}
free(gatt_conn->prep_buf);
gatt_conn->prep_buf = NULL;
gatt_conn->prep_active = false;
}
conn_id = event->conn_id;
BTA_GATTS_SendRsp(conn_id, event->trans_id, status, NULL);
}
static void handle_gatts_notify_tx_event(struct bt_le_gatts_notify_tx_event *event)
{
struct gatts_list_node *n;
@@ -1973,15 +2274,17 @@ static void handle_gatts_notify_tx_event(struct bt_le_gatts_notify_tx_event *eve
/* Find conn once up front. If the acl_conn was already torn down (race
* with a queued CLOSE_EVT behind this CONF_EVT), leave the head in the
* list — disconnect drain will fire its func with BT_ATT_ERR_UNLIKELY. */
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL) {
LOG_WRN("[B]NotifyTxNoConn[%u]", event->conn_handle);
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
if (gatt_conn == NULL) {
LOG_WRN("[B]NotifyTxUnknownConn[0x%04x]", event->conn_id);
return;
}
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_handle(event->conn_handle);
if (gatt_conn == NULL) {
LOG_WRN("[B]NotifyTxUnknownConn[%u]", event->conn_handle);
event->conn_handle = gatt_conn->conn_handle;
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL) {
LOG_WRN("[B]NotifyTxNoConn[%u]", event->conn_handle);
return;
}
@@ -2022,7 +2325,7 @@ static void handle_gatts_notify_tx_event(struct bt_le_gatts_notify_tx_event *eve
if (next_snode != NULL) {
struct gatts_list_node *next_n =
CONTAINER_OF(next_snode, struct gatts_list_node, node);
gatts_indicate_dispatch(conn, next_n);
gatts_indicate_dispatch(conn, gatt_conn->conn_id, next_n);
}
}
@@ -2108,6 +2411,10 @@ void bt_le_bluedroid_gatt_handle_event(uint8_t *data, size_t data_len)
handle_gatts_write_event(&param->gatts_write);
break;
case BT_LE_GATTS_EXEC_WRITE_EVENT:
handle_gatts_exec_write_event(&param->gatts_exec_write);
break;
case BT_LE_GATTS_NOTIFY_TX_EVENT:
handle_gatts_notify_tx_event(&param->gatts_notify_tx);
break;
@@ -2326,8 +2633,11 @@ static int gatts_notify(struct bt_conn *conn,
}
}
conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn->handle);
BTA_GATTS_HandleValueIndication(conn_id, data.handle, len,
if (!gatt_conn_id_lookup(conn->handle, &conn_id)) {
return -ENOTCONN;
}
BTA_GATTS_HandleValueIndication(to_gatts_conn_id(conn_id), data.handle, len,
(uint8_t *)value, need_cfm);
} else {
bt_conn_get_acl_conns(&conns, &conns_count);
@@ -2341,8 +2651,11 @@ static int gatts_notify(struct bt_conn *conn,
continue;
}
conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conns[i].handle);
BTA_GATTS_HandleValueIndication(conn_id, data.handle, len,
if (!gatt_conn_id_lookup(conns[i].handle, &conn_id)) {
continue;
}
BTA_GATTS_HandleValueIndication(to_gatts_conn_id(conn_id), data.handle, len,
(uint8_t *)value, need_cfm);
}
}
@@ -2383,7 +2696,7 @@ static int gatts_indicate_enqueue(struct bt_conn *conn,
sys_slist_append(&gatt_conn->gatts_list, &n->node);
if (was_empty) {
gatts_indicate_dispatch(conn, n);
gatts_indicate_dispatch(conn, gatt_conn->conn_id, n);
}
return 0;
@@ -2478,7 +2791,8 @@ int bt_le_bluedroid_gattc_disc_start(uint16_t conn_handle)
return 0;
}
static int gattc_disc_primary_svc(struct bt_conn *conn, struct bt_gatt_discover_params *params)
static int gattc_disc_primary_svc(struct bt_conn *conn, uint16_t conn_id,
struct bt_gatt_discover_params *params)
{
struct bt_gatt_service_val svc = {0};
struct bt_uuid_16 svc_uuid = {0};
@@ -2486,7 +2800,6 @@ static int gattc_disc_primary_svc(struct bt_conn *conn, struct bt_gatt_discover_
btgatt_db_element_t *db = NULL;
tBT_UUID uuid = {0};
uint16_t count = 0;
uint16_t conn_id;
uint8_t ret;
if (params->uuid == NULL) {
@@ -2494,7 +2807,6 @@ static int gattc_disc_primary_svc(struct bt_conn *conn, struct bt_gatt_discover_
return -ENOTSUP;
}
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
bt_le_bluedroid_gatt_uuid_convert(params->uuid, &uuid);
BTA_GATTC_GetServiceWithUUID(conn_id, &uuid, &db, &count);
@@ -2562,7 +2874,8 @@ end:
return 0;
}
static int gattc_disc_included_svc(struct bt_conn *conn, struct bt_gatt_discover_params *params)
static int gattc_disc_included_svc(struct bt_conn *conn, uint16_t conn_id,
struct bt_gatt_discover_params *params)
{
struct bt_gatt_include inc_svc = {0};
struct bt_uuid_16 svc_uuid = {0};
@@ -2570,10 +2883,8 @@ static int gattc_disc_included_svc(struct bt_conn *conn, struct bt_gatt_discover
btgatt_db_element_t *db = NULL;
tBT_UUID uuid = {0};
uint16_t count = 0;
uint16_t conn_id;
uint8_t ret;
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
if (params->uuid) {
bt_le_bluedroid_gatt_uuid_convert(params->uuid, &uuid);
}
@@ -2650,7 +2961,8 @@ end:
return 0;
}
static int gattc_disc_chrc(struct bt_conn *conn, struct bt_gatt_discover_params *params)
static int gattc_disc_chrc(struct bt_conn *conn, uint16_t conn_id,
struct bt_gatt_discover_params *params)
{
struct bt_uuid_16 chrc_uuid = {0};
struct bt_gatt_attr attr = {0};
@@ -2658,11 +2970,8 @@ static int gattc_disc_chrc(struct bt_conn *conn, struct bt_gatt_discover_params
btgatt_db_element_t *db = NULL;
tBT_UUID uuid = {0};
uint16_t count = 0;
uint16_t conn_id;
uint8_t ret;
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
/* Note:
* Increment start_handle with 1 here because, for example, if 2 characteristics
* are found with characteristic value handle equals to A and B. After handling
@@ -2747,7 +3056,8 @@ end:
return 0;
}
static int gattc_disc_chrc_desc(struct bt_conn *conn, struct bt_gatt_discover_params *params)
static int gattc_disc_chrc_desc(struct bt_conn *conn, uint16_t conn_id,
struct bt_gatt_discover_params *params)
{
struct bt_gatt_attr attr = {0};
btgatt_db_element_t *db = NULL;
@@ -2756,7 +3066,6 @@ static int gattc_disc_chrc_desc(struct bt_conn *conn, struct bt_gatt_discover_pa
uint16_t chrc_handle;
tBT_UUID uuid = {0};
uint16_t count = 0;
uint16_t conn_id;
int err = 0;
assert(params);
@@ -2773,7 +3082,6 @@ static int gattc_disc_chrc_desc(struct bt_conn *conn, struct bt_gatt_discover_pa
return -EINVAL;
}
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
chrc_handle = params->sub_params->value_handle;
bt_le_bluedroid_gatt_uuid_convert(params->uuid, &uuid);
@@ -2856,16 +3164,16 @@ int bt_le_bluedroid_gattc_discover(struct bt_conn *conn, struct bt_gatt_discover
switch (params->type) {
case BT_GATT_DISCOVER_PRIMARY:
return gattc_disc_primary_svc(conn, params);
return gattc_disc_primary_svc(conn, to_gattc_conn_id(gatt_conn->conn_id), params);
case BT_GATT_DISCOVER_INCLUDE:
return gattc_disc_included_svc(conn, params);
return gattc_disc_included_svc(conn, to_gattc_conn_id(gatt_conn->conn_id), params);
case BT_GATT_DISCOVER_CHARACTERISTIC:
return gattc_disc_chrc(conn, params);
return gattc_disc_chrc(conn, to_gattc_conn_id(gatt_conn->conn_id), params);
case BT_GATT_DISCOVER_DESCRIPTOR:
return gattc_disc_chrc_desc(conn, params);
return gattc_disc_chrc_desc(conn, to_gattc_conn_id(gatt_conn->conn_id), params);
default:
LOG_ERR("[B]DiscTypeNotSupp[%u]", params->type);
@@ -2899,7 +3207,7 @@ int bt_le_bluedroid_gattc_read(struct bt_conn *conn, struct bt_gatt_read_params
sys_slist_append(&gatt_conn->gattc_list, &op->node);
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
conn_id = to_gattc_conn_id(gatt_conn->conn_id);
if (op->read.params_copy.handle_count == 0) {
LOG_INF("[B]RdByTypeReq[0x%04x]",
@@ -2946,7 +3254,7 @@ int bt_le_bluedroid_gattc_write(struct bt_conn *conn, struct bt_gatt_write_param
sys_slist_append(&gatt_conn->gattc_list, &op->node);
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
conn_id = to_gattc_conn_id(gatt_conn->conn_id);
BTA_GATTC_WriteCharValue(conn_id, params->handle, BTA_GATTC_TYPE_WRITE,
params->length, (void *)params->data,
@@ -2972,7 +3280,7 @@ int bt_le_bluedroid_gattc_write_without_rsp(struct bt_conn *conn, uint16_t handl
return -ENODEV;
}
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
conn_id = to_gattc_conn_id(gatt_conn->conn_id);
/* TODO: BTA_GATTC_WriteCharValue is void and silently drops the cmd on
* osi_malloc failure. NimBLE returns the host status here, so the API
@@ -2992,7 +3300,13 @@ int bt_le_bluedroid_gattc_write_ccc(struct bt_conn *conn, struct bt_gatt_subscri
uint16_t chrc_handle;
uint16_t conn_id;
conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn->handle);
if (!gatt_conn_id_lookup(conn->handle, &conn_id)) {
LOG_INF("[B]NoConnInfo[%u]", conn->handle);
return -ENOTCONN;
}
conn_id = to_gattc_conn_id(conn_id);
chrc_handle = params->value_handle;
/* Unsubscribe path: caller (bt_gatt_unsubscribe) forces params->value to
@@ -20,6 +20,10 @@
extern "C" {
#endif
/* Reject ceiling for a reassembled ATT long write: the GATT attribute value max
* (0..512 octets). Costs no RAM — the buffer is malloc'd to the actual write size. */
#define GATTS_PREP_MAX_LEN 512
struct gatt_conn {
uint8_t used : 1;
uint8_t conn_create : 1;
@@ -30,6 +34,10 @@ struct gatt_conn {
uint8_t status;
uint8_t gatt_if;
uint16_t conn_handle;
/* BTA GATT conn_id: (internal GATT index << 8) | gatt_if. Diverges from
* conn_handle (the ACL handle) after connection churn, so it's captured
* verbatim from BTA at connect rather than rebuilt from the ACL handle. */
uint16_t conn_id;
uint8_t role;
struct {
uint8_t type;
@@ -54,6 +62,15 @@ struct gatt_conn {
* flag so the handler pops this list first to disambiguate from the
* indication acks tracked in gatts_list. See struct gatts_notify_node. */
sys_slist_t gatts_notify_list;
/* Per-connection ATT long-write (prepare/execute) reassembly, so concurrent long
* writes on different links never collide. prep_buf is malloc'd to the write size
* on demand and freed on execute/cancel/disconnect — idle links cost nothing. */
bool prep_active;
bool prep_error; /* sticky: a chunk overflowed or arrived out of order */
uint16_t prep_attr_handle;
uint16_t prep_len;
uint8_t *prep_buf;
};
uint8_t bt_le_bluedroid_gattc_get_if(void);
@@ -827,6 +827,14 @@ int bt_le_bluedroid_iso_cmd_send_sync(uint16_t opcode,
return bluedroid_err_to_errno(rc);
}
/* Strip the BTM_HCI_ERROR (0x80) flag Bluedroid ORs into tBTM_STATUS for controller
* HCI errors; the Zephyr host expects a raw HCI code. No-op on success. (Else a CIS
* failure surfaces as e.g. 0x9E instead of the real 0x1E.) */
static inline uint8_t iso_hci_status(uint8_t btm_status)
{
return btm_status & ~(uint8_t)BTM_HCI_ERROR;
}
static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *params)
{
enum iso_queue_item_type q_type;
@@ -870,7 +878,7 @@ static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *pa
qdata = calloc(1, qdata_len);
assert(qdata);
ev.status = params->btm_cis_established_evt.status;
ev.status = iso_hci_status(params->btm_cis_established_evt.status);
ev.conn_handle = params->btm_cis_established_evt.conn_handle;
sys_put_le24(params->btm_cis_established_evt.cig_sync_delay, ev.cig_sync_delay);
sys_put_le24(params->btm_cis_established_evt.cis_sync_delay, ev.cis_sync_delay);
@@ -920,7 +928,7 @@ static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *pa
qdata = calloc(1, qdata_len);
assert(qdata);
ev.status = params->btm_big_cmpl.status;
ev.status = iso_hci_status(params->btm_big_cmpl.status);
ev.big_handle = params->btm_big_cmpl.big_handle;
sys_put_le24(params->btm_big_cmpl.big_sync_delay, ev.sync_delay);
sys_put_le24(params->btm_big_cmpl.transport_latency, ev.latency);
@@ -968,7 +976,7 @@ static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *pa
qdata = calloc(1, qdata_len);
assert(qdata);
ev.status = params->btm_big_sync_estab.status;
ev.status = iso_hci_status(params->btm_big_sync_estab.status);
ev.big_handle = params->btm_big_sync_estab.big_handle;
sys_put_le24(params->btm_big_sync_estab.transport_latency_big, ev.latency);
ev.nse = params->btm_big_sync_estab.nse;
@@ -214,6 +214,27 @@ void bt_le_nimble_gap_post_event(void *param)
qev->security_change.bonded = desc.sec_state.bonded;
qev->security_change.dst.type = desc.peer_id_addr.type;
memcpy(qev->security_change.dst.val, desc.peer_id_addr.val, BT_ADDR_SIZE);
/* Capture the bonded LTK for the lib's CSIS SIRK encryption. The
* peripheral's own key (OUR_SEC) is the link LTK; fall back to the
* peer record. Mirrors nimble_desc_to_sec_level's store read. */
{
struct ble_store_value_sec sec = {0};
struct ble_store_key_sec skey = {0};
skey.peer_addr = desc.peer_id_addr;
if ((ble_store_read_our_sec(&skey, &sec) == 0 && sec.ltk_present) ||
(ble_store_read_peer_sec(&skey, &sec) == 0 && sec.ltk_present)) {
memcpy(qev->security_change.ltk, sec.ltk,
sizeof(qev->security_change.ltk));
qev->security_change.ltk_present = 1;
LOG_INF("[N]LtkFromStore[%u]", ev->enc_change.conn_handle);
} else {
/* Encrypted but no stored LTK; CSIS SIRK encryption will
* have no key. */
LOG_WRN("[N]NoLtkForEnc[%u]", ev->enc_change.conn_handle);
}
}
}
break;
@@ -169,8 +169,13 @@ static int gattc_nrp_read_by_uuid_cb_safe(uint16_t conn_handle,
LOG_WRN("[N]GattcNrpStatus[%04x]", error->status);
bt_le_nimble_gatt_nrp_remove(conn, GATTC_NRP_READ_BY_UUID, read_params, 0);
if ((error->status & BLE_HS_ERR_ATT_BASE) && !iter_stopped) {
read_params->func(conn, (uint8_t)error->status, read_params, NULL, 0);
if (!iter_stopped) {
/* A NimBLE ATT error carries the wire code (BLE_HS_ATT_ERR base);
* anything else is an internal/link failure -> generic "unlikely". */
uint8_t att_err = (error->status & BLE_HS_ERR_ATT_BASE) ?
(uint8_t)error->status : BT_ATT_ERR_UNLIKELY;
read_params->func(conn, att_err, read_params, NULL, 0);
}
rc = error->status;
@@ -253,8 +258,13 @@ static int gattc_nrp_read_long_cb_safe(uint16_t conn_handle,
LOG_WRN("[N]GattcNrpStatus[%04x]", error->status);
bt_le_nimble_gatt_nrp_remove(conn, GATTC_NRP_READ_LONG, read_params, 0);
if ((error->status & BLE_HS_ERR_ATT_BASE) && !iter_stopped) {
read_params->func(conn, (uint8_t)error->status, read_params, NULL, 0);
if (!iter_stopped) {
/* A NimBLE ATT error carries the wire code (BLE_HS_ATT_ERR base);
* anything else is an internal/link failure -> generic "unlikely". */
uint8_t att_err = (error->status & BLE_HS_ERR_ATT_BASE) ?
(uint8_t)error->status : BT_ATT_ERR_UNLIKELY;
read_params->func(conn, att_err, read_params, NULL, 0);
}
rc = error->status;
@@ -323,9 +333,10 @@ static int gattc_nrp_read_single_cb_safe(uint16_t conn_handle,
default:
LOG_WRN("[N]GattcNrpStatus[%04x]", error->status);
if (error->status & BLE_HS_ERR_ATT_BASE) {
func(conn, (uint8_t)error->status, original, NULL, 0);
}
/* A NimBLE ATT error carries the wire code (BLE_HS_ATT_ERR base);
* anything else is an internal/link failure -> generic "unlikely". */
func(conn, (error->status & BLE_HS_ERR_ATT_BASE) ?
(uint8_t)error->status : BT_ATT_ERR_UNLIKELY, original, NULL, 0);
rc = error->status;
break;
@@ -430,9 +441,10 @@ static int gattc_nrp_write_cb_safe(uint16_t conn_handle,
default:
LOG_WRN("[N]GattcNrpStatus[%04x]", error->status);
if (error->status & BLE_HS_ERR_ATT_BASE) {
write_params->func(conn, (uint8_t)error->status, write_params);
}
/* A NimBLE ATT error carries the wire code (BLE_HS_ATT_ERR base);
* anything else is an internal/link failure -> generic "unlikely". */
write_params->func(conn, (error->status & BLE_HS_ERR_ATT_BASE) ?
(uint8_t)error->status : BT_ATT_ERR_UNLIKELY, write_params);
rc = error->status;
break;
@@ -43,11 +43,11 @@ static int ots_l2cap_recv_ready(struct ble_l2cap_chan *chan)
struct os_mbuf *sdu_rx;
int rc;
LOG_DBG("[N]OtsRecvReady");
LOG_DBG("[N]L2capOtsRecvReady");
sdu_rx = os_mbuf_get_pkthdr(&ots_mbuf_pool, 0);
if (sdu_rx == NULL) {
LOG_ERR("[N]NoBufForL2capRecv");
LOG_ERR("[N]L2capNoBufForL2capRecv");
return -ENOMEM;
}
@@ -70,31 +70,31 @@ static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
uint8_t *sdu;
int err;
LOG_DBG("[N]OtsEvtCb[%u]", event->type);
LOG_DBG("[N]L2capOtsEvtCb[%u]", event->type);
switch (event->type) {
case BLE_L2CAP_EVENT_COC_CONNECTED:
if (event->connect.status) {
LOG_ERR("[N]CocConnectFail[%d]", event->connect.status);
LOG_ERR("[N]L2capCocConnectFail[%d]", event->connect.status);
return 0;
}
if (ots_chan) {
LOG_ERR("[N]CocChanExist");
LOG_ERR("[N]L2capCocChanExist");
return 0;
}
ots_chan = event->connect.chan;
if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) {
LOG_ERR("[N]CocGetChanInfoFail");
LOG_ERR("[N]L2capCocGetChanInfoFail");
/* Roll back the latch so the next COC_CONNECTED isn't refused
* by the if (ots_chan) guard above. */
ots_chan = NULL;
return -EIO;
}
LOG_DBG("[N]CocConnect[%u][%04x][%04x][%04x][%u][%u][%u][%u]",
LOG_INF("[N]L2capCocConnect[%u][%04x][%04x][%04x][%u][%u][%u][%u]",
event->connect.conn_handle, chan_info.scid, chan_info.dcid,
chan_info.psm, chan_info.our_l2cap_mtu, chan_info.peer_l2cap_mtu,
chan_info.our_coc_mtu, chan_info.peer_coc_mtu);
@@ -106,11 +106,11 @@ static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
if (ots_chan != event->disconnect.chan) {
LOG_ERR("[N]DisconnectInvCocChan");
LOG_ERR("[N]L2capDisconnectInvCocChan");
return 0;
}
LOG_DBG("[N]CocDisconnect[%u][%04x]",
LOG_INF("[N]L2capCocDisconnect[%u][%04x]",
event->disconnect.conn_handle, event->disconnect.chan->psm);
bt_le_l2cap_disconnected(event->disconnect.conn_handle, event->disconnect.chan->psm);
@@ -119,13 +119,11 @@ static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
return 0;
case BLE_L2CAP_EVENT_COC_ACCEPT:
if (event->accept.peer_sdu_size > L2CAP_LE_OTS_MTU) {
LOG_ERR("[N]InvAcceptMtu[%u][%u]",
event->accept.peer_sdu_size, L2CAP_LE_OTS_MTU);
return -EINVAL;
}
/* Don't reject on peer_sdu_size: LE CoC allows asymmetric MTUs. It's the peer's
* RX MTU (our TX ceiling) — harmless; our RX is bounded by ble_l2cap_create_server.
* (PTS uses 1024 > our 256; rejecting broke SCP.) */
LOG_DBG("[N]CocAccept[%u][%04x][%04x][%u][%u][%u][%u]",
LOG_DBG("[N]L2capCocAccept[%u][%04x][%04x][%u][%u][%u][%u]",
event->accept.conn_handle, event->accept.chan->psm,
event->accept.chan->dcid, event->accept.chan->coc_tx.mtu,
event->accept.chan->peer_coc_mps, event->accept.peer_sdu_size,
@@ -148,13 +146,13 @@ static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
if (ots_chan != event->receive.chan) {
LOG_ERR("[N]RecvOnInvCocChan");
LOG_ERR("[N]L2capRecvOnInvCocChan");
return 0;
}
assert(event->receive.sdu_rx);
LOG_DBG("[N]CocReceive[%u][%04x][%u]",
LOG_DBG("[N]L2capCocRecv[%u][%04x][%u]",
event->receive.conn_handle, event->receive.chan->psm,
event->receive.sdu_rx->om_len);
@@ -177,11 +175,11 @@ static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
if (ots_chan != event->tx_unstalled.chan) {
LOG_ERR("[N]TxUnstalledOnInvCocChan");
LOG_ERR("[N]L2capTxUnstalledOnInvCocChan");
return 0;
}
LOG_DBG("[N]CocTxUnstalled[%u][%d]",
LOG_WRN("[N]L2capCocTxUnstalled[%u][%d]",
event->tx_unstalled.conn_handle, event->tx_unstalled.status);
/* TODO: transmit the remaining data */
@@ -198,13 +196,13 @@ int bt_le_nimble_l2cap_chan_connect(uint16_t conn_handle)
int rc;
if (ots_chan) {
LOG_WRN("[N]OtsChanExist");
LOG_WRN("[N]L2capOtsChanExist");
return -EALREADY;
}
sdu_rx = os_mbuf_get_pkthdr(&ots_mbuf_pool, 0);
if (sdu_rx == NULL) {
LOG_ERR("[N]NoBufForL2capConnect");
LOG_ERR("[N]L2capNoBufForConnect");
return -ENOMEM;
}
@@ -225,12 +223,12 @@ int bt_le_nimble_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
int rc;
if (ots_chan == NULL) {
LOG_WRN("[N]NoOtsChan");
LOG_WRN("[N]L2capNoOtsChan");
return -ENOTCONN;
}
if (ble_l2cap_get_conn_handle(ots_chan) != chan->conn->handle) {
LOG_ERR("[N]UnexpOtsChan[%u][%u]",
LOG_ERR("[N]L2capUnexpOtsChan[%u][%u]",
ble_l2cap_get_conn_handle(ots_chan), chan->conn->handle);
return -EINVAL;
}
@@ -250,25 +248,25 @@ int bt_le_nimble_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf
int rc;
if (ots_chan == NULL) {
LOG_WRN("[N]NoOtsChan");
LOG_WRN("[N]L2capNoOtsChan");
return -ENOTCONN;
}
if (ble_l2cap_get_conn_handle(ots_chan) != chan->conn->handle) {
LOG_ERR("[N]UnexpOtsChan[%u][%u]",
LOG_ERR("[N]L2capUnexpOtsChan[%u][%u]",
ble_l2cap_get_conn_handle(ots_chan), chan->conn->handle);
return -EINVAL;
}
sdu_tx = os_mbuf_get_pkthdr(&ots_mbuf_pool, 0);
if (sdu_tx == NULL) {
LOG_ERR("[N]NoBufForL2capSend");
LOG_ERR("[N]L2capNoBufForSend");
return -ENOMEM;
}
rc = os_mbuf_append(sdu_tx, buf->data, buf->len);
if (rc) {
LOG_ERR("[N]AppendBufFail[%d]", rc);
LOG_ERR("[N]L2capAppendBufFail[%d]", rc);
os_mbuf_free_chain(sdu_tx);
return -EIO;
}
@@ -279,19 +277,23 @@ int bt_le_nimble_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf
/* sdu is queued in tx->sdus[0]; NimBLE will continue on
* BLE_L2CAP_EVENT_COC_TX_UNSTALLED. Do NOT free here.
*/
LOG_WRN("[N]MoreCreditsForL2capSend");
LOG_WRN("[N]L2capMoreCreditsForSend");
} else if (rc == BLE_HS_EBADDATA || rc == BLE_HS_EBUSY) {
/* sdu was rejected before being queued; caller still owns it. */
LOG_ERR("[N]L2capSendFail[%d]", rc);
os_mbuf_free_chain(sdu_tx);
} else {
/* Internal error inside continue_tx; NimBLE already freed sdu. */
LOG_ERR("[N]L2capSendFail[%d]", rc);
LOG_ERR("[N]L2capSendInternalFail[%d]", rc);
}
return nimble_err_to_errno(rc);
}
/* Payload copied into sdu_tx; consume buf here (no async TX-done to unref it),
* else the 1-buffer ot_chan_tx_pool leaks. Failure paths leave buf to caller. */
net_buf_unref(buf);
return 0;
}
@@ -301,20 +303,20 @@ int bt_le_nimble_l2cap_init(void)
rc = os_mempool_init(&ots_mbuf_mempool, OTS_L2CAP_BUF_COUNT, L2CAP_LE_OTS_MTU * 2, ots_mem, "ots_pool");
if (rc) {
LOG_ERR("[N]InitOtsMempoolFail[%d]", rc);
LOG_ERR("[N]L2capInitOtsMempoolFail[%d]", rc);
return rc;
}
rc = os_mbuf_pool_init(&ots_mbuf_pool, &ots_mbuf_mempool, L2CAP_LE_OTS_MTU, OTS_L2CAP_BUF_COUNT);
if (rc) {
LOG_ERR("[N]InitOtsMbufPoolFail[%d]", rc);
LOG_ERR("[N]L2capInitOtsMbufPoolFail[%d]", rc);
return rc;
}
#if CONFIG_BT_OTS
rc = ble_l2cap_create_server(L2CAP_LE_OTS_PSM, L2CAP_LE_OTS_MTU, ots_l2cap_event_cb, NULL);
if (rc) {
LOG_ERR("[N]CreateL2capSrvFail[%d]", rc);
LOG_ERR("[N]L2capCreateL2capSrvFail[%d]", rc);
return rc;
}
#endif /* CONFIG_BT_OTS */
+8 -1
View File
@@ -70,7 +70,8 @@ struct bt_le_ext_adv *bt_le_ext_adv_find(uint8_t adv_handle)
}
_IDF_ONLY
int bt_le_ext_adv_new_safe(uint8_t adv_handle)
int bt_le_ext_adv_new_safe(uint8_t adv_handle, uint8_t addr_type,
const uint8_t *addr, uint8_t sid)
{
struct bt_le_ext_adv *adv = NULL;
int err = 0;
@@ -92,6 +93,9 @@ int bt_le_ext_adv_new_safe(uint8_t adv_handle)
}
adv->handle = adv_handle;
adv->addr.type = addr_type;
bt_addr_copy(&adv->addr.a, (const bt_addr_t *)addr);
adv->sid = sid;
end:
bt_le_host_unlock();
@@ -136,6 +140,9 @@ int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv,
return -EINVAL;
}
info->sid = adv->sid;
info->addr = &adv->addr;
/* Force to use ENABLED state for LIB usage */
info->ext_adv_state = BT_LE_EXT_ADV_STATE_ENABLED;
info->per_adv_state = BT_LE_PER_ADV_STATE_ENABLED;
@@ -400,6 +400,17 @@ static void handle_security_change_event_safe(struct bt_le_gap_app_param *param)
assert(err == 0);
}
/* Point conn->le.keys at the bonded LTK the adapter captured, so the lib's
* CSIS SIRK encryption can read it. conn exists here (found or just made). */
if (param->security_change.ltk_present) {
conn = bt_le_acl_conn_find(event.security_change.conn_handle);
if (conn != NULL) {
bt_conn_le_set_ltk(conn, param->security_change.ltk);
} else {
LOG_WRN("SecChgLtkNoConn[%u]", event.security_change.conn_handle);
}
}
err = bt_le_acl_conn_security_changed_listener(event.security_change.conn_handle,
event.security_change.sec_level);
assert(err == 0);
@@ -25,6 +25,11 @@ LOG_MODULE_REGISTER(ISO_CONN, CONFIG_BT_ISO_LOG_LEVEL);
static struct bt_conn acl_conns[CONFIG_BT_MAX_CONN];
/* Per-ACL LTK backing store (indexed in lockstep with acl_conns[]). conn->le.keys is
* a bare pointer the adapters fill after bonding; point it at the matching slot so the
* lib's CSIS sirk_encrypt can read conn->le.keys->ltk.val. */
static struct bt_keys conn_ltk[CONFIG_BT_MAX_CONN];
extern struct bt_conn iso_conns[CONFIG_BT_ISO_MAX_CHAN];
static sys_slist_t conn_cbs = SYS_SLIST_STATIC_INIT(&conn_cbs);
@@ -386,6 +391,33 @@ int bt_le_acl_conn_new(uint16_t conn_handle,
return (conn ? 0 : -ENOMEM);
}
/* Point conn->le.keys at this ACL connection's LTK slot, filled with the bonded
* LTK the adapter captured. Used as key K by the lib's CSIS SIRK encryption. */
_IDF_ONLY
void bt_conn_le_set_ltk(struct bt_conn *conn, const uint8_t *ltk)
{
size_t idx;
if (conn == NULL || ltk == NULL) {
LOG_ERR("ConnSetLtkBadArg");
return;
}
/* Only ACL (LE) connections carry an LTK; the pool tracks acl_conns[]. */
if (conn < acl_conns || conn >= &acl_conns[ARRAY_SIZE(acl_conns)]) {
LOG_WRN("ConnSetLtkNotAcl");
return;
}
idx = (size_t)(conn - acl_conns);
memset(&conn_ltk[idx], 0, sizeof(conn_ltk[idx]));
memcpy(conn_ltk[idx].ltk.val, ltk, sizeof(conn_ltk[idx].ltk.val));
conn->le.keys = &conn_ltk[idx];
LOG_INF("ConnSetLtk[%u][%s]", conn->handle, bt_hex(ltk, 16));
}
_IDF_ONLY
int bt_le_acl_conn_delete(uint16_t conn_handle)
{
@@ -400,6 +432,10 @@ int bt_le_acl_conn_delete(uint16_t conn_handle)
return -ENOTCONN;
}
/* Wipe this connection's LTK slot (key hygiene); the memset below then nulls
* conn->le.keys. */
memset(&conn_ltk[conn - acl_conns], 0, sizeof(conn_ltk[0]));
memset(conn, 0, sizeof(struct bt_conn));
return 0;
@@ -685,5 +721,9 @@ int bt_le_acl_conn_bond_deleted_listener(uint8_t id, const bt_addr_le_t *peer)
}
}
/* Profiles above cleared their own per-client state; also drop the peer's
* retained server CCC cfg, which is only kept while the bond exists. */
bt_le_acl_conn_bond_deleted_gatt_listener(id, peer);
return 0;
}
+215 -63
View File
@@ -19,6 +19,7 @@
#include <zephyr/bluetooth/common/bt_str.h>
#include <../host/conn_internal.h>
#include <../host/hci_core.h>
#include "common/host.h"
@@ -28,7 +29,53 @@ static sys_slist_t gatt_db = SYS_SLIST_STATIC_INIT(&gatt_db);
static struct gattc_sub subscriptions[CONFIG_BT_MAX_CONN];
/* Each CCC descriptor's cfg is a heap pool (depth BT_GATT_CCC_MAX = bonded peers +
* active conn), allocated here at registration; freed in gatts_free_svc_ccc_cfg. */
_LIB_ONLY
static int gatts_alloc_svc_ccc_cfg(struct bt_gatt_service *svc)
{
for (size_t i = 0; i < svc->attr_count; i++) {
const struct bt_gatt_attr *attr = &svc->attrs[i];
struct bt_gatt_ccc_managed_user_data *ccc;
if (attr->uuid->type != BT_UUID_TYPE_16 ||
BT_UUID_16(attr->uuid)->val != BT_UUID_GATT_CCC_VAL) {
continue;
}
ccc = attr->user_data;
ccc->cfg = calloc(BT_GATT_CCC_MAX, sizeof(struct bt_gatt_ccc_cfg));
if (ccc->cfg == NULL) {
return -ENOMEM;
}
ccc->cfg_count = BT_GATT_CCC_MAX;
}
return 0;
}
static void gatts_free_svc_ccc_cfg(struct bt_gatt_service *svc)
{
for (size_t i = 0; i < svc->attr_count; i++) {
const struct bt_gatt_attr *attr = &svc->attrs[i];
struct bt_gatt_ccc_managed_user_data *ccc;
if (attr->uuid->type != BT_UUID_TYPE_16 ||
BT_UUID_16(attr->uuid)->val != BT_UUID_GATT_CCC_VAL) {
continue;
}
ccc = attr->user_data;
if (ccc->cfg != NULL) {
free(ccc->cfg);
ccc->cfg = NULL;
}
ccc->cfg_count = 0;
ccc->value = 0; /* else a re-registered svc keeps the old subscription
state and suppresses the next cfg_changed. */
}
}
int bt_gatt_service_register(struct bt_gatt_service *svc)
{
const struct bt_uuid_16 *uuid;
@@ -52,6 +99,13 @@ int bt_gatt_service_register(struct bt_gatt_service *svc)
sys_slist_append(&gatt_db, &svc->node);
if (gatts_alloc_svc_ccc_cfg(svc) != 0) {
LOG_ERR("GattSvcCccAllocFail");
gatts_free_svc_ccc_cfg(svc);
(void)sys_slist_find_and_remove(&gatt_db, &svc->node);
return -ENOMEM;
}
return 0;
}
@@ -80,6 +134,8 @@ int bt_gatt_service_unregister(struct bt_gatt_service *svc)
return -ENOENT;
}
gatts_free_svc_ccc_cfg(svc);
return 0;
}
@@ -166,7 +222,7 @@ uint16_t bt_gatt_attr_value_handle(const struct bt_gatt_attr *attr)
*/
if (attr) {
LOG_DBG("GattAttrUuid[%s]", attr->uuid ? bt_uuid_str(attr->uuid) : "Null");
LOG_DBG("GattAttrValHdlUuid[%s]", attr->uuid ? bt_uuid_str(attr->uuid) : "Null");
if (attr->uuid == NULL) {
handle = (attr->handle + 1);
@@ -251,7 +307,7 @@ static uint8_t gatt_foreach_iter(const struct bt_gatt_attr *attr,
/* Match attribute user_data if set */
if (attr_data && attr_data != attr->user_data) {
LOG_DBG("MismatchAttrData");
LOG_DBG("GattForeachIterMismatchData");
return BT_GATT_ITER_CONTINUE;
}
@@ -260,11 +316,11 @@ static uint8_t gatt_foreach_iter(const struct bt_gatt_attr *attr,
result = func(attr, handle, user_data);
if (*num_matches == 0) {
LOG_DBG("NumMatchesZero");
LOG_DBG("GattForeachIterNumMatchesZero");
return BT_GATT_ITER_STOP;
}
LOG_DBG("GattIter%s", result == BT_GATT_ITER_CONTINUE ? "Cont" : "Stop");
LOG_DBG("GattForeachIter%s", result == BT_GATT_ITER_CONTINUE ? "Cont" : "Stop");
return result;
}
@@ -359,9 +415,9 @@ bool bt_gatts_find_attr_by_uuid(struct notify_data *found, const struct bt_uuid
bt_gatt_foreach_attr_type(found->handle, 0xffff, uuid, NULL, 1, match_uuid, found);
if (found->attr) {
LOG_DBG("AttrFoundByUuid");
LOG_DBG("GattsFindAttrByUuidFound");
} else {
LOG_WRN("AttrNotFoundByUuid");
LOG_WRN("GattsFindAttrByUuidNotFound");
}
return (found->attr ? true : false);
@@ -397,7 +453,7 @@ ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
LOG_DBG("GattAttrRd[%u][%u][%u]", buf_len, offset, value_len);
if (offset > value_len) {
LOG_ERR("TooLargeOffset[%u][%u]", offset, value_len);
LOG_ERR("GattAttrRdTooLargeOffset[%u][%u]", offset, value_len);
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
@@ -454,12 +510,12 @@ int bt_gatt_indicate(struct bt_conn *conn, struct bt_gatt_indicate_params *param
}
if ((!params->data ^ !params->len)) {
LOG_ERR("InvData[%p][%u]", params->data, params->len);
LOG_ERR("GattIndInvData[%p][%u]", params->data, params->len);
return -EINVAL;
}
if (params->attr->uuid->type != BT_UUID_TYPE_16) {
LOG_ERR("InvUuidType[%u]", params->attr->uuid->type);
LOG_ERR("GattIndInvUuidType[%u]", params->attr->uuid->type);
return -ENOTSUP;
}
@@ -503,7 +559,7 @@ static uint8_t gatts_get_svc_handles(const struct bt_gatt_attr *attr, uint16_t h
/* Stop if attribute is a service */
if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) == 0 ||
bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY) == 0) {
LOG_DBG("SvcAttr");
LOG_DBG("GattsSvcAttr");
return BT_GATT_ITER_STOP;
}
@@ -580,36 +636,53 @@ ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn,
memcpy(pdu.uuid, BT_UUID_128(chrc->uuid)->val, 16);
value_len += 16U;
} else {
LOG_ERR("UnsupportedUuidType[%u]", chrc->uuid->type);
LOG_ERR("GattAttrRdNotSupUuidType[%u]", chrc->uuid->type);
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
}
return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len);
}
static struct bt_gatt_ccc_cfg *gatts_find_ccc_cfg(const struct bt_conn *conn,
struct bt_gatt_ccc_managed_user_data *ccc)
static struct bt_gatt_ccc_cfg *gatts_find_free_ccc_cfg(struct bt_gatt_ccc_managed_user_data *ccc)
{
/* LOG_DBG("GattsFindCccCfg"); */
for (size_t i = 0; i < ARRAY_SIZE(ccc->cfg); i++) {
for (size_t i = 0; i < ccc->cfg_count; i++) {
struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i];
if (conn) {
if (bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer)) {
/* LOG_DBG("CccCfgFound[%u]", i); */
return cfg;
}
} else if (bt_addr_le_eq(&cfg->peer, BT_ADDR_LE_ANY)) {
LOG_DBG("CccCfgNew[%u]", i);
if (bt_addr_le_eq(&cfg->peer, BT_ADDR_LE_ANY)) {
LOG_DBG("GattsCccCfgNew[%u]", i);
return cfg;
}
}
if (conn) {
/* LOG_DBG("CccCfgNotFound"); */
} else {
LOG_ERR("CccNoFreeCfg");
LOG_WRN("GattsCccNoFreeCfg");
return NULL;
}
static struct bt_gatt_ccc_cfg *gatts_find_ccc_cfg_by_conn(const struct bt_conn *conn,
struct bt_gatt_ccc_managed_user_data *ccc)
{
assert(conn);
for (size_t i = 0; i < ccc->cfg_count; i++) {
struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i];
if (bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer)) {
return cfg;
}
}
return NULL;
}
static struct bt_gatt_ccc_cfg *gatts_find_ccc_cfg_by_addr(const bt_addr_le_t *peer,
struct bt_gatt_ccc_managed_user_data *ccc)
{
for (size_t i = 0; i < ccc->cfg_count; i++) {
struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i];
if (bt_addr_le_eq(&cfg->peer, peer)) {
return cfg;
}
}
return NULL;
@@ -620,7 +693,7 @@ static void gatts_ccc_changed(const struct bt_gatt_attr *attr,
{
uint16_t value = 0x0000;
for (size_t i = 0; i < ARRAY_SIZE(ccc->cfg); i++) {
for (size_t i = 0; i < ccc->cfg_count; i++) {
if (ccc->cfg[i].value > value) {
value = ccc->cfg[i].value;
}
@@ -645,6 +718,62 @@ static void gatts_clear_ccc_cfg(struct bt_gatt_ccc_cfg *cfg)
cfg->value = 0U;
}
static void gatts_clear_ccc_cfg_by_conn(struct bt_conn *conn)
{
/* Free this peer's CCC cfg entries. The pool is only BT_GATT_CCC_MAX deep, so a
* stale entry would starve a later different peer's subscribe (CccNoFreeCfg). */
struct bt_gatt_service *svc;
SYS_SLIST_FOR_EACH_CONTAINER(&gatt_db, svc, node) {
for (size_t i = 0; i < svc->attr_count; i++) {
const struct bt_gatt_attr *attr = &svc->attrs[i];
struct bt_gatt_ccc_managed_user_data *ccc;
struct bt_gatt_ccc_cfg *cfg;
if (attr->uuid->type != BT_UUID_TYPE_16 ||
BT_UUID_16(attr->uuid)->val != BT_UUID_GATT_CCC_VAL) {
continue;
}
ccc = attr->user_data;
cfg = gatts_find_ccc_cfg_by_conn(conn, ccc);
if (cfg != NULL) {
gatts_clear_ccc_cfg(cfg);
gatts_ccc_changed(attr, ccc);
}
}
}
}
static void gatts_clear_ccc_cfg_by_addr(const bt_addr_le_t *peer)
{
/* Address-keyed twin of gatts_clear_ccc_cfg_by_conn: bond-delete carries only
* the identity address, with no bt_conn to key on. */
struct bt_gatt_service *svc;
SYS_SLIST_FOR_EACH_CONTAINER(&gatt_db, svc, node) {
for (size_t i = 0; i < svc->attr_count; i++) {
const struct bt_gatt_attr *attr = &svc->attrs[i];
struct bt_gatt_ccc_managed_user_data *ccc;
struct bt_gatt_ccc_cfg *cfg;
if (attr->uuid->type != BT_UUID_TYPE_16 ||
BT_UUID_16(attr->uuid)->val != BT_UUID_GATT_CCC_VAL) {
continue;
}
ccc = attr->user_data;
cfg = gatts_find_ccc_cfg_by_addr(peer, ccc);
if (cfg != NULL) {
gatts_clear_ccc_cfg(cfg);
gatts_ccc_changed(attr, ccc);
}
}
}
}
_LIB_IDF
ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
@@ -662,7 +791,7 @@ ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn,
LOG_DBG("GattAttrRdCcc[%u][%u]", len, offset);
cfg = gatts_find_ccc_cfg(conn, ccc);
cfg = gatts_find_ccc_cfg_by_conn(conn, ccc);
if (cfg) {
value = sys_cpu_to_le16(cfg->value);
} else {
@@ -690,7 +819,7 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
ccc = attr->user_data;
assert(ccc);
LOG_DBG("GattAttrWrChrc[%u][%u][%02x]", len, offset, flags);
LOG_DBG("GattAttrWrCcc[%u][%u][%02x]", len, offset, flags);
if (offset) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
@@ -708,18 +837,18 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
new_entry = false;
cfg = gatts_find_ccc_cfg(conn, ccc);
cfg = gatts_find_ccc_cfg_by_conn(conn, ccc);
if (cfg == NULL) {
/* If there's no existing entry, but the new value is zero,
* we don't need to do anything, since a disabled CCC is
* behaviorally the same as no written CCC.
*/
if (value == 0) {
LOG_INF("CccValZero");
LOG_INF("GattAttrWrCccValZero");
return len;
}
cfg = gatts_find_ccc_cfg(NULL, ccc);
cfg = gatts_find_free_ccc_cfg(ccc);
if (cfg == NULL) {
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
}
@@ -734,7 +863,7 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
if (ccc->cfg_write) {
ssize_t write = ccc->cfg_write(conn, attr, value);
LOG_INF("CccCfgWr[%d]", write);
LOG_INF("GattAttrWrCccCfgWr[%d]", write);
if (write < 0) {
if (new_entry) {
@@ -752,7 +881,7 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
}
}
LOG_DBG("CccCfg[%u][%04x][%04x]", attr->handle, cfg->value, value);
LOG_DBG("GattAttrWrCccCfg[%u][%04x][%04x]", attr->handle, cfg->value, value);
value_changed = (cfg->value != value);
cfg->value = value;
@@ -798,32 +927,32 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn,
ssize_t len;
if (attr->read == NULL) {
LOG_WRN("AttrRdCbNull");
LOG_WRN("GattIsSubAttrRdCbNull");
return false;
}
/* The characteristic properties is the first byte of the attribute value */
len = attr->read(NULL, attr, &properties, 1, 0);
if (len < 0) {
LOG_ERR("RdAttrFail[%zd]", len);
LOG_ERR("GattIsSubRdAttrFail[%zd]", len);
return false;
}
if (len != 1) {
LOG_ERR("InvRdLen[%zd]", len);
LOG_ERR("GattIsSubInvRdLen[%zd]", len);
return false;
}
if (!(properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) {
/* Characteristic doesn't support subscription */
LOG_WRN("SubNotSupp[%02x]", properties);
LOG_WRN("GattIsSubNotSupp[%02x]", properties);
return false;
}
attr = bt_gatt_attr_next(attr);
assert(attr && attr->uuid);
LOG_DBG("Attr[%s][%d]", bt_uuid_str(attr->uuid), __LINE__);
LOG_DBG("GattIsSubAttr[%s][%d]", bt_uuid_str(attr->uuid), __LINE__);
}
/* Check if attribute is a characteristic value */
@@ -834,7 +963,7 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn,
}
assert(attr->uuid);
LOG_DBG("Attr[%s][%d]", bt_uuid_str(attr->uuid), __LINE__);
LOG_DBG("GattIsSubAttr[%s][%d]", bt_uuid_str(attr->uuid), __LINE__);
}
/* Find the CCC Descriptor */
@@ -851,7 +980,7 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn,
assert(attr->uuid);
LOG_DBG("Attr[%s][%d]", bt_uuid_str(attr->uuid), __LINE__);
LOG_DBG("GattIsSubAttr[%s][%d]", bt_uuid_str(attr->uuid), __LINE__);
if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC)) {
return false;
@@ -861,10 +990,10 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn,
assert(ccc);
/* Check if the connection is subscribed */
for (size_t i = 0; i < ARRAY_SIZE(ccc->cfg); i++) {
for (size_t i = 0; i < ccc->cfg_count; i++) {
const struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i];
LOG_DBG("CccCfg[%u][%04x]", i, ccc->cfg[i].value);
LOG_DBG("GattIsSubCccCfg[%u][%04x]", i, ccc->cfg[i].value);
if (bt_conn_is_peer_addr_le(conn, cfg->id, &cfg->peer) &&
(ccc_type & ccc->cfg[i].value)) {
@@ -888,18 +1017,18 @@ static int gatts_ccc_cfg_update(struct bt_conn *conn,
/* LOG_DBG("GattsCccCfgUpd[%04x]", value); */
cfg = gatts_find_ccc_cfg(conn, ccc);
cfg = gatts_find_ccc_cfg_by_conn(conn, ccc);
if (cfg == NULL) {
/* If there's no existing entry, and the new notification value
* is zero, we don't need to do anything, since a disabled CCC
* is behaviorally the same as no written CCC.
*/
if (value == 0) {
LOG_INF("CccValZero");
LOG_INF("GattsCccCfgUpdValZero");
return 0;
}
cfg = gatts_find_ccc_cfg(NULL, ccc);
cfg = gatts_find_free_ccc_cfg(ccc);
if (cfg == NULL) {
return -ENOMEM;
}
@@ -1014,7 +1143,7 @@ int bt_gatt_discover(struct bt_conn *conn, struct bt_gatt_discover_params *param
}
if (params->uuid && params->uuid->type != BT_UUID_TYPE_16) {
LOG_ERR("InvUuid[%s]", bt_uuid_str(params->uuid));
LOG_ERR("GattDiscInvUuid[%s]", bt_uuid_str(params->uuid));
return -ENOTSUP;
}
@@ -1034,7 +1163,7 @@ static struct gattc_sub *gattc_sub_find(struct bt_conn *conn)
if (conn == NULL) {
if (bt_addr_le_eq(&sub->peer, BT_ADDR_LE_ANY)) {
LOG_DBG("SubNew[%u]", i);
LOG_DBG("GattcSubFindNew[%u]", i);
return sub;
}
} else if (bt_conn_is_peer_addr_le(conn, sub->id, &sub->peer)) {
@@ -1046,7 +1175,7 @@ static struct gattc_sub *gattc_sub_find(struct bt_conn *conn)
if (conn) {
/* LOG_DBG("SubNotFound"); */
} else {
LOG_WRN("NoFreeSub");
LOG_WRN("GattcSubFindNoFree");
}
return NULL;
@@ -1064,7 +1193,7 @@ static struct gattc_sub *gattc_sub_add(struct bt_conn *conn)
if (sub) {
bt_addr_le_copy(&sub->peer, &conn->le.dst);
sub->id = conn->id;
LOG_DBG("SubPeer[%s]", bt_addr_le_str(&sub->peer));
LOG_DBG("GattcSubAddPeer[%s]", bt_addr_le_str(&sub->peer));
}
}
@@ -1098,6 +1227,10 @@ static void gattc_sub_clear(struct bt_conn *conn)
tmp->value = 0;
}
sys_slist_init(&sub->list);
/* Release the pool slot back to free. Without this it stays bound to the disconnected
* peer's address, so a later connection from a different peer finds no free slot -> -ENOMEM. */
bt_addr_le_copy(&sub->peer, BT_ADDR_LE_ANY);
}
_IDF_ONLY
@@ -1126,7 +1259,7 @@ static uint8_t gattc_ccc_discover_cb(struct bt_conn *conn,
if (params->type == BT_GATT_DISCOVER_DESCRIPTOR) {
memset(params, 0, sizeof(*params));
LOG_DBG("CccHdl[%u]", attr->handle);
LOG_DBG("GattcCccDiscCbHdl[%u]", attr->handle);
sub_params->ccc_handle = attr->handle;
@@ -1193,7 +1326,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *par
* can resubscribe.
*/
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("GattSubNotConn[%u][%u]", conn->handle, conn->state);
LOG_ERR("GattcSubNotConn[%u][%u]", conn->handle, conn->state);
params->value_handle = 0; /* unlinked: clear retry guard */
return -ENOTCONN;
}
@@ -1219,7 +1352,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *par
*/
if (tmp->value_handle == params->value_handle &&
tmp->value >= params->value) {
LOG_INF("AnotherSubExist[%04x]", tmp->value);
LOG_INF("GattcSubAnotherExist[%04x]", tmp->value);
has_subscription = true;
}
}
@@ -1232,7 +1365,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *par
if (params->ccc_handle == BT_GATT_AUTO_DISCOVER_CCC_HANDLE) {
int err = gattc_ccc_discover(conn, params);
if (err) {
LOG_ERR("DiscCccFail[%d]", err);
LOG_ERR("GattcSubDiscCccFail[%d]", err);
params->value_handle = 0; /* unlinked: clear retry guard */
return err;
}
@@ -1277,7 +1410,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *p
params->value, params->value_handle, params->ccc_handle, params->end_handle);
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("GattUnsubNotConn[%u][%u]", conn->handle, conn->state);
LOG_ERR("GattcUnsubNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -1298,7 +1431,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *p
* characteristic value handle.
*/
if (tmp->value_handle == params->value_handle) {
LOG_INF("AnotherSubFound");
LOG_INF("GattcUnsubAnotherSub");
has_subscription = true;
}
}
@@ -1321,7 +1454,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *p
* the write was issued. Not a real failure.
*/
if (err != -ENOTCONN) {
LOG_ERR("WrCccFail[%d]", err);
LOG_ERR("GattcUnsubWrCccFail[%d]", err);
}
return err;
}
@@ -1330,7 +1463,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *p
sys_slist_find_and_remove(&sub->list, &params->node);
if (sys_slist_is_empty(&sub->list)) {
LOG_DBG("SubListEmpty");
LOG_DBG("GattcUnsubListEmpty");
bt_addr_le_copy(&sub->peer, BT_ADDR_LE_ANY);
}
@@ -1358,7 +1491,7 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
}
if (params->handle_count > 1) {
LOG_ERR("NotSuppToRdHdl[%u]", params->handle_count);
LOG_ERR("GattRdNotSuppHdl[%u]", params->handle_count);
return -ENOTSUP;
}
@@ -1366,7 +1499,7 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
assert(params->by_uuid.uuid);
if (params->by_uuid.uuid->type != BT_UUID_TYPE_16) {
LOG_ERR("InvUuid[%s]", bt_uuid_str(params->by_uuid.uuid));
LOG_ERR("GattRdInvUuid[%s]", bt_uuid_str(params->by_uuid.uuid));
return -ENOTSUP;
}
}
@@ -1395,7 +1528,7 @@ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params)
}
if (params->offset != 0) {
LOG_ERR("PrepWrNotSupp[%u][%u]", params->handle, params->offset);
LOG_ERR("GattWrPrepNotSupp[%u][%u]", params->handle, params->offset);
return -ENOTSUP;
}
@@ -1427,7 +1560,7 @@ int bt_gatt_write_without_response_cb(struct bt_conn *conn, uint16_t handle,
}
if (sign) {
LOG_ERR("WrCmdNotSupp");
LOG_ERR("GattWrCmdNotSupp");
return -ENOTSUP;
}
@@ -1472,9 +1605,28 @@ void bt_le_acl_conn_disconnected_gatt_listener(uint16_t conn_handle)
conn = bt_le_acl_conn_find(conn_handle);
if (conn) {
gattc_sub_clear(conn);
/* Bonded peers keep their cfg (reconnect notifications resume without a
* re-subscribe); free non-bonded peers' cfg so the pool isn't leaked. */
if (!bt_le_bond_exists(conn->id, &conn->le.dst)) {
gatts_clear_ccc_cfg_by_conn(conn);
}
}
}
_LIB_ONLY
void bt_le_acl_conn_bond_deleted_gatt_listener(uint8_t id, const bt_addr_le_t *peer)
{
ARG_UNUSED(id);
/* Cfg is retained across disconnect only for bonded peers (see disconnect
* listener), so bond deletion must drop it — else an unbonded reconnect from
* that address gets notified for a subscription it never made. */
LOG_DBG("AclConnBondDeletedGattListener");
gatts_clear_ccc_cfg_by_addr(peer);
}
void bt_le_gatt_handle_event(uint8_t *data, size_t data_len)
{
assert(data && data_len);
@@ -17,7 +17,8 @@ extern "C" {
struct bt_le_ext_adv *bt_le_ext_adv_find(uint8_t adv_handle);
int bt_le_ext_adv_new_safe(uint8_t adv_handle);
int bt_le_ext_adv_new_safe(uint8_t adv_handle, uint8_t addr_type,
const uint8_t *addr, uint8_t sid);
int bt_le_ext_adv_delete_safe(uint8_t adv_handle);
@@ -94,6 +94,10 @@ struct bt_le_gap_app_security_change_param {
uint8_t sec_level;
uint8_t bonded : 1;
struct bt_le_addr dst;
/* Bonded LTK (CSIS SIRK-encryption key K), captured by the adapter; the
* safe handler hands it to bt_conn_le_set_ltk. Valid only if ltk_present. */
uint8_t ltk[16];
uint8_t ltk_present : 1;
};
struct bt_le_gap_app_identity_resolve_param {
@@ -41,6 +41,8 @@ int bt_le_acl_conn_new(uint16_t conn_handle,
int bt_le_acl_conn_delete(uint16_t conn_handle);
void bt_conn_le_set_ltk(struct bt_conn *conn, const uint8_t *ltk);
int bt_le_acl_conn_update(uint16_t conn_handle,
uint8_t sec_level,
bool *encrypted);
@@ -50,6 +50,7 @@ struct bt_le_gattc_discover_event {
struct bt_le_gattc_disc_cmpl_event {
uint8_t status;
uint16_t conn_handle;
uint16_t conn_id;
};
struct bt_le_gatts_subscribe_event {
@@ -65,6 +66,7 @@ struct bt_le_gatts_subscribe_event {
struct bt_le_gattc_notify_rx_event {
bool is_notify;
uint16_t conn_handle;
uint16_t conn_id;
uint16_t attr_handle;
uint16_t len;
uint8_t *value;
@@ -73,6 +75,7 @@ struct bt_le_gattc_notify_rx_event {
struct bt_le_gatts_notify_tx_event {
bool is_notify;
uint16_t conn_handle;
uint16_t conn_id;
uint16_t attr_handle;
uint8_t status;
};
@@ -90,29 +93,34 @@ struct bt_le_addr_simple {
struct bt_le_gattc_connect_event {
uint16_t conn_handle;
uint16_t conn_id;
uint8_t role;
struct bt_le_addr_simple peer;
};
struct bt_le_gattc_disconnect_event {
uint16_t conn_handle;
uint16_t conn_id;
uint8_t reason;
};
struct bt_le_gattc_open_event {
uint8_t status;
uint16_t conn_handle;
uint16_t conn_id;
};
struct bt_le_gattc_mtu_event {
uint8_t status;
uint16_t conn_handle;
uint16_t conn_id;
uint16_t mtu;
};
struct bt_le_gattc_read_chrc_event {
uint8_t status;
uint16_t conn_handle;
uint16_t conn_id;
uint16_t attr_handle;
uint16_t len;
uint8_t *value;
@@ -121,28 +129,33 @@ struct bt_le_gattc_read_chrc_event {
struct bt_le_gattc_write_chrc_event {
uint8_t status;
uint16_t conn_handle;
uint16_t conn_id;
uint16_t attr_handle;
uint16_t offset;
};
struct bt_le_gatts_connect_event {
uint16_t conn_handle;
uint16_t conn_id;
uint8_t role;
struct bt_le_addr_simple peer;
};
struct bt_le_gatts_disconnect_event {
uint16_t conn_handle;
uint16_t conn_id;
uint8_t reason;
};
struct bt_le_gatts_mtu_event {
uint16_t conn_handle;
uint16_t conn_id;
uint16_t mtu;
};
struct bt_le_gatts_read_event {
uint16_t conn_handle;
uint16_t conn_id;
uint32_t trans_id;
uint8_t peer[6];
uint16_t attr_handle;
@@ -153,6 +166,7 @@ struct bt_le_gatts_read_event {
struct bt_le_gatts_write_event {
uint16_t conn_handle;
uint16_t conn_id;
uint32_t trans_id;
uint8_t peer[6];
uint16_t attr_handle;
@@ -163,6 +177,13 @@ struct bt_le_gatts_write_event {
uint8_t *value;
};
struct bt_le_gatts_exec_write_event {
uint16_t conn_handle;
uint16_t conn_id;
uint32_t trans_id;
bool exec; /* true = execute the queued long write, false = cancel */
};
struct bt_le_gatt_event_param {
uint8_t type;
@@ -186,6 +207,7 @@ struct bt_le_gatt_event_param {
struct bt_le_gatts_mtu_event gatts_mtu;
struct bt_le_gatts_read_event gatts_read;
struct bt_le_gatts_write_event gatts_write;
struct bt_le_gatts_exec_write_event gatts_exec_write;
};
};
@@ -209,6 +231,7 @@ enum {
BT_LE_GATTS_MTU_EVENT,
BT_LE_GATTS_READ_EVENT,
BT_LE_GATTS_WRITE_EVENT,
BT_LE_GATTS_EXEC_WRITE_EVENT,
BT_LE_GATT_EVENT_MAX,
};
@@ -253,7 +276,7 @@ struct notify_data {
struct bt_gatt_attr *bt_gatts_find_attr_by_handle(uint16_t handle);
bool bt_gatts_find_attr_by_uuid(struct notify_data *found,
const struct bt_uuid *uuid);
const struct bt_uuid *uuid);
int bt_gatts_sub_changed(uint16_t conn_handle,
uint16_t ccc_handle,
@@ -267,6 +290,8 @@ struct gattc_sub *bt_gattc_sub_find(struct bt_conn *conn);
void bt_le_acl_conn_disconnected_gatt_listener(uint16_t conn_handle);
void bt_le_acl_conn_bond_deleted_gatt_listener(uint8_t id, const bt_addr_le_t *peer);
void bt_le_gatt_handle_event(uint8_t *data, size_t data_len);
#ifdef __cplusplus
+17 -12
View File
@@ -91,14 +91,14 @@ int bt_le_iso_cb_register_safe(struct bt_le_iso_cb *cb)
LOG_DBG("IsoCbReg");
if (cb == NULL) {
LOG_ERR("IsoCbNull");
LOG_ERR("IsoCbRegNull");
return -EINVAL;
}
bt_le_host_lock();
if (sys_slist_find(&iso_cbs, &cb->node, NULL)) {
LOG_WRN("IsoCbExist");
LOG_WRN("IsoCbRegExist");
err = -EEXIST;
goto end;
}
@@ -117,7 +117,7 @@ void bt_le_iso_cb_unregister_safe(struct bt_le_iso_cb *cb)
LOG_DBG("IsoCbUnreg");
if (cb == NULL) {
LOG_ERR("IsoCbNull");
LOG_ERR("IsoCbUnregNull");
return;
}
@@ -415,7 +415,7 @@ static int iso_tx_sdu_insert(struct bt_iso_chan *chan, const uint8_t *sdu, uint1
sdu_node = calloc(1, sizeof(*sdu_node));
if (sdu_node == NULL) {
LOG_ERR("IsoTxInsertNoMem[%u]", sizeof(*sdu_node));
LOG_ERR("IsoTxSduInsertNoMem[%u]", sizeof(*sdu_node));
return -ENOMEM;
}
@@ -445,7 +445,7 @@ static void iso_tx_sdu_clear(uint16_t handle, bool is_big)
if ((is_big == false && iso->handle == handle) ||
(is_big && iso->iso.info.type == BT_ISO_CHAN_TYPE_BROADCASTER &&
iso->iso.info.broadcaster.big_handle == handle)) {
LOG_DBG("IsoSduRem[%u]", sdu_node->sdu_len);
LOG_DBG("IsoTxSduClearRem[%u]", sdu_node->sdu_len);
sys_slist_remove(&iso_tx_sdu_list, prev, &sdu_node->node);
@@ -463,22 +463,27 @@ static int validate_iso_send(struct bt_iso_chan *chan, struct net_buf *buf)
{
if (chan == NULL || buf == NULL ||
(buf->data == NULL && buf->len != 0)) {
LOG_ERR("InvSendParam[%p][%p]", chan, buf);
LOG_ERR("IsoSendInvParam[%p][%p]", chan, buf);
return -EINVAL;
}
if (chan->iso == NULL) {
LOG_ERR("IsoSendChanNoIso");
return -ENOTCONN;
}
if (chan->state != BT_ISO_STATE_CONNECTED) {
LOG_ERR("ChanNotConn[%u]", chan->iso->handle);
LOG_ERR("IsoSendChanNotConn[%u]", chan->iso->handle);
return -ENOTCONN;
}
if (chan->iso->iso.info.can_send == false) {
LOG_ERR("ChanNotSend[%u]", chan->iso->handle);
LOG_ERR("IsoSendChanNotSend[%u]", chan->iso->handle);
return -EINVAL;
}
if (buf->len > chan->qos->tx->sdu) {
LOG_ERR("ChanCannotSend[%u][%u][%u]",
LOG_ERR("IsoSendChanCannotSend[%u][%u][%u]",
chan->iso->handle, buf->len, chan->qos->tx->sdu);
return -EMSGSIZE;
}
@@ -510,7 +515,7 @@ static int iso_tx_now(struct bt_iso_chan *chan, const uint8_t *sdu,
pkt = malloc(4 + data_total_len);
if (pkt == NULL) {
LOG_ERR("IsoTxNoMem[%u]", 4 + data_total_len);
LOG_ERR("IsoTxNowNoMem[%u]", 4 + data_total_len);
return -ENOMEM;
}
@@ -563,7 +568,7 @@ static int iso_tx_now(struct bt_iso_chan *chan, const uint8_t *sdu,
* If using controller from other vendors, the pkt buffer may
* needs to be freed here.
*/
LOG_WRN("IsoTxFail[%d]", err);
LOG_WRN("IsoTxNowFail[%d]", err);
return err;
}
@@ -642,7 +647,7 @@ void bt_le_iso_handle_tx_comp(uint8_t *data, size_t data_len)
* If using controller from other vendors, the pkt buffer may
* needs to be freed here.
*/
LOG_WRN("IsoTxFail[%d]", err);
LOG_WRN("IsoTxCompFail[%d]", err);
}
free(sdu_node);
+11 -3
View File
@@ -12,6 +12,7 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/iso.h>
#include <zephyr/bluetooth/hci_types.h>
@@ -199,7 +200,7 @@ struct bt_le_per_adv_sync *bt_le_per_adv_sync_lookup_addr(const bt_addr_le_t *ad
assert(adv_addr);
LOG_DBG("PaSyncLookupAddr[%s][%u]", bt_addr_le_str(adv_addr), sid);
LOG_INF("PaSyncLookupAddr[%s][%u]", bt_addr_le_str(adv_addr), sid);
for (size_t i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) {
if (atomic_test_bit(per_adv_sync_pool[i].flags, BT_PER_ADV_SYNC_SYNCED) &&
@@ -275,7 +276,7 @@ int bt_le_per_adv_sync_new(uint16_t sync_handle,
{
struct bt_le_per_adv_sync *per_adv_sync;
LOG_DBG("PaSyncNew[%u][%u][%u][%u][%u]",
LOG_INF("PaSyncNew[%u][%u][%u][%u][%u]",
sync_handle, sid, phy, interval, conn_handle);
if (addr_type > BT_ADDR_LE_RANDOM_ID || addr == NULL) {
@@ -301,9 +302,16 @@ int bt_le_per_adv_sync_new(uint16_t sync_handle,
per_adv_sync->interval = interval;
per_adv_sync->conn_handle = conn_handle;
per_adv_sync->addr.type = addr_type;
#if CONFIG_BT_BLUEDROID_ENABLED
/* Bluedroid delivers the advertiser address MSB-first, but the lib compares in
* on-air/LSB-first order (bt_addr_le_t.a.val). NimBLE already supplies on-air
* order, so reverse only here. */
sys_memcpy_swap(per_adv_sync->addr.a.val, addr, BT_ADDR_SIZE);
#else
memcpy(per_adv_sync->addr.a.val, addr, BT_ADDR_SIZE);
#endif
LOG_DBG("Addr[%s]", bt_addr_le_str(&per_adv_sync->addr));
LOG_INF("PaSyncAddr[%s]", bt_addr_le_str(&per_adv_sync->addr));
if (out_sync) {
*out_sync = per_adv_sync;
+10 -1
View File
@@ -2881,7 +2881,16 @@ void hci_le_big_terminate(struct net_buf *buf)
LOG_DBG("BigTerminated[%u][%p]", big->handle, big);
big_disconnect(big, evt->reason);
/* Source BIG is only terminated by the local host → reason must be
* LOCALHOST_TERM (Core 7.7.65.28); fix up controllers reporting otherwise. */
uint8_t reason = evt->reason;
if (reason != BT_HCI_ERR_LOCALHOST_TERM_CONN) {
LOG_INF("BigTermReasonFixup[%02x][%02x]", reason, BT_HCI_ERR_LOCALHOST_TERM_CONN);
reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;
}
big_disconnect(big, reason);
}
#endif /* CONFIG_BT_ISO_BROADCASTER */
+12 -4
View File
@@ -251,14 +251,14 @@ int k_work_reschedule(struct k_work_delayable *dwork, k_timeout_t ms)
return 0;
}
int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_ms)
int k_work_schedule_periodic_us(struct k_work_delayable *dwork, uint64_t period_us)
{
int err;
LOG_DBG("WorkSchedulePeriodic[%p][%u]", dwork, period_ms);
LOG_DBG("WorkSchedulePeriodicUs[%p][%llu]", dwork, (unsigned long long)period_us);
assert(dwork);
assert(period_ms > 0);
assert(period_us > 0);
if (dwork->work.timer == NULL) {
LOG_WRN("TimerNotCreated");
@@ -267,7 +267,7 @@ int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_
esp_timer_stop(dwork->work.timer);
err = esp_timer_start_periodic(dwork->work.timer, (uint64_t)period_ms * 1000);
err = esp_timer_start_periodic(dwork->work.timer, period_us);
if (err) {
LOG_ERR("StartPeriodicTimerFail[%d]", err);
return -EIO;
@@ -276,6 +276,14 @@ int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_
return 0;
}
int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_ms)
{
/* Sub-millisecond ISO SDU intervals (e.g. 7500, 8163, 10884 us) lose
* precision when forced to whole milliseconds; callers that need the exact
* interval should use k_work_schedule_periodic_us(). */
return k_work_schedule_periodic_us(dwork, (uint64_t)period_ms * 1000);
}
k_timeout_t k_work_delayable_remaining_get(struct k_work_delayable *dwork)
{
k_timeout_t timeout;
+141 -15
View File
@@ -7,16 +7,27 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <zephyr/net_buf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/hci_types.h>
#include <zephyr/bluetooth/gap.h>
#include <zephyr/bluetooth/conn.h>
#include <../host/keys.h>
#include <../host/hci_core.h>
#if CONFIG_BT_BLUEDROID_ENABLED
#include "esp_gap_ble_api.h"
#else
#include "host/ble_gap.h"
#include "host/ble_store.h"
#endif
LOG_MODULE_REGISTER(ISO_UTILS, CONFIG_BT_ISO_LOG_LEVEL);
struct bt_dev bt_dev;
uint8_t bt_get_phy(uint8_t hci_phy)
@@ -29,44 +40,159 @@ uint8_t bt_get_phy(uint8_t hci_phy)
case BT_HCI_LE_PHY_CODED:
return BT_GAP_LE_PHY_CODED;
default:
LOG_WRN("UnknownHciPhy[%u]", hci_phy);
return 0;
}
}
static struct bt_keys key_pool[CONFIG_BT_MAX_PAIRED];
/* Query the active host's persistent bond store (the old local key_pool was never
* populated, so bt_le_bond_exists() was always false). `id` ignored (single identity);
* addresses stay in native host byte order. */
#if CONFIG_BT_BLUEDROID_ENABLED
void bt_foreach_bond(uint8_t id, void (*func)(const struct bt_bond_info *info,
void *user_data),
void *user_data)
{
int num = esp_ble_get_bond_device_num();
esp_ble_bond_dev_t *list;
assert(func);
(void)id;
for (size_t i = 0; i < ARRAY_SIZE(key_pool); i++) {
struct bt_keys *keys = &key_pool[i];
LOG_DBG("[B]ForeachBond[%d]", num);
if (keys->keys && keys->id == id) {
struct bt_bond_info info;
if (num <= 0) {
return;
}
bt_addr_le_copy(&info.addr, &keys->addr);
list = calloc(num, sizeof(*list));
if (list == NULL) {
LOG_ERR("[B]BondAllocFail[%d]", num);
return;
}
if (esp_ble_get_bond_device_list(&num, list) == ESP_OK) {
for (int i = 0; i < num; i++) {
struct bt_bond_info info = {0};
info.addr.type = list[i].bd_addr_type;
memcpy(info.addr.a.val, list[i].bd_addr, BT_ADDR_SIZE);
func(&info, user_data);
}
} else {
LOG_WRN("[B]BondReadFail");
}
free(list);
}
bool bt_le_bond_exists(uint8_t id, const bt_addr_le_t *addr)
{
int num = esp_ble_get_bond_device_num();
esp_ble_bond_dev_t *list;
bool found = false;
(void)id;
if (addr == NULL) {
LOG_WRN("[B]BondNullAddr");
return false;
}
if (num == 0) {
LOG_DBG("[B]BondNone"); /* genuinely no bonds stored */
return false;
}
/* A query failure (num<0, OOM, read error) doesn't mean unbonded — the record is
* intact, we just can't read it. Callers treat false as "not bonded" and drop the
* peer's CCC/keys, so fail safe to true rather than destroy a real bond's state. */
if (num < 0) {
LOG_WRN("[B]BondNumFail[%d]", num);
return true;
}
list = calloc(num, sizeof(*list));
if (list == NULL) {
LOG_ERR("[B]BondAllocFail[%d]", num);
return true;
}
if (esp_ble_get_bond_device_list(&num, list) != ESP_OK) {
LOG_WRN("[B]BondReadFail");
free(list);
return true;
}
for (int i = 0; i < num; i++) {
/* Match the 6-byte address only: bd_addr uniquely identifies a bond,
* and the type encodings (public/random vs *_ID/RPA) do not map 1:1
* across the host boundary. */
if (memcmp(list[i].bd_addr, addr->a.val, BT_ADDR_SIZE) == 0) {
found = true;
break;
}
}
LOG_INF("[B]BondExists[%d][%02x:%02x:%02x:%02x:%02x:%02x]", found,
addr->a.val[0], addr->a.val[1], addr->a.val[2],
addr->a.val[3], addr->a.val[4], addr->a.val[5]);
free(list);
return found;
}
#else /* NimBLE host */
void bt_foreach_bond(uint8_t id, void (*func)(const struct bt_bond_info *info,
void *user_data),
void *user_data)
{
ble_addr_t peers[CONFIG_BT_MAX_PAIRED];
int num = 0;
assert(func);
(void)id;
if (ble_store_util_bonded_peers(peers, &num, ARRAY_SIZE(peers)) != 0) {
LOG_WRN("[N]BondReadFail");
return;
}
LOG_DBG("[N]ForeachBond[%d]", num);
for (int i = 0; i < num; i++) {
struct bt_bond_info info = {0};
info.addr.type = peers[i].type;
memcpy(info.addr.a.val, peers[i].val, BT_ADDR_SIZE);
func(&info, user_data);
}
}
bool bt_le_bond_exists(uint8_t id, const bt_addr_le_t *addr)
{
struct ble_store_value_sec value = {0};
struct ble_store_key_sec key = {0};
(void)id;
if (addr == NULL) {
LOG_WRN("[N]BondNullAddr");
return false;
}
for (size_t i = 0; i < ARRAY_SIZE(key_pool); i++) {
struct bt_keys *keys = &key_pool[i];
key.peer_addr.type = addr->type;
memcpy(key.peer_addr.val, addr->a.val, BT_ADDR_SIZE);
if (keys->keys && keys->id == id &&
bt_addr_le_eq(&keys->addr, addr)) {
return true;
}
}
bool found = ble_store_read_peer_sec(&key, &value) == 0;
return false;
LOG_INF("[N]BondExists[%d][%02x:%02x:%02x:%02x:%02x:%02x]", found,
addr->a.val[0], addr->a.val[1], addr->a.val[2],
addr->a.val[3], addr->a.val[4], addr->a.val[5]);
return found;
}
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
@@ -9,6 +9,8 @@
#pragma once
#include <zephyr/bluetooth/addr.h>
/* bt_dev flags: the flags defined here represent BT controller state */
enum {
/** The application either explicitly or implicitly instructed the stack to scan
@@ -46,6 +48,12 @@ struct bt_le_ext_adv {
/* Advertising handle */
uint8_t handle;
/* Advertising address */
bt_addr_le_t addr;
/* Advertising SID */
uint8_t sid;
ATOMIC_DEFINE(flags, BT_ADV_NUM_FLAGS);
};
@@ -893,7 +893,10 @@ ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn,
})), \
BT_GATT_ATTRIBUTE(_uuid, _perm, _read, _write, _user_data)
#define BT_GATT_CCC_MAX (1)
/* Depth of each CCC's subscriber-config pool: one slot per bonded peer (subscription
* retained across disconnect for reconnect notify) + the active connection. A 1-deep
* pool made a second bonded subscriber fail with CccNoFreeCfg. */
#define BT_GATT_CCC_MAX (CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN)
/** @brief GATT CCC configuration entry.
*
@@ -916,7 +919,8 @@ struct bt_gatt_ccc_cfg {
/** Internal representation of CCC value */
struct bt_gatt_ccc_managed_user_data {
/** Configuration for each connection */
struct bt_gatt_ccc_cfg cfg[BT_GATT_CCC_MAX];
struct bt_gatt_ccc_cfg *cfg;
uint8_t cfg_count;
/** Highest value of all connected peer's subscriptions */
uint16_t value;
@@ -1011,13 +1015,12 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
* @param _match Configuration match callback.
*/
#define BT_GATT_CCC_MANAGED_USER_DATA_INIT(_changed, _write, _match) \
{ \
.cfg = { \
[0 ... (BT_GATT_CCC_MAX - 1)] = {0}, \
}, \
.cfg_changed = _changed, \
.cfg_write = _write, \
.cfg_match = _match, \
{ \
.cfg = NULL, \
.cfg_count = 0, \
.cfg_changed = _changed, \
.cfg_write = _write, \
.cfg_match = _match, \
}
/**
@@ -4153,6 +4153,15 @@ struct bt_uuid_128 {
*/
#define BT_UUID_CSIS_RANK \
BT_UUID_DECLARE_16(BT_UUID_CSIS_RANK_VAL)
/**
* @brief Coordinated Set Name value (CSIS v1.1)
*/
#define BT_UUID_CSIS_SET_NAME_VAL 0x2c1a
/**
* @brief Coordinated Set Name
*/
#define BT_UUID_CSIS_SET_NAME \
BT_UUID_DECLARE_16(BT_UUID_CSIS_SET_NAME_VAL)
/**
* @brief GATT Characteristic Encrypted Data Key Material UUID Value
*/
@@ -259,6 +259,8 @@ int k_work_reschedule(struct k_work_delayable *dwork, k_timeout_t delay);
int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_ms);
int k_work_schedule_periodic_us(struct k_work_delayable *dwork, uint64_t period_us);
k_timeout_t k_work_delayable_remaining_get(struct k_work_delayable *dwork);
/* Slist */