mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(ble_audio): Miscellaneous update for verifying with PTS cases
This commit is contained in:
@@ -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, <v[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(¶m->gatts_write);
|
||||
break;
|
||||
|
||||
case BT_LE_GATTS_EXEC_WRITE_EVENT:
|
||||
handle_gatts_exec_write_event(¶m->gatts_exec_write);
|
||||
break;
|
||||
|
||||
case BT_LE_GATTS_NOTIFY_TX_EVENT:
|
||||
handle_gatts_notify_tx_event(¶m->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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, ¶ms->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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user